mod.rs
  1  use std::collections::{BTreeMap, BTreeSet, HashMap};
  2  use std::env;
  3  use std::net::SocketAddr;
  4  use std::time::Duration;
  5  
  6  use anyhow::{bail, format_err};
  7  use fedimint_core::admin_client::ConfigGenParamsConsensus;
  8  pub use fedimint_core::config::{
  9      serde_binary_human_readable, ClientConfig, DkgError, DkgPeerMsg, DkgResult, FederationId,
 10      GlobalClientConfig, JsonWithKind, ModuleInitRegistry, PeerUrl, ServerModuleConfig,
 11      ServerModuleConsensusConfig, ServerModuleInitRegistry, TypedServerModuleConfig,
 12  };
 13  use fedimint_core::core::{ModuleInstanceId, ModuleKind, MODULE_INSTANCE_ID_GLOBAL};
 14  use fedimint_core::envs::is_running_in_test_env;
 15  use fedimint_core::invite_code::InviteCode;
 16  use fedimint_core::module::{
 17      ApiAuth, ApiVersion, CoreConsensusVersion, DynServerModuleInit, MultiApiVersion, PeerHandle,
 18      SupportedApiVersionsSummary, SupportedCoreApiVersions, CORE_CONSENSUS_VERSION,
 19  };
 20  use fedimint_core::net::peers::{IMuxPeerConnections, IPeerConnections, PeerConnections};
 21  use fedimint_core::task::{timeout, Cancelled, Elapsed, TaskGroup};
 22  use fedimint_core::{secp256k1, timing, PeerId};
 23  use fedimint_logging::{LOG_NET_PEER, LOG_NET_PEER_DKG};
 24  use futures::future::join_all;
 25  use rand::rngs::OsRng;
 26  use secp256k1::{PublicKey, Secp256k1, SecretKey};
 27  use serde::de::DeserializeOwned;
 28  use serde::{Deserialize, Serialize};
 29  use tokio_rustls::rustls;
 30  use tracing::{error, info};
 31  
 32  use crate::config::api::ConfigGenParamsLocal;
 33  use crate::config::distributedgen::{DkgRunner, PeerHandleOps};
 34  use crate::envs::FM_MAX_CLIENT_CONNECTIONS_ENV;
 35  use crate::fedimint_core::encoding::Encodable;
 36  use crate::fedimint_core::NumPeersExt;
 37  use crate::multiplexed::PeerConnectionMultiplexer;
 38  use crate::net::connect::{dns_sanitize, Connector, TlsConfig};
 39  use crate::net::peers::{DelayCalculator, NetworkConfig};
 40  use crate::net::peers_reliable::ReconnectPeerConnectionsReliable;
 41  use crate::TlsTcpConnector;
 42  
 43  pub mod api;
 44  pub mod distributedgen;
 45  pub mod io;
 46  
 47  /// The default maximum open connections the API can handle
 48  const DEFAULT_MAX_CLIENT_CONNECTIONS: u32 = 1000;
 49  // if all nodes are correct the session will take 45 to 60 seconds. The
 50  // more nodes go offline the longer the session will take to complete.
 51  const DEFAULT_BROADCAST_EXPECTED_ROUNDS_PER_SESSION: u16 = 45 * 20;
 52  const DEFAULT_BROADCAST_ROUND_DELAY_MS: u16 = 50;
 53  const DEFAULT_BROADCAST_MAX_ROUNDS_PER_SESSION: u16 = 5000;
 54  
 55  /// Set of consensus broadcast settings that results in around 10s session time,
 56  /// that is useful for testing purposes. Not that all the values here together
 57  /// need to satisfy bunch of constrains in alephbft, so tweaking them
 58  /// separately will most probably result in panics and other misbehavior.
 59  const DEFAULT_TEST_BROADCAST_EXPECTED_ROUNDS_PER_SESSION: u16 = 3 * 20;
 60  const DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS: u16 = 50;
 61  const DEFAULT_TEST_BROADCAST_MAX_ROUNDS_PER_SESSION: u16 = 2700;
 62  
 63  #[derive(Debug, Clone, Serialize, Deserialize)]
 64  /// All the serializable configuration for the fedimint server
 65  pub struct ServerConfig {
 66      /// Contains all configuration that needs to be the same for every server
 67      pub consensus: ServerConfigConsensus,
 68      /// Contains all configuration that is locally configurable and not secret
 69      pub local: ServerConfigLocal,
 70      /// Contains all configuration that will be encrypted such as private key
 71      /// material
 72      pub private: ServerConfigPrivate,
 73  }
 74  
 75  impl ServerConfig {
 76      pub fn iter_module_instances(
 77          &self,
 78      ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
 79          self.consensus.iter_module_instances()
 80      }
 81  
 82      pub(crate) fn supported_api_versions_summary(
 83          modules: &BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
 84          module_inits: &ServerModuleInitRegistry,
 85      ) -> SupportedApiVersionsSummary {
 86          SupportedApiVersionsSummary {
 87              core: Self::supported_api_versions(),
 88              modules: modules
 89                  .iter()
 90                  .map(|(&id, config)| {
 91                      (
 92                          id,
 93                          module_inits
 94                              .get(&config.kind)
 95                              .expect("missing module kind gen")
 96                              .supported_api_versions(),
 97                      )
 98                  })
 99                  .collect(),
100          }
101      }
102  }
103  
104  #[derive(Debug, Clone, Serialize, Deserialize)]
105  pub struct ServerConfigPrivate {
106      /// Secret API auth string
107      pub api_auth: ApiAuth,
108      /// Secret key for TLS communication, required for peer authentication
109      #[serde(with = "serde_tls_key")]
110      pub tls_key: rustls::PrivateKey,
111      /// Secret key for the atomic broadcast to sign messages
112      pub broadcast_secret_key: SecretKey,
113      /// Secret material from modules
114      pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
115  }
116  
117  #[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
118  pub struct ServerConfigConsensus {
119      /// The version of the binary code running
120      pub code_version: String,
121      /// Agreed on core consensus version
122      pub version: CoreConsensusVersion,
123      /// Public keys for the atomic broadcast to authenticate messages
124      pub broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
125      /// Determines how long a session is expected to run. Has to be less than
126      /// 1000.
127      pub broadcast_expected_rounds_per_session: u16,
128      /// Maximum number of rounds permitted per session.
129      pub broadcast_max_rounds_per_session: u16,
130      /// Network addresses and names for all peer APIs
131      pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
132      /// Certs for TLS communication, required for peer authentication
133      #[serde(with = "serde_tls_cert_map")]
134      pub tls_certs: BTreeMap<PeerId, rustls::Certificate>,
135      /// All configuration that needs to be the same for modules
136      pub modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
137      #[encodable_ignore]
138      // FIXME: Make modules encodable or we will not check module keys
139      /// Human readable representation of [`Self::modules`]
140      pub modules_json: BTreeMap<ModuleInstanceId, JsonWithKind>,
141      /// Additional config the federation wants to transmit to the clients
142      pub meta: BTreeMap<String, String>,
143  }
144  
145  #[derive(Debug, Clone, Serialize, Deserialize)]
146  pub struct ServerConfigLocal {
147      /// Network addresses and names for all p2p connections
148      pub p2p_endpoints: BTreeMap<PeerId, PeerUrl>,
149      /// Our peer id (generally should not change)
150      pub identity: PeerId,
151      /// Our bind address for communicating with peers
152      pub fed_bind: SocketAddr,
153      /// Our bind address for our API endpoints
154      pub api_bind: SocketAddr,
155      /// How many API connections we will accept
156      pub max_connections: u32,
157      /// Influences the atomic broadcast latency, should be higher than the
158      /// expected latency between peers so everyone can get proposed consensus
159      /// items confirmed. This is only relevant for byzantine faults.
160      ///
161      /// If you are changing this value you likely also want to change
162      /// [`ServerConfigConsensus::broadcast_expected_rounds_per_session`]. To
163      /// keep the session time constant these two have to behave inversely
164      /// proportional.
165      pub broadcast_round_delay_ms: u16,
166      /// Non-consensus, non-private configuration from modules
167      pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
168  }
169  
170  #[derive(Debug, Clone)]
171  /// All the parameters necessary for generating the `ServerConfig` during setup
172  ///
173  /// * Guardians can create the parameters using a setup UI or CLI tool
174  /// * Used for distributed or trusted config generation
175  pub struct ConfigGenParams {
176      pub local: ConfigGenParamsLocal,
177      pub consensus: ConfigGenParamsConsensus,
178  }
179  
180  impl ServerConfigConsensus {
181      pub fn iter_module_instances(
182          &self,
183      ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
184          self.modules.iter().map(|(k, v)| (*k, &v.kind))
185      }
186  
187      pub fn to_client_config(
188          &self,
189          module_config_gens: &ModuleInitRegistry<DynServerModuleInit>,
190      ) -> Result<ClientConfig, anyhow::Error> {
191          let client = ClientConfig {
192              global: GlobalClientConfig {
193                  api_endpoints: self.api_endpoints.clone(),
194                  consensus_version: self.version,
195                  meta: self.meta.clone(),
196              },
197              modules: self
198                  .modules
199                  .iter()
200                  .map(|(k, v)| {
201                      let gen = module_config_gens
202                          .get(&v.kind)
203                          .ok_or_else(|| format_err!("Module gen kind={} not found", v.kind))?;
204                      Ok((*k, gen.get_client_config(*k, v)?))
205                  })
206                  .collect::<anyhow::Result<BTreeMap<_, _>>>()?,
207          };
208          Ok(client)
209      }
210  }
211  
212  impl ServerConfig {
213      /// Api versions supported by this server
214      pub fn supported_api_versions() -> SupportedCoreApiVersions {
215          SupportedCoreApiVersions {
216              core_consensus: CORE_CONSENSUS_VERSION,
217              api: MultiApiVersion::try_from_iter([ApiVersion { major: 0, minor: 2 }])
218                  .expect("not version conflicts"),
219          }
220      }
221      /// Creates a new config from the results of a trusted or distributed key
222      /// setup
223      pub fn from(
224          params: ConfigGenParams,
225          identity: PeerId,
226          broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
227          broadcast_secret_key: SecretKey,
228          modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>,
229          version_hash: String,
230      ) -> Self {
231          let private = ServerConfigPrivate {
232              api_auth: params.local.api_auth.clone(),
233              tls_key: params.local.our_private_key.clone(),
234              broadcast_secret_key,
235              modules: Default::default(),
236          };
237          let local = ServerConfigLocal {
238              p2p_endpoints: params.p2p_urls(),
239              identity,
240              fed_bind: params.local.p2p_bind,
241              api_bind: params.local.api_bind,
242              max_connections: DEFAULT_MAX_CLIENT_CONNECTIONS,
243              broadcast_round_delay_ms: if is_running_in_test_env() {
244                  DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS
245              } else {
246                  DEFAULT_BROADCAST_ROUND_DELAY_MS
247              },
248              modules: Default::default(),
249          };
250          let consensus = ServerConfigConsensus {
251              code_version: version_hash,
252              version: CORE_CONSENSUS_VERSION,
253              broadcast_public_keys,
254              broadcast_expected_rounds_per_session: if is_running_in_test_env() {
255                  DEFAULT_TEST_BROADCAST_EXPECTED_ROUNDS_PER_SESSION
256              } else {
257                  DEFAULT_BROADCAST_EXPECTED_ROUNDS_PER_SESSION
258              },
259              broadcast_max_rounds_per_session: if is_running_in_test_env() {
260                  DEFAULT_TEST_BROADCAST_MAX_ROUNDS_PER_SESSION
261              } else {
262                  DEFAULT_BROADCAST_MAX_ROUNDS_PER_SESSION
263              },
264              api_endpoints: params.api_urls(),
265              tls_certs: params.tls_certs(),
266              modules: Default::default(),
267              modules_json: Default::default(),
268              meta: params.consensus.meta,
269          };
270          let mut cfg = Self {
271              consensus,
272              local,
273              private,
274          };
275          cfg.add_modules(modules);
276          cfg
277      }
278  
279      pub fn get_invite_code(&self) -> InviteCode {
280          InviteCode::new(
281              self.consensus.api_endpoints[&self.local.identity]
282                  .url
283                  .clone(),
284              self.local.identity,
285              FederationId(self.consensus.api_endpoints.consensus_hash()),
286          )
287      }
288  
289      pub fn get_federation_id(&self) -> FederationId {
290          FederationId(self.consensus.api_endpoints.consensus_hash())
291      }
292  
293      pub fn add_modules(&mut self, modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>) {
294          for (name, config) in modules.into_iter() {
295              let ServerModuleConfig {
296                  local,
297                  private,
298                  consensus,
299                  consensus_json,
300              } = config;
301  
302              self.local.modules.insert(name, local);
303              self.private.modules.insert(name, private);
304              self.consensus.modules.insert(name, consensus);
305              self.consensus.modules_json.insert(name, consensus_json);
306          }
307      }
308  
309      /// Constructs a module config by name
310      pub fn get_module_config_typed<T: TypedServerModuleConfig>(
311          &self,
312          id: ModuleInstanceId,
313      ) -> anyhow::Result<T> {
314          let local = Self::get_module_cfg_by_instance_id(&self.local.modules, id)?;
315          let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
316          let consensus = self
317              .consensus
318              .modules
319              .get(&id)
320              .ok_or_else(|| format_err!("Module {id} not found"))?
321              .clone();
322          let consensus_json = Self::get_module_cfg_by_instance_id(&self.consensus.modules_json, id)?;
323          let module = ServerModuleConfig::from(local, private, consensus, consensus_json);
324  
325          module.to_typed()
326      }
327      pub fn get_module_id_by_kind(
328          &self,
329          kind: impl Into<ModuleKind>,
330      ) -> anyhow::Result<ModuleInstanceId> {
331          let kind = kind.into();
332          Ok(*self
333              .consensus
334              .modules
335              .iter()
336              .find(|(_, v)| v.kind == kind)
337              .ok_or_else(|| format_err!("Module {kind} not found"))?
338              .0)
339      }
340  
341      /// Constructs a module config by id
342      pub fn get_module_config(&self, id: ModuleInstanceId) -> anyhow::Result<ServerModuleConfig> {
343          let local = Self::get_module_cfg_by_instance_id(&self.local.modules, id)?;
344          let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
345          let consensus = self
346              .consensus
347              .modules
348              .get(&id)
349              .ok_or_else(|| format_err!("Module {id} not found"))?
350              .clone();
351          let consensus_json = Self::get_module_cfg_by_instance_id(&self.consensus.modules_json, id)?;
352          Ok(ServerModuleConfig::from(
353              local,
354              private,
355              consensus,
356              consensus_json,
357          ))
358      }
359  
360      fn get_module_cfg_by_instance_id(
361          json: &BTreeMap<ModuleInstanceId, JsonWithKind>,
362          id: ModuleInstanceId,
363      ) -> anyhow::Result<JsonWithKind> {
364          Ok(json
365              .get(&id)
366              .ok_or_else(|| format_err!("Module {id} not found"))
367              .cloned()?
368              .with_fixed_empty_value())
369      }
370  
371      pub fn validate_config(
372          &self,
373          identity: &PeerId,
374          module_config_gens: &ServerModuleInitRegistry,
375      ) -> anyhow::Result<()> {
376          let peers = self.local.p2p_endpoints.clone();
377          let consensus = self.consensus.clone();
378          let private = self.private.clone();
379  
380          let my_public_key = private.broadcast_secret_key.public_key(&Secp256k1::new());
381  
382          if Some(&my_public_key) != consensus.broadcast_public_keys.get(identity) {
383              bail!("Broadcast secret key doesn't match corresponding public key");
384          }
385          if peers.keys().max().copied().map(|id| id.to_usize()) != Some(peers.len() - 1) {
386              bail!("Peer ids are not indexed from 0");
387          }
388          if peers.keys().min().copied() != Some(PeerId::from(0)) {
389              bail!("Peer ids are not indexed from 0");
390          }
391  
392          for (module_id, module_kind) in self
393              .consensus
394              .modules
395              .iter()
396              .map(|(id, config)| Ok((*id, config.kind.clone())))
397              .collect::<anyhow::Result<BTreeSet<_>>>()?
398              .iter()
399          {
400              module_config_gens
401                  .get(module_kind)
402                  .ok_or_else(|| format_err!("module config gen not found {module_kind}"))?
403                  .validate_config(identity, self.get_module_config(*module_id)?)?;
404          }
405  
406          Ok(())
407      }
408  
409      pub fn trusted_dealer_gen(
410          params: &HashMap<PeerId, ConfigGenParams>,
411          registry: ServerModuleInitRegistry,
412          version_hash: String,
413      ) -> BTreeMap<PeerId, Self> {
414          let peer0 = &params[&PeerId::from(0)];
415  
416          let mut broadcast_pks = BTreeMap::new();
417          let mut broadcast_sks = BTreeMap::new();
418          for peer_id in peer0.peer_ids() {
419              let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
420              broadcast_pks.insert(peer_id, broadcast_pk);
421              broadcast_sks.insert(peer_id, broadcast_sk);
422          }
423  
424          let modules = peer0.consensus.modules.iter_modules();
425          let module_configs: BTreeMap<_, _> = modules
426              .map(|(module_id, kind, module_params)| {
427                  (
428                      module_id,
429                      registry
430                          .get(kind)
431                          .expect("Module not registered")
432                          .trusted_dealer_gen(&peer0.peer_ids(), module_params),
433                  )
434              })
435              .collect();
436  
437          let server_config: BTreeMap<_, _> = peer0
438              .peer_ids()
439              .iter()
440              .map(|&id| {
441                  let config = ServerConfig::from(
442                      params[&id].clone(),
443                      id,
444                      broadcast_pks.clone(),
445                      *broadcast_sks.get(&id).expect("We created this entry"),
446                      module_configs
447                          .iter()
448                          .map(|(module_id, cfgs)| (*module_id, cfgs[&id].clone()))
449                          .collect(),
450                      version_hash.clone(),
451                  );
452                  (id, config)
453              })
454              .collect();
455  
456          server_config
457      }
458  
459      /// Runs the distributed key gen algorithm
460      pub async fn distributed_gen(
461          params: &ConfigGenParams,
462          registry: ServerModuleInitRegistry,
463          delay_calculator: DelayCalculator,
464          task_group: &mut TaskGroup,
465          version_hash: String,
466      ) -> DkgResult<Self> {
467          let _timing /* logs on drop */ = timing::TimeReporter::new("distributed-gen").info();
468          let server_conn = connect(
469              params.p2p_network(),
470              params.tls_config(),
471              delay_calculator,
472              task_group,
473          )
474          .await;
475          let connections = PeerConnectionMultiplexer::new(server_conn).into_dyn();
476  
477          let peers = &params.peer_ids();
478          let our_id = &params.local.our_id;
479  
480          let broadcast_keys_exchange = PeerHandle::new(
481              &connections,
482              MODULE_INSTANCE_ID_GLOBAL,
483              *our_id,
484              peers.clone(),
485          );
486  
487          let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
488  
489          let broadcast_public_keys = broadcast_keys_exchange
490              .exchange_pubkeys("broadcast".to_string(), broadcast_pk)
491              .await?;
492  
493          // in case we are running by ourselves, avoid DKG
494          if peers.len() == 1 {
495              let server = Self::trusted_dealer_gen(
496                  &HashMap::from([(*our_id, params.clone())]),
497                  registry,
498                  version_hash,
499              );
500              return Ok(server[our_id].clone());
501          }
502          info!(
503              target: LOG_NET_PEER_DKG,
504              "Peer {} running distributed key generation...", our_id
505          );
506  
507          // BFT uses a lower threshold of signing keys (f+1)
508          let mut dkg = DkgRunner::new(KeyType::Bft, peers.one_honest(), our_id, peers);
509          dkg.add(KeyType::Auth, peers.threshold());
510          dkg.add(KeyType::Epoch, peers.threshold());
511  
512          let mut registered_modules = registry.kinds();
513          let mut module_cfgs: BTreeMap<ModuleInstanceId, ServerModuleConfig> = Default::default();
514          let modules = params.consensus.modules.iter_modules();
515          let modules_runner = modules.map(|(module_instance_id, kind, module_params)| {
516              let dkg = PeerHandle::new(&connections, module_instance_id, *our_id, peers.clone());
517              let registry = registry.clone();
518  
519              async move {
520                  let result = match registry.get(kind) {
521                      None => Err(DkgError::ModuleNotFound(kind.clone())),
522                      Some(gen) => gen.distributed_gen(&dkg, module_params).await,
523                  };
524                  (module_instance_id, result)
525              }
526          });
527          for (module_instance_id, config) in join_all(modules_runner).await {
528              let config = config?;
529              registered_modules.remove(config.consensus_json.kind());
530              module_cfgs.insert(module_instance_id, config);
531          }
532          if !registered_modules.is_empty() {
533              return Err(DkgError::ParamsNotFound(registered_modules));
534          }
535  
536          info!(
537              target: LOG_NET_PEER_DKG,
538              "Sending confirmations to other peers."
539          );
540          // Note: Since our outgoing buffers are asynchronous, we don't actually know
541          // if other peers received our message, just because we received theirs.
542          // That's why we need to do a one last best effort sync.
543          let dkg_done = "DKG DONE".to_string();
544          connections
545              .send(
546                  peers,
547                  (MODULE_INSTANCE_ID_GLOBAL, dkg_done.clone()),
548                  DkgPeerMsg::Done,
549              )
550              .await?;
551  
552          info!(
553              target: LOG_NET_PEER_DKG,
554              "Waiting for confirmations from other peers."
555          );
556          if let Err(Elapsed) = timeout(Duration::from_secs(30), async {
557              let mut done_peers = BTreeSet::from([*our_id]);
558  
559              while done_peers.len() < peers.len() {
560                  match connections.receive((MODULE_INSTANCE_ID_GLOBAL, dkg_done.clone())).await {
561                      Ok((peer_id, DkgPeerMsg::Done)) => {
562                          info!(
563                              target: LOG_NET_PEER_DKG,
564                              pper_id = %peer_id, "Got completion confirmation");
565                          done_peers.insert(peer_id);
566                      },
567                      Ok((peer_id, msg)) => {
568                          error!(target: LOG_NET_PEER_DKG, %peer_id, ?msg, "Received incorrect message after dkg was supposed to be finished. Probably dkg multiplexing bug.");
569                      },
570                      Err(Cancelled) => {/* ignore shutdown for time being, we'll timeout soon anyway */},
571                  }
572              }
573          })
574          .await
575          {
576              error!(target: LOG_NET_PEER_DKG, "Timeout waiting for dkg completion confirmation from other peers");
577          };
578  
579          let server = ServerConfig::from(
580              params.clone(),
581              *our_id,
582              broadcast_public_keys,
583              broadcast_sk,
584              module_cfgs,
585              version_hash,
586          );
587  
588          info!(
589              target: LOG_NET_PEER,
590              "Distributed key generation has completed successfully!"
591          );
592  
593          Ok(server)
594      }
595  }
596  
597  /// The types of keys to run distributed key generation for
598  #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
599  pub enum KeyType {
600      Bft,
601      Epoch,
602      Auth,
603  }
604  
605  impl ServerConfig {
606      pub fn network_config(&self) -> NetworkConfig {
607          NetworkConfig {
608              identity: self.local.identity,
609              bind_addr: self.local.fed_bind,
610              peers: self
611                  .local
612                  .p2p_endpoints
613                  .iter()
614                  .map(|(&id, endpoint)| (id, endpoint.url.clone()))
615                  .collect(),
616          }
617      }
618  
619      pub fn tls_config(&self) -> TlsConfig {
620          TlsConfig {
621              our_private_key: self.private.tls_key.clone(),
622              peer_certs: self.consensus.tls_certs.clone(),
623              peer_names: self
624                  .local
625                  .p2p_endpoints
626                  .iter()
627                  .map(|(id, endpoint)| (*id, endpoint.name.to_string()))
628                  .collect(),
629          }
630      }
631  
632      pub fn get_incoming_count(&self) -> u16 {
633          self.local.identity.into()
634      }
635  }
636  
637  impl ConfigGenParams {
638      pub fn peer_ids(&self) -> Vec<PeerId> {
639          self.consensus.peers.keys().cloned().collect()
640      }
641  
642      pub fn p2p_network(&self) -> NetworkConfig {
643          NetworkConfig {
644              identity: self.local.our_id,
645              bind_addr: self.local.p2p_bind,
646              peers: self
647                  .p2p_urls()
648                  .into_iter()
649                  .map(|(id, peer)| (id, peer.url))
650                  .collect(),
651          }
652      }
653  
654      pub fn tls_config(&self) -> TlsConfig {
655          TlsConfig {
656              our_private_key: self.local.our_private_key.clone(),
657              peer_certs: self.tls_certs(),
658              peer_names: self
659                  .p2p_urls()
660                  .into_iter()
661                  .map(|(id, peer)| (id, peer.name))
662                  .collect(),
663          }
664      }
665  
666      pub fn tls_certs(&self) -> BTreeMap<PeerId, rustls::Certificate> {
667          self.consensus
668              .peers
669              .iter()
670              .map(|(id, peer)| (*id, peer.cert.clone()))
671              .collect::<BTreeMap<_, _>>()
672      }
673  
674      pub fn p2p_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
675          self.consensus
676              .peers
677              .iter()
678              .map(|(id, peer)| {
679                  (
680                      *id,
681                      PeerUrl {
682                          name: peer.name.clone(),
683                          url: peer.p2p_url.clone(),
684                      },
685                  )
686              })
687              .collect::<BTreeMap<_, _>>()
688      }
689  
690      pub fn api_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
691          self.consensus
692              .peers
693              .iter()
694              .map(|(id, peer)| {
695                  (
696                      *id,
697                      PeerUrl {
698                          name: peer.name.clone(),
699                          url: peer.api_url.clone(),
700                      },
701                  )
702              })
703              .collect::<BTreeMap<_, _>>()
704      }
705  }
706  
707  // TODO: Remove once new config gen UI is written
708  pub fn max_connections() -> u32 {
709      env::var(FM_MAX_CLIENT_CONNECTIONS_ENV)
710          .ok()
711          .and_then(|s| s.parse().ok())
712          .unwrap_or(DEFAULT_MAX_CLIENT_CONNECTIONS)
713  }
714  
715  pub async fn connect<T>(
716      network: NetworkConfig,
717      certs: TlsConfig,
718      delay_calculator: DelayCalculator,
719      task_group: &mut TaskGroup,
720  ) -> PeerConnections<T>
721  where
722      T: std::fmt::Debug + Clone + Serialize + DeserializeOwned + Unpin + Send + Sync + 'static,
723  {
724      let connector = TlsTcpConnector::new(certs, network.identity).into_dyn();
725      let (connections, _) =
726          ReconnectPeerConnectionsReliable::new(network, delay_calculator, connector, task_group)
727              .await;
728      connections.into_dyn()
729  }
730  
731  pub fn gen_cert_and_key(
732      name: &str,
733  ) -> Result<(rustls::Certificate, rustls::PrivateKey), anyhow::Error> {
734      let keypair = rcgen::KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?;
735      let keypair_ser = keypair.serialize_der();
736      let mut params = rcgen::CertificateParams::new(vec![dns_sanitize(name)]);
737  
738      params.key_pair = Some(keypair);
739      params.alg = &rcgen::PKCS_ECDSA_P256_SHA256;
740      params.is_ca = rcgen::IsCa::NoCa;
741      params
742          .distinguished_name
743          .push(rcgen::DnType::CommonName, dns_sanitize(name));
744  
745      let cert = rcgen::Certificate::from_params(params)?;
746  
747      Ok((
748          rustls::Certificate(cert.serialize_der()?),
749          rustls::PrivateKey(keypair_ser),
750      ))
751  }
752  
753  mod serde_tls_cert_map {
754      use std::borrow::Cow;
755      use std::collections::BTreeMap;
756  
757      use fedimint_core::PeerId;
758      use hex::{FromHex, ToHex};
759      use serde::de::Error;
760      use serde::ser::SerializeMap;
761      use serde::{Deserialize, Deserializer, Serializer};
762      use tokio_rustls::rustls;
763  
764      pub fn serialize<S>(
765          certs: &BTreeMap<PeerId, rustls::Certificate>,
766          serializer: S,
767      ) -> Result<S::Ok, S::Error>
768      where
769          S: Serializer,
770      {
771          let mut serializer = serializer.serialize_map(Some(certs.len()))?;
772          for (key, value) in certs.iter() {
773              serializer.serialize_key(key)?;
774              let hex_str = value.0.encode_hex::<String>();
775              serializer.serialize_value(&hex_str)?;
776          }
777          serializer.end()
778      }
779  
780      pub fn deserialize<'de, D>(
781          deserializer: D,
782      ) -> Result<BTreeMap<PeerId, rustls::Certificate>, D::Error>
783      where
784          D: Deserializer<'de>,
785      {
786          let map: BTreeMap<PeerId, Cow<str>> = Deserialize::deserialize(deserializer)?;
787          let mut certs = BTreeMap::new();
788  
789          for (key, value) in map {
790              let cert =
791                  rustls::Certificate(Vec::from_hex(value.as_ref()).map_err(D::Error::custom)?);
792              certs.insert(key, cert);
793          }
794          Ok(certs)
795      }
796  }
797  
798  mod serde_tls_key {
799      use std::borrow::Cow;
800  
801      use hex::{FromHex, ToHex};
802      use serde::{Deserialize, Deserializer, Serialize, Serializer};
803      use tokio_rustls::rustls;
804  
805      pub fn serialize<S>(key: &rustls::PrivateKey, serializer: S) -> Result<S::Ok, S::Error>
806      where
807          S: Serializer,
808      {
809          let hex_str = key.0.encode_hex::<String>();
810          Serialize::serialize(&hex_str, serializer)
811      }
812  
813      pub fn deserialize<'de, D>(deserializer: D) -> Result<rustls::PrivateKey, D::Error>
814      where
815          D: Deserializer<'de>,
816      {
817          let hex_str: Cow<str> = Deserialize::deserialize(deserializer)?;
818          let bytes = Vec::from_hex(hex_str.as_ref()).map_err(serde::de::Error::custom)?;
819          Ok(rustls::PrivateKey(bytes))
820      }
821  }