Skip to content

Commit 9796ecc

Browse files
authored
Merge pull request #53 from fedimint/mnemonic-single-db
2 parents 1eb0d0e + 6b24daa commit 9796ecc

File tree

10 files changed

+112
-99
lines changed

10 files changed

+112
-99
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fm_client_db
88
.DS_Store
99
fm_db
1010
fm_db_dir
11+
fm_db_mnemonic
1112
result
1213
/vendor
1314

Cargo.lock

Lines changed: 21 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ fedimint-mint-client = "0.4.2"
2020
fedimint-ln-client = "0.4.2"
2121
fedimint-ln-common = "0.4.2"
2222
fedimint-rocksdb = "0.4.2"
23+
fedimint-bip39 = "0.4.2"
2324

2425
# Config for 'cargo dist'
2526
[workspace.metadata.dist]

example.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ FEDIMINT_CLIENTD_DB_PATH='/absolute/path/to/fm_db_dir'
88
FEDIMINT_CLIENTD_PASSWORD='password'
99
FEDIMINT_CLIENTD_BASE_URL='http://127.0.0.1:3333'
1010
FEDIMINT_CLIENTD_ADDR='127.0.0.1:3333'
11+
MULTIMINT_MNEMONIC_ENV='ivory put armed include entire report oblige mystery ivory reunion siren actor'

fedimint-clientd/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ time = { version = "0.3.25", features = ["formatting"] }
3535
chrono = "0.4.31"
3636
futures-util = "0.3.30"
3737
clap = { version = "3", features = ["derive", "env"] }
38-
multimint = { version = "0.4.0" }
39-
# multimint = { path = "../multimint" }
38+
# multimint = { version = "0.4.0" }
39+
multimint = { path = "../multimint" }
4040
hex = "0.4.3"
4141

4242
futures = "0.3"

fedimint-clientd/src/main.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,9 @@ async fn main() -> Result<()> {
9797

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

100-
let manual_secret = match cli.manual_secret {
101-
Some(secret) => Some(secret),
102-
None => match std::env::var("FEDIMINT_CLIENTD_MANUAL_SECRET") {
103-
Ok(secret) => Some(secret),
104-
Err(_) => None,
105-
},
106-
};
107-
108100
match InviteCode::from_str(&cli.invite_code) {
109101
Ok(invite_code) => {
110-
let federation_id = state
111-
.multimint
112-
.register_new(invite_code, manual_secret)
113-
.await?;
102+
let federation_id = state.multimint.register_new(invite_code).await?;
114103
info!("Created client for federation id: {:?}", federation_id);
115104
}
116105
Err(e) => {

fedimint-clientd/src/router/handlers/admin/join.rs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::env;
2-
31
use anyhow::{anyhow, Error};
42
use axum::extract::State;
53
use axum::http::StatusCode;
@@ -17,7 +15,6 @@ use crate::state::AppState;
1715
#[serde(rename_all = "camelCase")]
1816
pub struct JoinRequest {
1917
pub invite_code: InviteCode,
20-
pub use_manual_secret: bool,
2118
}
2219

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

3027
async fn _join(mut multimint: MultiMint, req: JoinRequest) -> Result<JoinResponse, Error> {
31-
let manual_secret = if req.use_manual_secret {
32-
match env::var("FEDIMINT_CLIENTD_MANUAL_SECRET") {
33-
Ok(secret) => Some(secret),
34-
Err(_) => {
35-
return Err(anyhow!(
36-
"FEDIMINT_CLIENTD_MANUAL_SECRET must be set to join with manual secret"
37-
))
38-
}
39-
}
40-
} else {
41-
None
42-
};
43-
44-
let this_federation_id = multimint
45-
.register_new(req.invite_code.clone(), manual_secret)
46-
.await?;
28+
let this_federation_id = multimint.register_new(req.invite_code.clone()).await?;
4729

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

multimint/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ fedimint-mint-client = { workspace = true }
2525
fedimint-ln-client = { workspace = true }
2626
fedimint-ln-common = { workspace = true }
2727
fedimint-rocksdb = { workspace = true }
28+
fedimint-bip39 = { workspace = true }
2829
futures-util = "0.3.30"
2930
rand = "0.8.5"
3031
tracing = "0.1.40"
3132
hex = "0.4.3"
33+
bip39 = "2.1.0"

multimint/src/client.rs

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,36 @@
33
44
use std::collections::BTreeMap;
55
use std::fmt::Debug;
6-
use std::path::PathBuf;
76
use std::sync::Arc;
87

98
use anyhow::Result;
10-
use fedimint_client::secret::{PlainRootSecretStrategy, RootSecretStrategy};
11-
use fedimint_client::Client;
9+
use bip39::Mnemonic;
10+
use fedimint_bip39::Bip39RootSecretStrategy;
11+
use fedimint_client::db::ClientConfigKey;
12+
use fedimint_client::derivable_secret::{ChildId, DerivableSecret};
13+
use fedimint_client::module::init::ClientModuleInitRegistry;
14+
use fedimint_client::secret::RootSecretStrategy;
15+
use fedimint_client::{Client, ClientBuilder};
16+
use fedimint_core::config::FederationId;
1217
use fedimint_core::db::{
1318
Committable, Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped,
1419
};
20+
use fedimint_core::encoding::Encodable;
1521
use fedimint_ln_client::LightningClientInit;
1622
use fedimint_mint_client::MintClientInit;
1723
use fedimint_wallet_client::WalletClientInit;
1824
use futures_util::StreamExt;
19-
use rand::thread_rng;
20-
use tracing::info;
2125

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

2428
#[derive(Debug, Clone)]
2529
pub struct LocalClientBuilder {
26-
work_dir: PathBuf,
30+
mnemonic: Mnemonic,
2731
}
2832

2933
impl LocalClientBuilder {
30-
pub fn new(work_dir: PathBuf) -> Self {
31-
Self { work_dir }
34+
pub fn new(mnemonic: Mnemonic) -> Self {
35+
Self { mnemonic }
3236
}
3337
}
3438

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

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

47-
let db = Database::new(
48-
fedimint_rocksdb::RocksDb::open(db_path.clone())?,
49-
Default::default(),
50-
);
51-
52-
let mut client_builder = Client::builder(db.clone()).await?;
53-
client_builder.with_module(WalletClientInit(None));
54-
client_builder.with_module(MintClientInit);
55-
client_builder.with_module(LightningClientInit::default());
56-
client_builder.with_primary_module(1);
57-
58-
let client_secret = match Client::load_decodable_client_secret::<[u8; 64]>(&db).await {
59-
Ok(secret) => secret,
60-
Err(_) => {
61-
if let Some(manual_secret) = manual_secret {
62-
info!("Using manual secret provided by user and writing to client storage");
63-
Client::store_encodable_client_secret(&db, manual_secret).await?;
64-
manual_secret
65-
} else {
66-
info!("Generating new secret and writing to client storage");
67-
let secret = PlainRootSecretStrategy::random(&mut thread_rng());
68-
Client::store_encodable_client_secret(&db, secret).await?;
69-
secret
70-
}
71-
}
72-
};
73-
74-
let root_secret = PlainRootSecretStrategy::to_root_secret(&client_secret);
7554
let client_res = if Client::is_initialized(&db).await {
76-
client_builder.open(root_secret).await
55+
client_builder.open(secret).await
7756
} else {
7857
let client_config =
7958
fedimint_api_client::download_from_invite_code(&config.invite_code).await?;
8059
client_builder
81-
.join(root_secret, client_config.to_owned(), None)
60+
.join(secret, client_config.to_owned(), None)
8261
.await
8362
}?;
8463

@@ -107,4 +86,37 @@ impl LocalClientBuilder {
10786
.cloned()
10887
.collect::<Vec<_>>()
10988
}
89+
90+
pub fn derive_federation_secret(&self, federation_id: &FederationId) -> DerivableSecret {
91+
let global_root_secret = Bip39RootSecretStrategy::<12>::to_root_secret(&self.mnemonic);
92+
let multi_federation_root_secret = global_root_secret.child_key(ChildId(0));
93+
let federation_root_secret = multi_federation_root_secret.federation_key(federation_id);
94+
let federation_wallet_root_secret = federation_root_secret.child_key(ChildId(0));
95+
federation_wallet_root_secret.child_key(ChildId(0))
96+
}
97+
98+
/// Verifies that the saved `ClientConfig` contains the expected
99+
/// federation's config.
100+
async fn verify_client_config(db: &Database, federation_id: FederationId) -> Result<()> {
101+
let mut dbtx = db.begin_transaction_nc().await;
102+
if let Some(config) = dbtx.get_value(&ClientConfigKey).await {
103+
if config.calculate_federation_id() != federation_id {
104+
anyhow::bail!("Federation Id did not match saved federation ID")
105+
}
106+
}
107+
Ok(())
108+
}
109+
110+
/// Constructs the client builder with the modules, database, and connector
111+
/// used to create clients for connected federations.
112+
async fn create_client_builder(&self, db: Database) -> Result<ClientBuilder> {
113+
let mut registry = ClientModuleInitRegistry::new();
114+
registry.attach(WalletClientInit::default());
115+
registry.attach(MintClientInit);
116+
registry.attach(LightningClientInit::default());
117+
let mut client_builder = Client::builder(db).await?;
118+
client_builder.with_module_inits(registry);
119+
client_builder.with_primary_module(1);
120+
Ok(client_builder)
121+
}
110122
}

0 commit comments

Comments
 (0)