fedimint_server/config/
mod.rs

1use std::collections::{BTreeMap, BTreeSet, HashMap};
2use std::net::SocketAddr;
3use std::sync::Arc;
4use std::time::Duration;
5
6use anyhow::{Context, bail, format_err};
7use bitcoin::hashes::sha256;
8use fedimint_core::config::ServerModuleConfigGenParamsRegistry;
9pub use fedimint_core::config::{
10    ClientConfig, FederationId, GlobalClientConfig, JsonWithKind, ModuleInitRegistry, P2PMessage,
11    PeerUrl, ServerModuleConfig, ServerModuleConsensusConfig, TypedServerModuleConfig,
12};
13use fedimint_core::core::{ModuleInstanceId, ModuleKind};
14use fedimint_core::envs::is_running_in_test_env;
15use fedimint_core::invite_code::InviteCode;
16use fedimint_core::module::{
17    ApiAuth, ApiVersion, CORE_CONSENSUS_VERSION, CoreConsensusVersion, MultiApiVersion,
18    SupportedApiVersionsSummary, SupportedCoreApiVersions,
19};
20use fedimint_core::net::peers::{DynP2PConnections, Recipient};
21use fedimint_core::setup_code::{PeerEndpoints, PeerSetupCode};
22use fedimint_core::task::sleep;
23use fedimint_core::util::SafeUrl;
24use fedimint_core::{NumPeersExt, PeerId, secp256k1, timing};
25use fedimint_logging::LOG_NET_PEER_DKG;
26use fedimint_server_core::config::PeerHandleOpsExt as _;
27use fedimint_server_core::{DynServerModuleInit, ServerModuleInitRegistry};
28use hex::{FromHex, ToHex};
29use peer_handle::PeerHandle;
30use rand::rngs::OsRng;
31use secp256k1::{PublicKey, Secp256k1, SecretKey};
32use serde::{Deserialize, Serialize};
33use tokio_rustls::rustls;
34use tracing::{error, info, warn};
35
36use crate::fedimint_core::encoding::Encodable;
37use crate::net::p2p::P2PStatusReceivers;
38use crate::net::p2p_connector::TlsConfig;
39
40pub mod dkg;
41pub mod io;
42pub mod peer_handle;
43pub mod setup;
44
45/// The default maximum open connections the API can handle
46pub const DEFAULT_MAX_CLIENT_CONNECTIONS: u32 = 1000;
47
48/// Consensus broadcast settings that result in 3 minutes session time
49const DEFAULT_BROADCAST_ROUND_DELAY_MS: u16 = 50;
50const DEFAULT_BROADCAST_ROUNDS_PER_SESSION: u16 = 3600;
51
52fn default_broadcast_rounds_per_session() -> u16 {
53    DEFAULT_BROADCAST_ROUNDS_PER_SESSION
54}
55
56/// Consensus broadcast settings that result in 10 seconds session time
57const DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS: u16 = 50;
58const DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION: u16 = 200;
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61/// All the serializable configuration for the fedimint server
62pub struct ServerConfig {
63    /// Contains all configuration that needs to be the same for every server
64    pub consensus: ServerConfigConsensus,
65    /// Contains all configuration that is locally configurable and not secret
66    pub local: ServerConfigLocal,
67    /// Contains all configuration that will be encrypted such as private key
68    /// material
69    pub private: ServerConfigPrivate,
70}
71
72impl ServerConfig {
73    pub fn iter_module_instances(
74        &self,
75    ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
76        self.consensus.iter_module_instances()
77    }
78
79    pub(crate) fn supported_api_versions_summary(
80        modules: &BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
81        module_inits: &ServerModuleInitRegistry,
82    ) -> SupportedApiVersionsSummary {
83        SupportedApiVersionsSummary {
84            core: Self::supported_api_versions(),
85            modules: modules
86                .iter()
87                .map(|(&id, config)| {
88                    (
89                        id,
90                        module_inits
91                            .get(&config.kind)
92                            .expect("missing module kind gen")
93                            .supported_api_versions(),
94                    )
95                })
96                .collect(),
97        }
98    }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ServerConfigPrivate {
103    /// Secret API auth string
104    pub api_auth: ApiAuth,
105    /// Optional secret key for our websocket p2p endpoint
106    pub tls_key: Option<String>,
107    /// Optional secret key for our iroh api endpoint
108    #[serde(default)]
109    pub iroh_api_sk: Option<iroh::SecretKey>,
110    /// Optional secret key for our iroh p2p endpoint
111    #[serde(default)]
112    pub iroh_p2p_sk: Option<iroh::SecretKey>,
113    /// Secret key for the atomic broadcast to sign messages
114    pub broadcast_secret_key: SecretKey,
115    /// Secret material from modules
116    pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
120pub struct ServerConfigConsensus {
121    /// The version of the binary code running
122    pub code_version: String,
123    /// Agreed on core consensus version
124    pub version: CoreConsensusVersion,
125    /// Public keys for the atomic broadcast to authenticate messages
126    pub broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
127    /// Number of rounds per session.
128    #[serde(default = "default_broadcast_rounds_per_session")]
129    pub broadcast_rounds_per_session: u16,
130    /// Network addresses and names for all peer APIs
131    pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
132    /// Public keys for all iroh api and p2p endpoints
133    #[serde(default)]
134    pub iroh_endpoints: BTreeMap<PeerId, PeerIrohEndpoints>,
135    /// Certs for TLS communication, required for peer authentication
136    pub tls_certs: BTreeMap<PeerId, String>,
137    /// All configuration that needs to be the same for modules
138    pub modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
139    /// Additional config the federation wants to transmit to the clients
140    pub meta: BTreeMap<String, String>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
144pub struct PeerIrohEndpoints {
145    /// The peer's name
146    pub name: String,
147    /// Public key for our iroh api endpoint
148    pub api_pk: iroh::PublicKey,
149    /// Public key for our iroh p2p endpoint
150    pub p2p_pk: iroh::PublicKey,
151}
152
153pub fn legacy_consensus_config_hash(cfg: &ServerConfigConsensus) -> sha256::Hash {
154    #[derive(Encodable)]
155    struct LegacyServerConfigConsensusHashMap {
156        code_version: String,
157        version: CoreConsensusVersion,
158        broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
159        broadcast_rounds_per_session: u16,
160        api_endpoints: BTreeMap<PeerId, PeerUrl>,
161        tls_certs: BTreeMap<PeerId, String>,
162        modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
163        meta: BTreeMap<String, String>,
164    }
165
166    LegacyServerConfigConsensusHashMap {
167        code_version: cfg.code_version.clone(),
168        version: cfg.version,
169        broadcast_public_keys: cfg.broadcast_public_keys.clone(),
170        broadcast_rounds_per_session: cfg.broadcast_rounds_per_session,
171        api_endpoints: cfg.api_endpoints.clone(),
172        tls_certs: cfg.tls_certs.clone(),
173        modules: cfg.modules.clone(),
174        meta: cfg.meta.clone(),
175    }
176    .consensus_hash_sha256()
177}
178
179// FIXME: (@leonardo) Should this have another field for the expected transport
180// ? (e.g. clearnet/tor/...)
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct ServerConfigLocal {
183    /// Network addresses and names for all p2p connections
184    pub p2p_endpoints: BTreeMap<PeerId, PeerUrl>,
185    /// Our peer id (generally should not change)
186    pub identity: PeerId,
187    /// How many API connections we will accept
188    pub max_connections: u32,
189    /// Influences the atomic broadcast ordering latency, should be higher than
190    /// the expected latency between peers so everyone can get proposed
191    /// consensus items confirmed. This is only relevant for byzantine
192    /// faults.
193    pub broadcast_round_delay_ms: u16,
194}
195
196/// All the info we configure prior to config gen starting
197#[derive(Debug, Clone)]
198pub struct ConfigGenSettings {
199    /// Bind address for our P2P connection (both iroh and tcp/tls)
200    pub p2p_bind: SocketAddr,
201    /// Bind address for our API
202    pub api_bind: SocketAddr,
203    /// Bind address for our UI connection (always http)
204    pub ui_bind: SocketAddr,
205    /// URL for our P2P connection
206    pub p2p_url: Option<SafeUrl>,
207    /// URL for our API connection
208    pub api_url: Option<SafeUrl>,
209    /// Enable iroh for networking
210    pub enable_iroh: bool,
211    /// Optional URL of the Iroh DNS server
212    pub iroh_dns: Option<SafeUrl>,
213    /// Optional URLs of the Iroh relays to register on
214    pub iroh_relays: Vec<SafeUrl>,
215    /// Set the params (if leader) or just the local params (if follower)
216    pub modules: ServerModuleConfigGenParamsRegistry,
217    /// Registry for config gen
218    pub registry: ServerModuleInitRegistry,
219}
220
221#[derive(Debug, Clone)]
222/// All the parameters necessary for generating the `ServerConfig` during setup
223///
224/// * Guardians can create the parameters using a setup UI or CLI tool
225/// * Used for distributed or trusted config generation
226pub struct ConfigGenParams {
227    /// Our own peer id
228    pub identity: PeerId,
229    /// Our TLS certificate private key
230    pub tls_key: Option<Arc<rustls::pki_types::PrivateKeyDer<'static>>>,
231    /// Optional secret key for our iroh api endpoint
232    pub iroh_api_sk: Option<iroh::SecretKey>,
233    /// Optional secret key for our iroh p2p endpoint
234    pub iroh_p2p_sk: Option<iroh::SecretKey>,
235    /// Secret API auth string
236    pub api_auth: ApiAuth,
237    /// Endpoints of all servers
238    pub peers: BTreeMap<PeerId, PeerSetupCode>,
239    /// Guardian-defined key-value pairs that will be passed to the client
240    pub meta: BTreeMap<String, String>,
241    /// Whether to disable base fees for this federation
242    pub disable_base_fees: bool,
243}
244
245impl ServerConfigConsensus {
246    pub fn api_endpoints(&self) -> BTreeMap<PeerId, PeerUrl> {
247        if self.iroh_endpoints.is_empty() {
248            self.api_endpoints.clone()
249        } else {
250            self.iroh_endpoints
251                .iter()
252                .map(|(peer, endpoints)| {
253                    let url = PeerUrl {
254                        name: endpoints.name.clone(),
255                        url: SafeUrl::parse(&format!("iroh://{}", endpoints.api_pk))
256                            .expect("Failed to parse iroh url"),
257                    };
258
259                    (*peer, url)
260                })
261                .collect()
262        }
263    }
264
265    pub fn iter_module_instances(
266        &self,
267    ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
268        self.modules.iter().map(|(k, v)| (*k, &v.kind))
269    }
270
271    pub fn to_client_config(
272        &self,
273        module_config_gens: &ModuleInitRegistry<DynServerModuleInit>,
274    ) -> Result<ClientConfig, anyhow::Error> {
275        let client = ClientConfig {
276            global: GlobalClientConfig {
277                api_endpoints: self.api_endpoints(),
278                broadcast_public_keys: Some(self.broadcast_public_keys.clone()),
279                consensus_version: self.version,
280                meta: self.meta.clone(),
281            },
282            modules: self
283                .modules
284                .iter()
285                .map(|(k, v)| {
286                    let r#gen = module_config_gens
287                        .get(&v.kind)
288                        .ok_or_else(|| format_err!("Module gen kind={} not found", v.kind))?;
289                    Ok((*k, r#gen.get_client_config(*k, v)?))
290                })
291                .collect::<anyhow::Result<BTreeMap<_, _>>>()?,
292        };
293        Ok(client)
294    }
295}
296
297impl ServerConfig {
298    /// Api versions supported by this server
299    pub fn supported_api_versions() -> SupportedCoreApiVersions {
300        SupportedCoreApiVersions {
301            core_consensus: CORE_CONSENSUS_VERSION,
302            api: MultiApiVersion::try_from_iter([ApiVersion { major: 0, minor: 7 }])
303                .expect("not version conflicts"),
304        }
305    }
306    /// Creates a new config from the results of a trusted or distributed key
307    /// setup
308    pub fn from(
309        params: ConfigGenParams,
310        identity: PeerId,
311        broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
312        broadcast_secret_key: SecretKey,
313        modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>,
314        code_version: String,
315    ) -> Self {
316        let consensus = ServerConfigConsensus {
317            code_version,
318            version: CORE_CONSENSUS_VERSION,
319            broadcast_public_keys,
320            broadcast_rounds_per_session: if is_running_in_test_env() {
321                DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION
322            } else {
323                DEFAULT_BROADCAST_ROUNDS_PER_SESSION
324            },
325            api_endpoints: params.api_urls(),
326            iroh_endpoints: params.iroh_endpoints(),
327            tls_certs: params.tls_certs(),
328            modules: modules
329                .iter()
330                .map(|(peer, cfg)| (*peer, cfg.consensus.clone()))
331                .collect(),
332            meta: params.meta.clone(),
333        };
334
335        let local = ServerConfigLocal {
336            p2p_endpoints: params.p2p_urls(),
337            identity,
338            max_connections: DEFAULT_MAX_CLIENT_CONNECTIONS,
339            broadcast_round_delay_ms: if is_running_in_test_env() {
340                DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS
341            } else {
342                DEFAULT_BROADCAST_ROUND_DELAY_MS
343            },
344        };
345
346        let private = ServerConfigPrivate {
347            api_auth: params.api_auth.clone(),
348            tls_key: params
349                .tls_key
350                .map(|key| key.secret_der().to_vec().encode_hex()),
351            iroh_api_sk: params.iroh_api_sk,
352            iroh_p2p_sk: params.iroh_p2p_sk,
353            broadcast_secret_key,
354            modules: modules
355                .iter()
356                .map(|(peer, cfg)| (*peer, cfg.private.clone()))
357                .collect(),
358        };
359
360        Self {
361            consensus,
362            local,
363            private,
364        }
365    }
366
367    pub fn get_invite_code(&self, api_secret: Option<String>) -> InviteCode {
368        InviteCode::new(
369            self.consensus.api_endpoints()[&self.local.identity]
370                .url
371                .clone(),
372            self.local.identity,
373            self.calculate_federation_id(),
374            api_secret,
375        )
376    }
377
378    pub fn calculate_federation_id(&self) -> FederationId {
379        FederationId(self.consensus.api_endpoints().consensus_hash())
380    }
381
382    /// Constructs a module config by name
383    pub fn get_module_config_typed<T: TypedServerModuleConfig>(
384        &self,
385        id: ModuleInstanceId,
386    ) -> anyhow::Result<T> {
387        let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
388        let consensus = self
389            .consensus
390            .modules
391            .get(&id)
392            .ok_or_else(|| format_err!("Typed module {id} not found"))?
393            .clone();
394        let module = ServerModuleConfig::from(private, consensus);
395
396        module.to_typed()
397    }
398    pub fn get_module_id_by_kind(
399        &self,
400        kind: impl Into<ModuleKind>,
401    ) -> anyhow::Result<ModuleInstanceId> {
402        let kind = kind.into();
403        Ok(*self
404            .consensus
405            .modules
406            .iter()
407            .find(|(_, v)| v.kind == kind)
408            .ok_or_else(|| format_err!("Module {kind} not found"))?
409            .0)
410    }
411
412    /// Constructs a module config by id
413    pub fn get_module_config(&self, id: ModuleInstanceId) -> anyhow::Result<ServerModuleConfig> {
414        let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
415        let consensus = self
416            .consensus
417            .modules
418            .get(&id)
419            .ok_or_else(|| format_err!("Module config {id} not found"))?
420            .clone();
421        Ok(ServerModuleConfig::from(private, consensus))
422    }
423
424    fn get_module_cfg_by_instance_id(
425        json: &BTreeMap<ModuleInstanceId, JsonWithKind>,
426        id: ModuleInstanceId,
427    ) -> anyhow::Result<JsonWithKind> {
428        Ok(json
429            .get(&id)
430            .ok_or_else(|| format_err!("Module cfg {id} not found"))
431            .cloned()?
432            .with_fixed_empty_value())
433    }
434
435    pub fn validate_config(
436        &self,
437        identity: &PeerId,
438        module_config_gens: &ServerModuleInitRegistry,
439    ) -> anyhow::Result<()> {
440        let endpoints = self.consensus.api_endpoints().clone();
441        let consensus = self.consensus.clone();
442        let private = self.private.clone();
443
444        let my_public_key = private.broadcast_secret_key.public_key(&Secp256k1::new());
445
446        if Some(&my_public_key) != consensus.broadcast_public_keys.get(identity) {
447            bail!("Broadcast secret key doesn't match corresponding public key");
448        }
449        if endpoints.keys().max().copied().map(PeerId::to_usize) != Some(endpoints.len() - 1) {
450            bail!("Peer ids are not indexed from 0");
451        }
452        if endpoints.keys().min().copied() != Some(PeerId::from(0)) {
453            bail!("Peer ids are not indexed from 0");
454        }
455
456        for (module_id, module_kind) in &self
457            .consensus
458            .modules
459            .iter()
460            .map(|(id, config)| Ok((*id, config.kind.clone())))
461            .collect::<anyhow::Result<BTreeSet<_>>>()?
462        {
463            module_config_gens
464                .get(module_kind)
465                .ok_or_else(|| format_err!("module config gen not found {module_kind}"))?
466                .validate_config(identity, self.get_module_config(*module_id)?)?;
467        }
468
469        Ok(())
470    }
471
472    pub fn trusted_dealer_gen(
473        modules: ServerModuleConfigGenParamsRegistry,
474        params: &HashMap<PeerId, ConfigGenParams>,
475        registry: &ServerModuleInitRegistry,
476        code_version_str: &str,
477    ) -> BTreeMap<PeerId, Self> {
478        let peer0 = &params[&PeerId::from(0)];
479
480        let mut broadcast_pks = BTreeMap::new();
481        let mut broadcast_sks = BTreeMap::new();
482        for peer_id in peer0.peer_ids() {
483            let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
484            broadcast_pks.insert(peer_id, broadcast_pk);
485            broadcast_sks.insert(peer_id, broadcast_sk);
486        }
487
488        let module_configs: BTreeMap<_, _> = modules
489            .iter_modules()
490            .map(|(module_id, kind, module_params)| {
491                (
492                    module_id,
493                    registry
494                        .get(kind)
495                        .expect("Module not registered")
496                        .trusted_dealer_gen(
497                            &peer0.peer_ids(),
498                            module_params,
499                            params[&PeerId::from(0)].disable_base_fees,
500                        ),
501                )
502            })
503            .collect();
504
505        let server_config: BTreeMap<_, _> = peer0
506            .peer_ids()
507            .iter()
508            .map(|&id| {
509                let config = ServerConfig::from(
510                    params[&id].clone(),
511                    id,
512                    broadcast_pks.clone(),
513                    *broadcast_sks.get(&id).expect("We created this entry"),
514                    module_configs
515                        .iter()
516                        .map(|(module_id, cfgs)| (*module_id, cfgs[&id].clone()))
517                        .collect(),
518                    code_version_str.to_string(),
519                );
520                (id, config)
521            })
522            .collect();
523
524        server_config
525    }
526
527    /// Runs the distributed key gen algorithm
528    pub async fn distributed_gen(
529        modules: ServerModuleConfigGenParamsRegistry,
530        params: &ConfigGenParams,
531        registry: ServerModuleInitRegistry,
532        code_version_str: String,
533        connections: DynP2PConnections<P2PMessage>,
534        p2p_status_receivers: P2PStatusReceivers,
535    ) -> anyhow::Result<Self> {
536        let _timing /* logs on drop */ = timing::TimeReporter::new("distributed-gen").info();
537
538        // in case we are running by ourselves, avoid DKG
539        if params.peer_ids().len() == 1 {
540            let server = Self::trusted_dealer_gen(
541                modules,
542                &HashMap::from([(params.identity, params.clone())]),
543                &registry,
544                &code_version_str,
545            );
546
547            return Ok(server[&params.identity].clone());
548        }
549
550        info!(
551            target: LOG_NET_PEER_DKG,
552            "Waiting for all p2p connections to open..."
553        );
554
555        while p2p_status_receivers.values().any(|r| r.borrow().is_none()) {
556            let connected_peers = p2p_status_receivers
557                .iter()
558                .filter_map(|entry| entry.1.borrow().is_some().then_some(*entry.0))
559                .collect::<Vec<PeerId>>();
560
561            let disconnected_peers = p2p_status_receivers
562                .iter()
563                .filter_map(|entry| entry.1.borrow().is_none().then_some(*entry.0))
564                .collect::<Vec<PeerId>>();
565
566            info!(
567                target: LOG_NET_PEER_DKG,
568                connected_peers = ?connected_peers,
569                disconnected_peers = ?disconnected_peers,
570                "Waiting for all p2p connections to open..."
571            );
572
573            sleep(Duration::from_secs(1)).await;
574        }
575
576        let checksum = params.peers.consensus_hash_sha256();
577
578        info!(
579            target: LOG_NET_PEER_DKG,
580            "Comparing connection codes checksum {checksum}..."
581        );
582
583        connections
584            .send(Recipient::Everyone, P2PMessage::Checksum(checksum))
585            .await;
586
587        for peer in params
588            .peer_ids()
589            .into_iter()
590            .filter(|p| *p != params.identity)
591        {
592            let peer_message = connections
593                .receive_from_peer(peer)
594                .await
595                .context("Unexpected shutdown of p2p connections")?;
596
597            if peer_message != P2PMessage::Checksum(checksum) {
598                error!(
599                    target: LOG_NET_PEER_DKG,
600                    expected = ?P2PMessage::Checksum(checksum),
601                    received = ?peer_message,
602                    "Peer {peer} has sent invalid connection code checksum message"
603                );
604
605                bail!("Peer {peer} has sent invalid connection code checksum message");
606            }
607
608            info!(
609                target: LOG_NET_PEER_DKG,
610                "Peer {peer} has sent valid connection code checksum message"
611            );
612        }
613
614        info!(
615            target: LOG_NET_PEER_DKG,
616            "Running config generation..."
617        );
618
619        let handle = PeerHandle::new(
620            params.peer_ids().to_num_peers(),
621            params.identity,
622            &connections,
623        );
624
625        let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
626
627        let broadcast_public_keys = handle.exchange_encodable(broadcast_pk).await?;
628
629        let mut module_cfgs = BTreeMap::new();
630
631        for (module_id, kind, module_params) in modules.iter_modules() {
632            info!(
633                target: LOG_NET_PEER_DKG,
634                "Running config generation for module of kind {kind}..."
635            );
636
637            let cfg = registry
638                .get(kind)
639                .with_context(|| format!("Module of kind {kind} not found"))?
640                .distributed_gen(&handle, module_params, params.disable_base_fees)
641                .await?;
642
643            module_cfgs.insert(module_id, cfg);
644        }
645
646        let cfg = ServerConfig::from(
647            params.clone(),
648            params.identity,
649            broadcast_public_keys,
650            broadcast_sk,
651            module_cfgs,
652            code_version_str,
653        );
654
655        let checksum = cfg.consensus.consensus_hash_sha256();
656
657        info!(
658            target: LOG_NET_PEER_DKG,
659            "Comparing consensus config checksum {checksum}..."
660        );
661
662        connections
663            .send(Recipient::Everyone, P2PMessage::Checksum(checksum))
664            .await;
665
666        for peer in params
667            .peer_ids()
668            .into_iter()
669            .filter(|p| *p != params.identity)
670        {
671            let peer_message = connections
672                .receive_from_peer(peer)
673                .await
674                .context("Unexpected shutdown of p2p connections")?;
675
676            if peer_message != P2PMessage::Checksum(checksum) {
677                warn!(
678                    target: LOG_NET_PEER_DKG,
679                    expected = ?P2PMessage::Checksum(checksum),
680                    received = ?peer_message,
681                    config = ?cfg.consensus,
682                    "Peer {peer} has sent invalid consensus config checksum message"
683                );
684
685                bail!("Peer {peer} has sent invalid consensus config checksum message");
686            }
687
688            info!(
689                target: LOG_NET_PEER_DKG,
690                "Peer {peer} has sent valid consensus config checksum message"
691            );
692        }
693
694        info!(
695            target: LOG_NET_PEER_DKG,
696            "Config generation has completed successfully!"
697        );
698
699        Ok(cfg)
700    }
701}
702
703impl ServerConfig {
704    pub fn tls_config(&self) -> TlsConfig {
705        TlsConfig {
706            private_key: Arc::new(
707                rustls::pki_types::PrivateKeyDer::try_from(
708                    Vec::from_hex(self.private.tls_key.clone().unwrap()).unwrap(),
709                )
710                .expect("Failed to parse private key"),
711            ),
712            certificates: self
713                .consensus
714                .tls_certs
715                .iter()
716                .map(|(peer, cert)| {
717                    (
718                        *peer,
719                        rustls::pki_types::CertificateDer::from(Vec::from_hex(cert).unwrap()),
720                    )
721                })
722                .collect(),
723            peer_names: self
724                .local
725                .p2p_endpoints
726                .iter()
727                .map(|(id, endpoint)| (*id, endpoint.name.to_string()))
728                .collect(),
729        }
730    }
731}
732
733impl ConfigGenParams {
734    pub fn peer_ids(&self) -> Vec<PeerId> {
735        self.peers.keys().copied().collect()
736    }
737
738    pub fn tls_config(&self) -> TlsConfig {
739        TlsConfig {
740            private_key: self.tls_key.clone().unwrap(),
741            certificates: self
742                .tls_certs()
743                .iter()
744                .map(|(peer, cert)| {
745                    (
746                        *peer,
747                        rustls::pki_types::CertificateDer::from(Vec::from_hex(cert).unwrap()),
748                    )
749                })
750                .collect(),
751            peer_names: self
752                .p2p_urls()
753                .into_iter()
754                .map(|(id, peer)| (id, peer.name))
755                .collect(),
756        }
757    }
758
759    pub fn tls_certs(&self) -> BTreeMap<PeerId, String> {
760        self.peers
761            .iter()
762            .filter_map(|(id, peer)| {
763                match peer.endpoints.clone() {
764                    PeerEndpoints::Tcp { cert, .. } => Some(cert.encode_hex()),
765                    PeerEndpoints::Iroh { .. } => None,
766                }
767                .map(|peer| (*id, peer))
768            })
769            .collect()
770    }
771
772    pub fn p2p_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
773        self.peers
774            .iter()
775            .filter_map(|(id, peer)| {
776                match peer.endpoints.clone() {
777                    PeerEndpoints::Tcp { p2p_url, .. } => Some(PeerUrl {
778                        name: peer.name.clone(),
779                        url: p2p_url.clone(),
780                    }),
781                    PeerEndpoints::Iroh { .. } => None,
782                }
783                .map(|peer| (*id, peer))
784            })
785            .collect()
786    }
787
788    pub fn api_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
789        self.peers
790            .iter()
791            .filter_map(|(id, peer)| {
792                match peer.endpoints.clone() {
793                    PeerEndpoints::Tcp { api_url, .. } => Some(PeerUrl {
794                        name: peer.name.clone(),
795                        url: api_url.clone(),
796                    }),
797                    PeerEndpoints::Iroh { .. } => None,
798                }
799                .map(|peer| (*id, peer))
800            })
801            .collect()
802    }
803
804    pub fn iroh_endpoints(&self) -> BTreeMap<PeerId, PeerIrohEndpoints> {
805        self.peers
806            .iter()
807            .filter_map(|(id, peer)| {
808                match peer.endpoints.clone() {
809                    PeerEndpoints::Tcp { .. } => None,
810                    PeerEndpoints::Iroh { api_pk, p2p_pk } => Some(PeerIrohEndpoints {
811                        name: peer.name.clone(),
812                        api_pk,
813                        p2p_pk,
814                    }),
815                }
816                .map(|peer| (*id, peer))
817            })
818            .collect()
819    }
820}