/ fedimint-server / src / lib.rs
lib.rs
  1  #![allow(where_clauses_object_safety)] // https://github.com/dtolnay/async-trait/issues/228
  2  extern crate fedimint_core;
  3  
  4  use std::fs;
  5  use std::path::{Path, PathBuf};
  6  
  7  use config::io::{read_server_config, PLAINTEXT_PASSWORD};
  8  use config::ServerConfig;
  9  use fedimint_aead::random_salt;
 10  use fedimint_core::config::ServerModuleInitRegistry;
 11  use fedimint_core::db::Database;
 12  use fedimint_core::epoch::ConsensusItem;
 13  use fedimint_core::task::TaskGroup;
 14  use fedimint_core::util::write_new;
 15  use fedimint_logging::LOG_CONSENSUS;
 16  use tracing::info;
 17  
 18  use crate::config::api::{ConfigGenApi, ConfigGenSettings};
 19  use crate::config::io::{write_server_config, SALT_FILE};
 20  use crate::metrics::initialize_gauge_metrics;
 21  use crate::net::api::RpcHandlerCtx;
 22  use crate::net::connect::TlsTcpConnector;
 23  
 24  pub mod envs;
 25  pub mod metrics;
 26  
 27  pub mod atomic_broadcast;
 28  
 29  /// The actual implementation of consensus
 30  pub mod consensus;
 31  
 32  /// Networking for mint-to-mint and client-to-mint communiccation
 33  pub mod net;
 34  
 35  /// Fedimint toplevel config
 36  pub mod config;
 37  
 38  /// Implementation of multiplexed peer connections
 39  pub mod multiplexed;
 40  
 41  pub async fn run(
 42      data_dir: PathBuf,
 43      settings: ConfigGenSettings,
 44      db: Database,
 45      version_hash: String,
 46      module_init_registry: &ServerModuleInitRegistry,
 47      task_group: TaskGroup,
 48  ) -> anyhow::Result<()> {
 49      let cfg = match get_config(&data_dir).await? {
 50          Some(cfg) => cfg,
 51          None => {
 52              run_config_gen(
 53                  data_dir,
 54                  settings,
 55                  db.clone(),
 56                  version_hash,
 57                  task_group.make_subgroup(),
 58              )
 59              .await?
 60          }
 61      };
 62  
 63      let decoders = module_init_registry.decoders_strict(
 64          cfg.consensus
 65              .modules
 66              .iter()
 67              .map(|(id, config)| (*id, &config.kind)),
 68      )?;
 69  
 70      let db = db.with_decoders(decoders);
 71  
 72      initialize_gauge_metrics(&db).await;
 73  
 74      consensus::run(cfg, db, module_init_registry.clone(), &task_group).await?;
 75  
 76      info!(target: LOG_CONSENSUS, "Shutting down tasks");
 77  
 78      task_group.shutdown();
 79  
 80      Ok(())
 81  }
 82  
 83  pub async fn get_config(data_dir: &Path) -> anyhow::Result<Option<ServerConfig>> {
 84      // Attempt get the config with local password, otherwise start config gen
 85      if let Ok(password) = fs::read_to_string(data_dir.join(PLAINTEXT_PASSWORD)) {
 86          return Ok(Some(read_server_config(&password, data_dir.to_owned())?));
 87      }
 88  
 89      Ok(None)
 90  }
 91  
 92  pub async fn run_config_gen(
 93      data_dir: PathBuf,
 94      settings: ConfigGenSettings,
 95      db: Database,
 96      version_hash: String,
 97      mut task_group: TaskGroup,
 98  ) -> anyhow::Result<ServerConfig> {
 99      info!(target: LOG_CONSENSUS, "Starting config gen");
100  
101      initialize_gauge_metrics(&db).await;
102  
103      let (cfg_sender, mut cfg_receiver) = tokio::sync::mpsc::channel(1);
104  
105      let config_gen = ConfigGenApi::new(
106          settings.clone(),
107          db.clone(),
108          cfg_sender,
109          &mut task_group,
110          version_hash.clone(),
111      );
112  
113      let mut rpc_module = RpcHandlerCtx::new_module(config_gen);
114  
115      net::api::attach_endpoints(&mut rpc_module, config::api::server_endpoints(), None);
116  
117      let api_handler = net::api::spawn("config-gen", &settings.api_bind, rpc_module, 10).await;
118  
119      let cfg = cfg_receiver.recv().await.expect("should not close");
120  
121      api_handler
122          .stop()
123          .expect("Config api should still be running");
124  
125      api_handler.stopped().await;
126  
127      // TODO: Make writing password optional
128      write_new(data_dir.join(PLAINTEXT_PASSWORD), &cfg.private.api_auth.0)?;
129      write_new(data_dir.join(SALT_FILE), random_salt())?;
130      write_server_config(
131          &cfg,
132          data_dir.clone(),
133          &cfg.private.api_auth.0,
134          &settings.registry,
135      )?;
136  
137      Ok(cfg)
138  }