client.rs
1 use std::collections::BTreeMap; 2 use std::fmt::Debug; 3 use std::path::PathBuf; 4 use std::sync::Arc; 5 6 use fedimint_client::module::init::ClientModuleInitRegistry; 7 use fedimint_client::secret::{PlainRootSecretStrategy, RootSecretStrategy}; 8 use fedimint_client::Client; 9 use fedimint_core::core::ModuleInstanceId; 10 use fedimint_core::db::{ 11 Committable, Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped, 12 }; 13 use fedimint_core::module::registry::ModuleDecoderRegistry; 14 use futures::StreamExt; 15 use rand::thread_rng; 16 use tracing::info; 17 18 use crate::db::{FederationConfig, FederationIdKey, FederationIdKeyPrefix}; 19 use crate::gateway_module_v2::GatewayClientInitV2; 20 use crate::state_machine::GatewayClientInit; 21 use crate::{Gateway, GatewayError, Result}; 22 23 #[derive(Debug, Clone)] 24 pub struct GatewayClientBuilder { 25 work_dir: PathBuf, 26 registry: ClientModuleInitRegistry, 27 primary_module: ModuleInstanceId, 28 } 29 30 impl GatewayClientBuilder { 31 pub fn new( 32 work_dir: PathBuf, 33 registry: ClientModuleInitRegistry, 34 primary_module: ModuleInstanceId, 35 ) -> Self { 36 Self { 37 work_dir, 38 registry, 39 primary_module, 40 } 41 } 42 } 43 44 impl GatewayClientBuilder { 45 pub async fn build( 46 &self, 47 config: FederationConfig, 48 gateway: Gateway, 49 ) -> Result<fedimint_client::ClientHandleArc> { 50 let FederationConfig { 51 invite_code, 52 mint_channel_id, 53 timelock_delta, 54 .. 55 } = config; 56 let federation_id = invite_code.federation_id(); 57 58 let mut registry = self.registry.clone(); 59 60 registry.attach(GatewayClientInit { 61 timelock_delta, 62 mint_channel_id, 63 gateway: gateway.clone(), 64 }); 65 registry.attach(GatewayClientInitV2 { gateway }); 66 67 let db_path = self.work_dir.join(format!("{federation_id}.db")); 68 69 let rocksdb = fedimint_rocksdb::RocksDb::open(db_path.clone()).map_err(|e| { 70 GatewayError::DatabaseError(anyhow::anyhow!("Error opening rocksdb: {e:?}")) 71 })?; 72 let db = Database::new(rocksdb, ModuleDecoderRegistry::default()); 73 74 let mut client_builder = Client::builder(db); 75 client_builder.with_module_inits(registry); 76 client_builder.with_primary_module(self.primary_module); 77 78 let client_secret = 79 match Client::load_decodable_client_secret::<[u8; 64]>(client_builder.db_no_decoders()) 80 .await 81 { 82 Ok(secret) => secret, 83 Err(_) => { 84 info!("Generating secret and writing to client storage"); 85 let secret = PlainRootSecretStrategy::random(&mut thread_rng()); 86 Client::store_encodable_client_secret(client_builder.db_no_decoders(), secret) 87 .await 88 .map_err(GatewayError::ClientStateMachineError)?; 89 secret 90 } 91 }; 92 93 let root_secret = PlainRootSecretStrategy::to_root_secret(&client_secret); 94 if Client::is_initialized(client_builder.db_no_decoders()).await { 95 client_builder 96 // TODO: make this configurable? 97 .open(root_secret) 98 .await 99 } else { 100 let client_config = 101 fedimint_api_client::download_from_invite_code(&invite_code).await?; 102 client_builder 103 // TODO: make this configurable? 104 .join(root_secret, client_config.to_owned()) 105 .await 106 } 107 .map(Arc::new) 108 .map_err(GatewayError::ClientStateMachineError) 109 } 110 111 pub async fn save_config( 112 &self, 113 config: FederationConfig, 114 mut dbtx: DatabaseTransaction<'_, Committable>, 115 ) -> Result<()> { 116 let id = config.invite_code.federation_id(); 117 dbtx.insert_entry(&FederationIdKey { id }, &config).await; 118 dbtx.commit_tx_result() 119 .await 120 .map_err(GatewayError::DatabaseError) 121 } 122 123 pub async fn load_configs(&self, mut dbtx: DatabaseTransaction<'_>) -> Vec<FederationConfig> { 124 dbtx.find_by_prefix(&FederationIdKeyPrefix) 125 .await 126 .collect::<BTreeMap<FederationIdKey, FederationConfig>>() 127 .await 128 .values() 129 .cloned() 130 .collect::<Vec<_>>() 131 } 132 }