/ gateway / ln-gateway / src / client.rs
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  }