Skip to content

Commit

Permalink
chore: clientd updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Kodylow committed Mar 17, 2024
1 parent 9529ce6 commit 9b83e77
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 59 deletions.
63 changes: 25 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
<img src="assets/federated-cashu.jpg" width="500">
# fedimint-clientd: A Fedimint Client for Server Side Applications

# fedimint-http: A Fedimint HTTP Client (and Cashu Proxy)
fedimint-clientd runs a fedimint client with Ecash, Lightning, and Onchain modules to let a server side application hold and use Bitcoin with Fedimint. It exposes a REST API & provides wrappers in typescript, python, and golang.

fedimint-http exposes a REST API to interact with the Fedimint client.
This project is intended to be an easy-to-use starting point for those interested in adding Fedimint client support to their applications. Fedimint-clientd only exposes Fedimint's default modules, and any more complex Fedimint integration will require custom implementation using [Fedimint's rust crates](https://github.com/fedimint/fedimint).

Set the variables in `.env` by copying `example.env`, then run `cargo run`. It's also set up as a clap app so you can start the server with command line args as well.
## Getting Started

## Fedimint Client Endpoints
You can install the cli app with `cargo install fedimint-clientd` or by cloning the repo and running `cargo build --release` in the root directory.

The Fedimint client supports the following endpoints (and has naive websocket support at `/fedimint/v2/ws`, see code for details until I improve the interface. PRs welcome!)
`fedimint-clientd` runs from the command line and takes a few arguments, which are also available as environment variables. Fedimint uses rocksDB, an embedded key-value store, to store its state. The `--fm_db_path` argument is required and should be an absolute path to a directory where the database will be stored.

```
CLI USAGE:
fedimint-clientd \
--fm_db_path=/absolute/path/to/dir/to/store/database \
--password="some-secure-password-that-becomes-the-bearer-token" \
--addr="127.0.0.1:8080"
--mode="default"
ENV USAGE:
FM_DB_PATH=/absolute/path/to/dir/to/store/database
FEDIMINT_CLIENTD_PASSWORD="some-secure-password-that-becomes-the-bearer-token"
FEDIMINT_CLIENTD_ADDR="127.0.0.1:8080"
FEDIMINT_CLIENTD_MODE="default"
```

## Fedimint Clientd Endpoints

`fedimint-clientd` supports the following endpoints (and has naive websocket support at `/fedimint/v2/ws`, see code for details until I improve the interface. PRs welcome!)

### Admin related commands:

Expand Down Expand Up @@ -47,35 +66,3 @@ The Fedimint client supports the following endpoints (and has naive websocket su

- `/health`: health check endpoint.
- `/metrics`: exports API metrics using opentelemetry with prometheus exporter (num requests, latency, high-level metrics only)


Soon(TM): maps [Cashu NUT](https://github.com/cashubtc/nuts) endpoints to fedimint client.

# Supported Cashu NUTs: (Notation, Utilization, and Terminology)

- [ ] NUT-00: Notation, Utilization, and Terminology
- Fedimint ecash does not currently encode the federation endpoint as part of the ecash, just the federation id. Fedimint encourages longer running relationships based off its trust model so doesnt currently support on the fly issuance / reissuance. Can coerce a mapping but doesnt exactly match. returns a federation id instead
- [ ] NUT-01: Mint public key exchange
- [ ] `/v1/keys`: supportable
- [ ] `/v1/keys/{keyset-id}`: supportable (fedimint only maintains 1 keyset)
- Fedimint does not currently rotate keysets. Responds with single keyset mapping in Cashu format.
- [ ] NUT-02: Keysets and keyset ID
- [ ] `/v1/keysets`: supportable
- [ ] NUT-03: Swap tokens
- [ ] `/v1/swap`: supportable
- Equivalent to Fedimint Reissue. Proofs are slightly different but functionally equivalent.
- [ ] NUT-04: Mint tokens
- [ ] `/v1/mint/quote/{method}`: supportable
- [ ] method=bolt11: supportable via lngateway
- [ ] method=onchain: supportable via pegin
- [ ] `/v1/mint/quote/{method}/{quote_id}`: supportable
- [ ] `/v1/mint/{method}`: supportable
- Fedimint client handles these a little differently but can probably coerce the flow, dont get why it requires the 2nd round after status is completed, should just return the notes there.
- [ ] NUT-05: Melting tokens
- [ ] `/v1/melt/quote/{method}`: supportable
- [ ] method=bolt11: supportable via lngateway
- [ ] method=onchain: supportable via pegout
- [ ] `/v1/melt/quote/{method}/{quote_id}`: supportable
- [ ] NUT-06: Mint information
- [ ] `/v1/info`: supportable

Binary file removed assets/federated-cashu.jpg
Binary file not shown.
29 changes: 29 additions & 0 deletions docs/cashu_compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Soon(TM): maps [Cashu NUT](https://github.com/cashubtc/nuts) endpoints to fedimint client.

# Supported Cashu NUTs: (Notation, Utilization, and Terminology)

- [ ] NUT-00: Notation, Utilization, and Terminology
- Fedimint ecash does not currently encode the federation endpoint as part of the ecash, just the federation id. Fedimint encourages longer running relationships based off its trust model so doesnt currently support on the fly issuance / reissuance. Can coerce a mapping but doesnt exactly match. returns a federation id instead
- [ ] NUT-01: Mint public key exchange
- [ ] `/v1/keys`: supportable
- [ ] `/v1/keys/{keyset-id}`: supportable (fedimint only maintains 1 keyset)
- Fedimint does not currently rotate keysets. Responds with single keyset mapping in Cashu format.
- [ ] NUT-02: Keysets and keyset ID
- [ ] `/v1/keysets`: supportable
- [ ] NUT-03: Swap tokens
- [ ] `/v1/swap`: supportable
- Equivalent to Fedimint Reissue. Proofs are slightly different but functionally equivalent.
- [ ] NUT-04: Mint tokens
- [ ] `/v1/mint/quote/{method}`: supportable
- [ ] method=bolt11: supportable via lngateway
- [ ] method=onchain: supportable via pegin
- [ ] `/v1/mint/quote/{method}/{quote_id}`: supportable
- [ ] `/v1/mint/{method}`: supportable
- Fedimint client handles these a little differently but can probably coerce the flow, dont get why it requires the 2nd round after status is completed, should just return the notes there.
- [ ] NUT-05: Melting tokens
- [ ] `/v1/melt/quote/{method}`: supportable
- [ ] method=bolt11: supportable via lngateway
- [ ] method=onchain: supportable via pegout
- [ ] `/v1/melt/quote/{method}/{quote_id}`: supportable
- [ ] NUT-06: Mint information
- [ ] `/v1/info`: supportable
10 changes: 5 additions & 5 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
description = "A fedimint http client";
description = "description = A fedimint client daemon for server side applications to hold, use, and manage Bitcoin";

inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
Expand Down Expand Up @@ -28,7 +28,7 @@
flakeboxLib = flakebox.lib.${system} { };
rustSrc = flakeboxLib.filterSubPaths {
root = builtins.path {
name = "fedimint-http";
name = "fedimint-clientd";
path = ./.;
};
paths = [ "Cargo.toml" "Cargo.lock" ".cargo" "src" ];
Expand Down Expand Up @@ -78,14 +78,14 @@
workspaceDeps = craneLib.buildWorkspaceDepsOnly { };
workspaceBuild =
craneLib.buildWorkspace { cargoArtifacts = workspaceDeps; };
fedimint-http = craneLib.buildPackageGroup
{ pname = "fedimint-http"; packages = [ "fedimint-http" ]; mainProgram = "fedimint-http"; };
fedimint-clientd = craneLib.buildPackageGroup
{ pname = "fedimint-clientd"; packages = [ "fedimint-clientd" ]; mainProgram = "fedimint-clientd"; };
});
in
{
legacyPackages = outputs;
packages = {
default = outputs.fedimint-http;
default = outputs.fedimint-clientd;
};
devShells = flakeboxLib.mkShells (commonArgs // {
toolchain = toolchainNative;
Expand Down
5 changes: 3 additions & 2 deletions scripts/mprocs-nix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ fi
DEVIMINT_COMMAND=$1
MPROCS_PATH=$2

export FM_TEST_DIR="$TMP/fm-$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 4 || true)"
FM_TEST_DIR="$TMP/fm-$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 4 || true)"
export FM_TEST_DIR
export FM_FED_SIZE=4
export FM_PID_FILE="$FM_TEST_DIR/.pid"
export FM_LOGS_DIR="$FM_TEST_DIR/logs"
Expand All @@ -18,7 +19,7 @@ mkdir $FM_LOGS_DIR
touch $FM_PID_FILE

# Flag to have devimint use binaries in specific folder, e.g. "../fedimint/target/debug"
if [ -n DEVIMINT_BIN ]; then
if [ -n "$DEVIMINT_BIN" ]; then
export PATH=$DEVIMINT_BIN:$PATH
fi

Expand Down
26 changes: 12 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,20 @@ enum Commands {
#[clap(version = "1.0", author = "Kody Low")]
struct Cli {
/// Federation invite code
#[clap(long, env = "FEDERATION_INVITE_CODE", required = false)]
#[clap(long, env = "FEDIMINT_CLIENTD_INVITE_CODE", required = false)]
federation_invite_code: String,

/// Path to FM database
#[clap(long, env = "FM_DB_PATH", required = true)]
#[clap(long, env = "FEDIMINT_CLIENTD_DB_PATH", required = true)]
fm_db_path: PathBuf,

/// Password
#[clap(long, env = "PASSWORD", required = true)]
#[clap(long, env = "FEDIMINT_CLIENTD_PASSWORD", required = true)]
password: String,

/// Domain
#[clap(long, env = "DOMAIN", required = true)]
domain: String,

/// Port
#[clap(long, env = "PORT", default_value_t = 3333)]
port: u16,
/// Addr
#[clap(long, env = "FEDIMINT_CLIENTD_ADDR", required = true)]
addr: String,

/// Mode of operation
#[clap(long, default_value = "default")]
Expand Down Expand Up @@ -113,7 +109,7 @@ async fn main() -> Result<()> {
.allow_headers(Any);

let metrics = HttpMetricsLayerBuilder::new()
.with_service_name("fedimint-http".to_string())
.with_service_name("fedimint-clientd".to_string())
.build();

let app = app
Expand All @@ -125,10 +121,12 @@ async fn main() -> Result<()> {
.merge(metrics.routes())
.layer(metrics);

let listener = tokio::net::TcpListener::bind(format!("{}:{}", &cli.domain, &cli.port))
let listener = tokio::net::TcpListener::bind(format!("{}", &cli.addr))
.await
.unwrap();
info!("fedimint-http Listening on {}", &cli.port);
.expect(
"Failed to bind to address, should be a valid address and port like 127.0.0.1:3333",
);
info!("fedimint-clientd Listening on {}", &cli.addr);
axum::serve(listener, app).await.unwrap();

Ok(())
Expand Down

0 comments on commit 9b83e77

Please sign in to comment.