3
3
4
4
use std:: collections:: BTreeMap ;
5
5
use std:: fmt:: Debug ;
6
- use std:: path:: PathBuf ;
7
6
use std:: sync:: Arc ;
8
7
9
8
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 ;
12
17
use fedimint_core:: db:: {
13
18
Committable , Database , DatabaseTransaction , IDatabaseTransactionOpsCoreTyped ,
14
19
} ;
20
+ use fedimint_core:: encoding:: Encodable ;
15
21
use fedimint_ln_client:: LightningClientInit ;
16
22
use fedimint_mint_client:: MintClientInit ;
17
23
use fedimint_wallet_client:: WalletClientInit ;
18
24
use futures_util:: StreamExt ;
19
- use rand:: thread_rng;
20
- use tracing:: info;
21
25
22
26
use crate :: db:: { FederationConfig , FederationIdKey , FederationIdKeyPrefix } ;
23
27
24
28
#[ derive( Debug , Clone ) ]
25
29
pub struct LocalClientBuilder {
26
- work_dir : PathBuf ,
30
+ mnemonic : Mnemonic ,
27
31
}
28
32
29
33
impl LocalClientBuilder {
30
- pub fn new ( work_dir : PathBuf ) -> Self {
31
- Self { work_dir }
34
+ pub fn new ( mnemonic : Mnemonic ) -> Self {
35
+ Self { mnemonic }
32
36
}
33
37
}
34
38
@@ -37,48 +41,23 @@ impl LocalClientBuilder {
37
41
#[ allow( clippy:: too_many_arguments) ]
38
42
pub async fn build (
39
43
& self ,
44
+ db : & Database ,
40
45
config : FederationConfig ,
41
- manual_secret : Option < [ u8 ; 64 ] > ,
42
46
) -> Result < fedimint_client:: ClientHandleArc > {
43
47
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 ?;
44
51
45
- let db_path = self . work_dir . join ( format ! ( "{federation_id}.db" ) ) ;
52
+ let client_builder = self . create_client_builder ( db . clone ( ) ) . await ? ;
46
53
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) ;
75
54
let client_res = if Client :: is_initialized ( & db) . await {
76
- client_builder. open ( root_secret ) . await
55
+ client_builder. open ( secret ) . await
77
56
} else {
78
57
let client_config =
79
58
fedimint_api_client:: download_from_invite_code ( & config. invite_code ) . await ?;
80
59
client_builder
81
- . join ( root_secret , client_config. to_owned ( ) , None )
60
+ . join ( secret , client_config. to_owned ( ) , None )
82
61
. await
83
62
} ?;
84
63
@@ -107,4 +86,37 @@ impl LocalClientBuilder {
107
86
. cloned ( )
108
87
. collect :: < Vec < _ > > ( )
109
88
}
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
+ }
110
122
}
0 commit comments