Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: single menmonic and db #53

Merged
merged 1 commit into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fm_client_db
.DS_Store
fm_db
fm_db_dir
fm_db_mnemonic
result
/vendor

Expand Down
26 changes: 21 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fedimint-mint-client = "0.4.2"
fedimint-ln-client = "0.4.2"
fedimint-ln-common = "0.4.2"
fedimint-rocksdb = "0.4.2"
fedimint-bip39 = "0.4.2"

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand Down
1 change: 1 addition & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ FEDIMINT_CLIENTD_DB_PATH='/absolute/path/to/fm_db_dir'
FEDIMINT_CLIENTD_PASSWORD='password'
FEDIMINT_CLIENTD_BASE_URL='http://127.0.0.1:3333'
FEDIMINT_CLIENTD_ADDR='127.0.0.1:3333'
MULTIMINT_MNEMONIC_ENV='ivory put armed include entire report oblige mystery ivory reunion siren actor'
4 changes: 2 additions & 2 deletions fedimint-clientd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ time = { version = "0.3.25", features = ["formatting"] }
chrono = "0.4.31"
futures-util = "0.3.30"
clap = { version = "3", features = ["derive", "env"] }
multimint = { version = "0.4.0" }
# multimint = { path = "../multimint" }
# multimint = { version = "0.4.0" }
multimint = { path = "../multimint" }
hex = "0.4.3"

futures = "0.3"
Expand Down
13 changes: 1 addition & 12 deletions fedimint-clientd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,9 @@ async fn main() -> Result<()> {

let mut state = AppState::new(cli.db_path).await?;

let manual_secret = match cli.manual_secret {
Some(secret) => Some(secret),
None => match std::env::var("FEDIMINT_CLIENTD_MANUAL_SECRET") {
Ok(secret) => Some(secret),
Err(_) => None,
},
};

match InviteCode::from_str(&cli.invite_code) {
Ok(invite_code) => {
let federation_id = state
.multimint
.register_new(invite_code, manual_secret)
.await?;
let federation_id = state.multimint.register_new(invite_code).await?;
info!("Created client for federation id: {:?}", federation_id);
}
Err(e) => {
Expand Down
20 changes: 1 addition & 19 deletions fedimint-clientd/src/router/handlers/admin/join.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::env;

use anyhow::{anyhow, Error};
use axum::extract::State;
use axum::http::StatusCode;
Expand All @@ -17,7 +15,6 @@ use crate::state::AppState;
#[serde(rename_all = "camelCase")]
pub struct JoinRequest {
pub invite_code: InviteCode,
pub use_manual_secret: bool,
}

#[derive(Debug, Serialize)]
Expand All @@ -28,22 +25,7 @@ pub struct JoinResponse {
}

async fn _join(mut multimint: MultiMint, req: JoinRequest) -> Result<JoinResponse, Error> {
let manual_secret = if req.use_manual_secret {
match env::var("FEDIMINT_CLIENTD_MANUAL_SECRET") {
Ok(secret) => Some(secret),
Err(_) => {
return Err(anyhow!(
"FEDIMINT_CLIENTD_MANUAL_SECRET must be set to join with manual secret"
))
}
}
} else {
None
};

let this_federation_id = multimint
.register_new(req.invite_code.clone(), manual_secret)
.await?;
let this_federation_id = multimint.register_new(req.invite_code.clone()).await?;

let federation_ids = multimint.ids().await.into_iter().collect::<Vec<_>>();

Expand Down
2 changes: 2 additions & 0 deletions multimint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ fedimint-mint-client = { workspace = true }
fedimint-ln-client = { workspace = true }
fedimint-ln-common = { workspace = true }
fedimint-rocksdb = { workspace = true }
fedimint-bip39 = { workspace = true }
futures-util = "0.3.30"
rand = "0.8.5"
tracing = "0.1.40"
hex = "0.4.3"
bip39 = "2.1.0"
92 changes: 52 additions & 40 deletions multimint/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@

use std::collections::BTreeMap;
use std::fmt::Debug;
use std::path::PathBuf;
use std::sync::Arc;

use anyhow::Result;
use fedimint_client::secret::{PlainRootSecretStrategy, RootSecretStrategy};
use fedimint_client::Client;
use bip39::Mnemonic;
use fedimint_bip39::Bip39RootSecretStrategy;
use fedimint_client::db::ClientConfigKey;
use fedimint_client::derivable_secret::{ChildId, DerivableSecret};
use fedimint_client::module::init::ClientModuleInitRegistry;
use fedimint_client::secret::RootSecretStrategy;
use fedimint_client::{Client, ClientBuilder};
use fedimint_core::config::FederationId;
use fedimint_core::db::{
Committable, Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped,
};
use fedimint_core::encoding::Encodable;
use fedimint_ln_client::LightningClientInit;
use fedimint_mint_client::MintClientInit;
use fedimint_wallet_client::WalletClientInit;
use futures_util::StreamExt;
use rand::thread_rng;
use tracing::info;

use crate::db::{FederationConfig, FederationIdKey, FederationIdKeyPrefix};

#[derive(Debug, Clone)]
pub struct LocalClientBuilder {
work_dir: PathBuf,
mnemonic: Mnemonic,
}

impl LocalClientBuilder {
pub fn new(work_dir: PathBuf) -> Self {
Self { work_dir }
pub fn new(mnemonic: Mnemonic) -> Self {
Self { mnemonic }
}
}

Expand All @@ -37,48 +41,23 @@ impl LocalClientBuilder {
#[allow(clippy::too_many_arguments)]
pub async fn build(
&self,
db: &Database,
config: FederationConfig,
manual_secret: Option<[u8; 64]>,
) -> Result<fedimint_client::ClientHandleArc> {
let federation_id = config.invite_code.federation_id();
let db = db.with_prefix(federation_id.consensus_encode_to_vec());
let secret = self.derive_federation_secret(&federation_id);
Self::verify_client_config(&db, federation_id).await?;

let db_path = self.work_dir.join(format!("{federation_id}.db"));
let client_builder = self.create_client_builder(db.clone()).await?;

let db = Database::new(
fedimint_rocksdb::RocksDb::open(db_path.clone())?,
Default::default(),
);

let mut client_builder = Client::builder(db.clone()).await?;
client_builder.with_module(WalletClientInit(None));
client_builder.with_module(MintClientInit);
client_builder.with_module(LightningClientInit::default());
client_builder.with_primary_module(1);

let client_secret = match Client::load_decodable_client_secret::<[u8; 64]>(&db).await {
Ok(secret) => secret,
Err(_) => {
if let Some(manual_secret) = manual_secret {
info!("Using manual secret provided by user and writing to client storage");
Client::store_encodable_client_secret(&db, manual_secret).await?;
manual_secret
} else {
info!("Generating new secret and writing to client storage");
let secret = PlainRootSecretStrategy::random(&mut thread_rng());
Client::store_encodable_client_secret(&db, secret).await?;
secret
}
}
};

let root_secret = PlainRootSecretStrategy::to_root_secret(&client_secret);
let client_res = if Client::is_initialized(&db).await {
client_builder.open(root_secret).await
client_builder.open(secret).await
} else {
let client_config =
fedimint_api_client::download_from_invite_code(&config.invite_code).await?;
client_builder
.join(root_secret, client_config.to_owned(), None)
.join(secret, client_config.to_owned(), None)
.await
}?;

Expand Down Expand Up @@ -107,4 +86,37 @@ impl LocalClientBuilder {
.cloned()
.collect::<Vec<_>>()
}

pub fn derive_federation_secret(&self, federation_id: &FederationId) -> DerivableSecret {
let global_root_secret = Bip39RootSecretStrategy::<12>::to_root_secret(&self.mnemonic);
let multi_federation_root_secret = global_root_secret.child_key(ChildId(0));
let federation_root_secret = multi_federation_root_secret.federation_key(federation_id);
let federation_wallet_root_secret = federation_root_secret.child_key(ChildId(0));
federation_wallet_root_secret.child_key(ChildId(0))
}

/// Verifies that the saved `ClientConfig` contains the expected
/// federation's config.
async fn verify_client_config(db: &Database, federation_id: FederationId) -> Result<()> {
let mut dbtx = db.begin_transaction_nc().await;
if let Some(config) = dbtx.get_value(&ClientConfigKey).await {
if config.calculate_federation_id() != federation_id {
anyhow::bail!("Federation Id did not match saved federation ID")
}
}
Ok(())
}

/// Constructs the client builder with the modules, database, and connector
/// used to create clients for connected federations.
async fn create_client_builder(&self, db: Database) -> Result<ClientBuilder> {
let mut registry = ClientModuleInitRegistry::new();
registry.attach(WalletClientInit::default());
registry.attach(MintClientInit);
registry.attach(LightningClientInit::default());
let mut client_builder = Client::builder(db).await?;
client_builder.with_module_inits(registry);
client_builder.with_primary_module(1);
Ok(client_builder)
}
}
Loading
Loading