1use std::collections::{BTreeMap, BTreeSet, HashMap};
2use std::env;
3use std::net::SocketAddr;
4use std::time::Duration;
5
6use anyhow::{bail, format_err, Context};
7use fedimint_api_client::api::P2PConnectionStatus;
8use fedimint_core::admin_client::ConfigGenParamsConsensus;
9pub use fedimint_core::config::{
10 serde_binary_human_readable, ClientConfig, DkgPeerMessage, FederationId, GlobalClientConfig,
11 JsonWithKind, ModuleInitRegistry, PeerUrl, ServerModuleConfig, ServerModuleConsensusConfig,
12 TypedServerModuleConfig,
13};
14use fedimint_core::core::{ModuleInstanceId, ModuleKind};
15use fedimint_core::envs::is_running_in_test_env;
16use fedimint_core::invite_code::InviteCode;
17use fedimint_core::module::{
18 ApiAuth, ApiVersion, CoreConsensusVersion, MultiApiVersion, PeerHandle,
19 SupportedApiVersionsSummary, SupportedCoreApiVersions, CORE_CONSENSUS_VERSION,
20};
21use fedimint_core::net::peers::{IP2PConnections, Recipient};
22use fedimint_core::task::{sleep, timeout, TaskGroup};
23use fedimint_core::{secp256k1, timing, NumPeersExt, PeerId};
24use fedimint_logging::LOG_NET_PEER_DKG;
25use fedimint_server_core::{DynServerModuleInit, ServerModuleInitRegistry};
26use rand::rngs::OsRng;
27use secp256k1::{PublicKey, Secp256k1, SecretKey};
28use serde::{Deserialize, Serialize};
29use tokio::sync::watch;
30use tokio_rustls::rustls;
31use tracing::{error, info};
32
33use crate::config::api::ConfigGenParamsLocal;
34use crate::config::distributedgen::PeerHandleOps;
35use crate::envs::FM_MAX_CLIENT_CONNECTIONS_ENV;
36use crate::fedimint_core::encoding::Encodable;
37use crate::net::p2p::ReconnectP2PConnections;
38use crate::net::p2p_connector::{dns_sanitize, IP2PConnector, TlsConfig, TlsTcpConnector};
39
40pub mod api;
41pub mod distributedgen;
42pub mod io;
43
44pub const DEFAULT_MAX_CLIENT_CONNECTIONS: u32 = 1000;
46
47const DEFAULT_BROADCAST_ROUND_DELAY_MS: u16 = 50;
49const DEFAULT_BROADCAST_ROUNDS_PER_SESSION: u16 = 3600;
50
51fn default_broadcast_rounds_per_session() -> u16 {
52 DEFAULT_BROADCAST_ROUNDS_PER_SESSION
53}
54
55const DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS: u16 = 50;
57const DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION: u16 = 200;
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct ServerConfig {
62 pub consensus: ServerConfigConsensus,
64 pub local: ServerConfigLocal,
66 pub private: ServerConfigPrivate,
69}
70
71impl ServerConfig {
72 pub fn iter_module_instances(
73 &self,
74 ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
75 self.consensus.iter_module_instances()
76 }
77
78 pub(crate) fn supported_api_versions_summary(
79 modules: &BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
80 module_inits: &ServerModuleInitRegistry,
81 ) -> SupportedApiVersionsSummary {
82 SupportedApiVersionsSummary {
83 core: Self::supported_api_versions(),
84 modules: modules
85 .iter()
86 .map(|(&id, config)| {
87 (
88 id,
89 module_inits
90 .get(&config.kind)
91 .expect("missing module kind gen")
92 .supported_api_versions(),
93 )
94 })
95 .collect(),
96 }
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ServerConfigPrivate {
102 pub api_auth: ApiAuth,
104 #[serde(with = "serde_tls_key")]
106 pub tls_key: rustls::PrivateKey,
107 pub broadcast_secret_key: SecretKey,
109 pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
114pub struct ServerConfigConsensus {
115 pub code_version: String,
117 pub version: CoreConsensusVersion,
119 pub broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
121 #[serde(default = "default_broadcast_rounds_per_session")]
123 pub broadcast_rounds_per_session: u16,
124 pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
126 #[serde(with = "serde_tls_cert_map")]
128 pub tls_certs: BTreeMap<PeerId, rustls::Certificate>,
129 pub modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
131 pub meta: BTreeMap<String, String>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ServerConfigLocal {
139 pub p2p_endpoints: BTreeMap<PeerId, PeerUrl>,
141 pub identity: PeerId,
143 pub max_connections: u32,
145 pub broadcast_round_delay_ms: u16,
150 pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
152}
153
154#[derive(Debug, Clone)]
155pub struct ConfigGenParams {
160 pub local: ConfigGenParamsLocal,
161 pub consensus: ConfigGenParamsConsensus,
162}
163
164impl ServerConfigConsensus {
165 pub fn iter_module_instances(
166 &self,
167 ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
168 self.modules.iter().map(|(k, v)| (*k, &v.kind))
169 }
170
171 pub fn to_client_config(
172 &self,
173 module_config_gens: &ModuleInitRegistry<DynServerModuleInit>,
174 ) -> Result<ClientConfig, anyhow::Error> {
175 let client = ClientConfig {
176 global: GlobalClientConfig {
177 api_endpoints: self.api_endpoints.clone(),
178 broadcast_public_keys: Some(self.broadcast_public_keys.clone()),
179 consensus_version: self.version,
180 meta: self.meta.clone(),
181 },
182 modules: self
183 .modules
184 .iter()
185 .map(|(k, v)| {
186 let gen = module_config_gens
187 .get(&v.kind)
188 .ok_or_else(|| format_err!("Module gen kind={} not found", v.kind))?;
189 Ok((*k, gen.get_client_config(*k, v)?))
190 })
191 .collect::<anyhow::Result<BTreeMap<_, _>>>()?,
192 };
193 Ok(client)
194 }
195}
196
197impl ServerConfig {
198 pub fn supported_api_versions() -> SupportedCoreApiVersions {
200 SupportedCoreApiVersions {
201 core_consensus: CORE_CONSENSUS_VERSION,
202 api: MultiApiVersion::try_from_iter([ApiVersion { major: 0, minor: 5 }])
203 .expect("not version conflicts"),
204 }
205 }
206 pub fn from(
209 params: ConfigGenParams,
210 identity: PeerId,
211 broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
212 broadcast_secret_key: SecretKey,
213 modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>,
214 code_version_str: String,
215 ) -> Self {
216 let private = ServerConfigPrivate {
217 api_auth: params.local.api_auth.clone(),
218 tls_key: params.local.our_private_key.clone(),
219 broadcast_secret_key,
220 modules: BTreeMap::new(),
221 };
222 let local = ServerConfigLocal {
223 p2p_endpoints: params.p2p_urls(),
224 identity,
225 max_connections: DEFAULT_MAX_CLIENT_CONNECTIONS,
226 broadcast_round_delay_ms: if is_running_in_test_env() {
227 DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS
228 } else {
229 DEFAULT_BROADCAST_ROUND_DELAY_MS
230 },
231 modules: BTreeMap::new(),
232 };
233 let consensus = ServerConfigConsensus {
234 code_version: code_version_str,
235 version: CORE_CONSENSUS_VERSION,
236 broadcast_public_keys,
237 broadcast_rounds_per_session: if is_running_in_test_env() {
238 DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION
239 } else {
240 DEFAULT_BROADCAST_ROUNDS_PER_SESSION
241 },
242 api_endpoints: params.api_urls(),
243 tls_certs: params.tls_certs(),
244 modules: BTreeMap::new(),
245 meta: params.consensus.meta,
246 };
247 let mut cfg = Self {
248 consensus,
249 local,
250 private,
251 };
252 cfg.add_modules(modules);
253 cfg
254 }
255
256 pub fn get_invite_code(&self, api_secret: Option<String>) -> InviteCode {
257 InviteCode::new(
258 self.consensus.api_endpoints[&self.local.identity]
259 .url
260 .clone(),
261 self.local.identity,
262 self.calculate_federation_id(),
263 api_secret,
264 )
265 }
266
267 pub fn calculate_federation_id(&self) -> FederationId {
268 FederationId(self.consensus.api_endpoints.consensus_hash())
269 }
270
271 pub fn add_modules(&mut self, modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>) {
272 for (name, config) in modules {
273 let ServerModuleConfig {
274 local,
275 private,
276 consensus,
277 } = config;
278
279 self.local.modules.insert(name, local);
280 self.private.modules.insert(name, private);
281 self.consensus.modules.insert(name, consensus);
282 }
283 }
284
285 pub fn get_module_config_typed<T: TypedServerModuleConfig>(
287 &self,
288 id: ModuleInstanceId,
289 ) -> anyhow::Result<T> {
290 let local = Self::get_module_cfg_by_instance_id(&self.local.modules, id)?;
291 let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
292 let consensus = self
293 .consensus
294 .modules
295 .get(&id)
296 .ok_or_else(|| format_err!("Module {id} not found"))?
297 .clone();
298 let module = ServerModuleConfig::from(local, private, consensus);
299
300 module.to_typed()
301 }
302 pub fn get_module_id_by_kind(
303 &self,
304 kind: impl Into<ModuleKind>,
305 ) -> anyhow::Result<ModuleInstanceId> {
306 let kind = kind.into();
307 Ok(*self
308 .consensus
309 .modules
310 .iter()
311 .find(|(_, v)| v.kind == kind)
312 .ok_or_else(|| format_err!("Module {kind} not found"))?
313 .0)
314 }
315
316 pub fn get_module_config(&self, id: ModuleInstanceId) -> anyhow::Result<ServerModuleConfig> {
318 let local = Self::get_module_cfg_by_instance_id(&self.local.modules, id)?;
319 let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
320 let consensus = self
321 .consensus
322 .modules
323 .get(&id)
324 .ok_or_else(|| format_err!("Module {id} not found"))?
325 .clone();
326 Ok(ServerModuleConfig::from(local, private, consensus))
327 }
328
329 fn get_module_cfg_by_instance_id(
330 json: &BTreeMap<ModuleInstanceId, JsonWithKind>,
331 id: ModuleInstanceId,
332 ) -> anyhow::Result<JsonWithKind> {
333 Ok(json
334 .get(&id)
335 .ok_or_else(|| format_err!("Module {id} not found"))
336 .cloned()?
337 .with_fixed_empty_value())
338 }
339
340 pub fn validate_config(
341 &self,
342 identity: &PeerId,
343 module_config_gens: &ServerModuleInitRegistry,
344 ) -> anyhow::Result<()> {
345 let peers = self.local.p2p_endpoints.clone();
346 let consensus = self.consensus.clone();
347 let private = self.private.clone();
348
349 let my_public_key = private.broadcast_secret_key.public_key(&Secp256k1::new());
350
351 if Some(&my_public_key) != consensus.broadcast_public_keys.get(identity) {
352 bail!("Broadcast secret key doesn't match corresponding public key");
353 }
354 if peers.keys().max().copied().map(PeerId::to_usize) != Some(peers.len() - 1) {
355 bail!("Peer ids are not indexed from 0");
356 }
357 if peers.keys().min().copied() != Some(PeerId::from(0)) {
358 bail!("Peer ids are not indexed from 0");
359 }
360
361 for (module_id, module_kind) in &self
362 .consensus
363 .modules
364 .iter()
365 .map(|(id, config)| Ok((*id, config.kind.clone())))
366 .collect::<anyhow::Result<BTreeSet<_>>>()?
367 {
368 module_config_gens
369 .get(module_kind)
370 .ok_or_else(|| format_err!("module config gen not found {module_kind}"))?
371 .validate_config(identity, self.get_module_config(*module_id)?)?;
372 }
373
374 Ok(())
375 }
376
377 pub fn trusted_dealer_gen(
378 params: &HashMap<PeerId, ConfigGenParams>,
379 registry: &ServerModuleInitRegistry,
380 code_version_str: &str,
381 ) -> BTreeMap<PeerId, Self> {
382 let peer0 = ¶ms[&PeerId::from(0)];
383
384 let mut broadcast_pks = BTreeMap::new();
385 let mut broadcast_sks = BTreeMap::new();
386 for peer_id in peer0.peer_ids() {
387 let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
388 broadcast_pks.insert(peer_id, broadcast_pk);
389 broadcast_sks.insert(peer_id, broadcast_sk);
390 }
391
392 let modules = peer0.consensus.modules.iter_modules();
393 let module_configs: BTreeMap<_, _> = modules
394 .map(|(module_id, kind, module_params)| {
395 (
396 module_id,
397 registry
398 .get(kind)
399 .expect("Module not registered")
400 .trusted_dealer_gen(&peer0.peer_ids(), module_params),
401 )
402 })
403 .collect();
404
405 let server_config: BTreeMap<_, _> = peer0
406 .peer_ids()
407 .iter()
408 .map(|&id| {
409 let config = ServerConfig::from(
410 params[&id].clone(),
411 id,
412 broadcast_pks.clone(),
413 *broadcast_sks.get(&id).expect("We created this entry"),
414 module_configs
415 .iter()
416 .map(|(module_id, cfgs)| (*module_id, cfgs[&id].clone()))
417 .collect(),
418 code_version_str.to_string(),
419 );
420 (id, config)
421 })
422 .collect();
423
424 server_config
425 }
426
427 pub async fn distributed_gen(
429 p2p_bind_addr: SocketAddr,
430 params: &ConfigGenParams,
431 registry: ServerModuleInitRegistry,
432 task_group: &TaskGroup,
433 code_version_str: String,
434 ) -> anyhow::Result<Self> {
435 let _timing = timing::TimeReporter::new("distributed-gen").info();
436
437 if params.peer_ids().len() == 1 {
439 let server = Self::trusted_dealer_gen(
440 &HashMap::from([(params.local.our_id, params.clone())]),
441 ®istry,
442 &code_version_str,
443 );
444 return Ok(server[¶ms.local.our_id].clone());
445 }
446
447 let connector = TlsTcpConnector::new(
448 params.tls_config(),
449 p2p_bind_addr,
450 params
451 .p2p_urls()
452 .into_iter()
453 .map(|(id, peer)| (id, peer.url))
454 .collect(),
455 params.local.our_id,
456 )
457 .into_dyn();
458
459 let mut p2p_status_senders = BTreeMap::new();
460 let mut p2p_status_receivers = BTreeMap::new();
461
462 for peer in connector.peers() {
463 let (p2p_sender, p2p_receiver) = watch::channel(P2PConnectionStatus::Disconnected);
464
465 p2p_status_senders.insert(peer, p2p_sender);
466 p2p_status_receivers.insert(peer, p2p_receiver);
467 }
468
469 let connections = ReconnectP2PConnections::new(
470 params.local.our_id,
471 connector,
472 task_group,
473 Some(p2p_status_senders),
474 )
475 .await
476 .into_dyn();
477
478 while p2p_status_receivers
479 .values()
480 .any(|r| *r.borrow() == P2PConnectionStatus::Disconnected)
481 {
482 info!(
483 target: LOG_NET_PEER_DKG,
484 "Waiting for all p2p connections to open..."
485 );
486
487 sleep(Duration::from_secs(1)).await;
488 }
489
490 info!(
491 target: LOG_NET_PEER_DKG,
492 "Running distributed key generation..."
493 );
494
495 let handle = PeerHandle::new(
496 params.peer_ids().to_num_peers(),
497 params.local.our_id,
498 &connections,
499 );
500
501 let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
502
503 let broadcast_public_keys = handle.exchange_encodable(broadcast_pk).await?;
504
505 let mut module_cfgs = BTreeMap::new();
506
507 for (module_id, kind, module_params) in params.consensus.modules.iter_modules() {
508 info!(
509 target: LOG_NET_PEER_DKG,
510 "Running distributed key generation for module of kind {kind}..."
511 );
512
513 let cfg = registry
514 .get(kind)
515 .context("Module of kind {kind} not found")?
516 .distributed_gen(&handle, module_params)
517 .await?;
518
519 module_cfgs.insert(module_id, cfg);
520 }
521
522 info!(
523 target: LOG_NET_PEER_DKG,
524 "Distributed key generation has completed successfully!"
525 );
526
527 connections
528 .send(Recipient::Everyone, DkgPeerMessage::Completed)
529 .await;
530
531 if timeout(Duration::from_secs(30), async {
532 for peer in params
533 .peer_ids()
534 .into_iter()
535 .filter(|p| *p != params.local.our_id)
536 {
537 connections.receive_from_peer(peer).await;
538 }
539 })
540 .await
541 .is_err()
542 {
543 error!(target: LOG_NET_PEER_DKG, "Timeout waiting for dkg completion confirmation");
544 }
545
546 let server = ServerConfig::from(
547 params.clone(),
548 params.local.our_id,
549 broadcast_public_keys,
550 broadcast_sk,
551 module_cfgs,
552 code_version_str,
553 );
554
555 Ok(server)
556 }
557}
558
559impl ServerConfig {
560 pub fn tls_config(&self) -> TlsConfig {
561 TlsConfig {
562 private_key: self.private.tls_key.clone(),
563 certificates: self.consensus.tls_certs.clone(),
564 peer_names: self
565 .local
566 .p2p_endpoints
567 .iter()
568 .map(|(id, endpoint)| (*id, endpoint.name.to_string()))
569 .collect(),
570 }
571 }
572
573 pub fn get_incoming_count(&self) -> u16 {
574 self.local.identity.into()
575 }
576}
577
578impl ConfigGenParams {
579 pub fn peer_ids(&self) -> Vec<PeerId> {
580 self.consensus.peers.keys().copied().collect()
581 }
582
583 pub fn tls_config(&self) -> TlsConfig {
584 TlsConfig {
585 private_key: self.local.our_private_key.clone(),
586 certificates: self.tls_certs(),
587 peer_names: self
588 .p2p_urls()
589 .into_iter()
590 .map(|(id, peer)| (id, peer.name))
591 .collect(),
592 }
593 }
594
595 pub fn tls_certs(&self) -> BTreeMap<PeerId, rustls::Certificate> {
596 self.consensus
597 .peers
598 .iter()
599 .map(|(id, peer)| (*id, peer.cert.clone()))
600 .collect::<BTreeMap<_, _>>()
601 }
602
603 pub fn p2p_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
604 self.consensus
605 .peers
606 .iter()
607 .map(|(id, peer)| {
608 (
609 *id,
610 PeerUrl {
611 name: peer.name.clone(),
612 url: peer.p2p_url.clone(),
613 },
614 )
615 })
616 .collect::<BTreeMap<_, _>>()
617 }
618
619 pub fn api_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
620 self.consensus
621 .peers
622 .iter()
623 .map(|(id, peer)| {
624 (
625 *id,
626 PeerUrl {
627 name: peer.name.clone(),
628 url: peer.api_url.clone(),
629 },
630 )
631 })
632 .collect::<BTreeMap<_, _>>()
633 }
634}
635
636pub fn max_connections() -> u32 {
638 env::var(FM_MAX_CLIENT_CONNECTIONS_ENV)
639 .ok()
640 .and_then(|s| s.parse().ok())
641 .unwrap_or(DEFAULT_MAX_CLIENT_CONNECTIONS)
642}
643
644pub fn gen_cert_and_key(
645 name: &str,
646) -> Result<(rustls::Certificate, rustls::PrivateKey), anyhow::Error> {
647 let keypair = rcgen::KeyPair::generate()?;
648 let keypair_ser = keypair.serialize_der();
649 let mut params = rcgen::CertificateParams::new(vec![dns_sanitize(name)])?;
650
651 params.is_ca = rcgen::IsCa::NoCa;
652 params
653 .distinguished_name
654 .push(rcgen::DnType::CommonName, dns_sanitize(name));
655
656 let cert = params.self_signed(&keypair)?;
657
658 Ok((
659 rustls::Certificate(cert.der().to_vec()),
660 rustls::PrivateKey(keypair_ser),
661 ))
662}
663
664mod serde_tls_cert_map {
665 use std::borrow::Cow;
666 use std::collections::BTreeMap;
667
668 use fedimint_core::PeerId;
669 use hex::{FromHex, ToHex};
670 use serde::de::Error;
671 use serde::ser::SerializeMap;
672 use serde::{Deserialize, Deserializer, Serializer};
673 use tokio_rustls::rustls;
674
675 pub fn serialize<S>(
676 certs: &BTreeMap<PeerId, rustls::Certificate>,
677 serializer: S,
678 ) -> Result<S::Ok, S::Error>
679 where
680 S: Serializer,
681 {
682 let mut serializer = serializer.serialize_map(Some(certs.len()))?;
683 for (key, value) in certs {
684 serializer.serialize_key(key)?;
685 let hex_str = value.0.encode_hex::<String>();
686 serializer.serialize_value(&hex_str)?;
687 }
688 serializer.end()
689 }
690
691 pub fn deserialize<'de, D>(
692 deserializer: D,
693 ) -> Result<BTreeMap<PeerId, rustls::Certificate>, D::Error>
694 where
695 D: Deserializer<'de>,
696 {
697 let map: BTreeMap<PeerId, Cow<str>> = Deserialize::deserialize(deserializer)?;
698 let mut certs = BTreeMap::new();
699
700 for (key, value) in map {
701 let cert =
702 rustls::Certificate(Vec::from_hex(value.as_ref()).map_err(D::Error::custom)?);
703 certs.insert(key, cert);
704 }
705 Ok(certs)
706 }
707}
708
709mod serde_tls_key {
710 use std::borrow::Cow;
711
712 use hex::{FromHex, ToHex};
713 use serde::{Deserialize, Deserializer, Serialize, Serializer};
714 use tokio_rustls::rustls;
715
716 pub fn serialize<S>(key: &rustls::PrivateKey, serializer: S) -> Result<S::Ok, S::Error>
717 where
718 S: Serializer,
719 {
720 let hex_str = key.0.encode_hex::<String>();
721 Serialize::serialize(&hex_str, serializer)
722 }
723
724 pub fn deserialize<'de, D>(deserializer: D) -> Result<rustls::PrivateKey, D::Error>
725 where
726 D: Deserializer<'de>,
727 {
728 let hex_str: Cow<str> = Deserialize::deserialize(deserializer)?;
729 let bytes = Vec::from_hex(hex_str.as_ref()).map_err(serde::de::Error::custom)?;
730 Ok(rustls::PrivateKey(bytes))
731 }
732}