1use std::collections::{BTreeMap, BTreeSet};
2use std::net::SocketAddr;
3use std::sync::Arc;
4use std::time::{Duration, Instant};
5
6use anyhow::{Context, bail, format_err};
7use bitcoin::hashes::sha256;
8pub use fedimint_core::config::{
9 ClientConfig, FederationId, GlobalClientConfig, JsonWithKind, ModuleInitRegistry, P2PMessage,
10 PeerUrl, ServerModuleConfig, ServerModuleConsensusConfig, TypedServerModuleConfig,
11};
12use fedimint_core::core::{ModuleInstanceId, ModuleKind};
13use fedimint_core::envs::{is_env_var_set, is_running_in_test_env};
14use fedimint_core::module::registry::ModuleRegistry;
15use fedimint_core::module::{
16 ApiVersion, CORE_CONSENSUS_VERSION, CoreConsensusVersion, MultiApiVersion,
17 SupportedApiVersionsSummary, SupportedCoreApiVersions, SupportedModuleApiVersions,
18};
19use fedimint_core::net::peers::{DynP2PConnections, Recipient};
20use fedimint_core::setup_code::{PeerEndpoints, PeerSetupCode};
21use fedimint_core::task::sleep;
22use fedimint_core::util::SafeUrl;
23use fedimint_core::{NumPeersExt, PeerId, secp256k1, timing};
24use fedimint_logging::LOG_NET_PEER_DKG;
25use fedimint_server_core::config::PeerHandleOpsExt as _;
26use fedimint_server_core::{
27 ConfigGenModuleArgs, DynServerModule, DynServerModuleInit, ServerModuleInitRegistry,
28};
29use futures::future::select_all;
30use hex::{FromHex, ToHex};
31use peer_handle::PeerHandle;
32use rand::rngs::OsRng;
33use secp256k1::{PublicKey, Secp256k1, SecretKey};
34use serde::{Deserialize, Serialize};
35use tokio::select;
36use tokio_rustls::rustls;
37use tracing::{error, info, warn};
38
39use crate::fedimint_core::encoding::Encodable;
40use crate::net::p2p::P2PStatusReceivers;
41use crate::net::p2p_connector::TlsConfig;
42
43pub mod dkg;
44pub mod dkg_g1;
45pub mod dkg_g2;
46pub mod io;
47pub mod peer_handle;
48pub mod setup;
49
50pub const DEFAULT_MAX_CLIENT_CONNECTIONS: u32 = 1000;
52
53const DEFAULT_BROADCAST_ROUND_DELAY_MS: u16 = 50;
55const DEFAULT_BROADCAST_ROUNDS_PER_SESSION: u16 = 3600;
56
57fn default_broadcast_rounds_per_session() -> u16 {
58 DEFAULT_BROADCAST_ROUNDS_PER_SESSION
59}
60
61const DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS: u16 = 50;
63const DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION: u16 = 200;
64
65#[allow(clippy::unsafe_derive_deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct ServerConfig {
69 pub consensus: ServerConfigConsensus,
71 pub local: ServerConfigLocal,
73 pub private: ServerConfigPrivate,
76}
77
78impl ServerConfig {
79 pub fn iter_module_instances(
80 &self,
81 ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
82 self.consensus.iter_module_instances()
83 }
84
85 pub(crate) fn supported_api_versions_summary(
86 modules: &BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
87 module_registry: &ModuleRegistry<DynServerModule>,
88 ) -> SupportedApiVersionsSummary {
89 SupportedApiVersionsSummary {
90 core: Self::supported_api_versions(),
91 modules: modules
92 .iter()
93 .map(|(&id, config)| {
94 let module = module_registry.get_expect(id);
95 (
96 id,
97 SupportedModuleApiVersions {
98 core_consensus: CORE_CONSENSUS_VERSION,
99 module_consensus: config.version,
100 api: module.supported_api_versions(),
101 },
102 )
103 })
104 .collect(),
105 }
106 }
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ServerConfigPrivate {
111 pub tls_key: Option<String>,
113 #[serde(default)]
115 pub iroh_api_sk: Option<iroh::SecretKey>,
116 #[serde(default)]
118 pub iroh_p2p_sk: Option<iroh::SecretKey>,
119 pub broadcast_secret_key: SecretKey,
121 pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
126pub struct ServerConfigConsensus {
127 pub code_version: String,
130 pub version: CoreConsensusVersion,
132 pub broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
134 #[serde(default = "default_broadcast_rounds_per_session")]
136 pub broadcast_rounds_per_session: u16,
137 pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
139 #[serde(default)]
141 pub iroh_endpoints: BTreeMap<PeerId, PeerIrohEndpoints>,
142 pub tls_certs: BTreeMap<PeerId, String>,
144 pub modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
146 pub meta: BTreeMap<String, String>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
151pub struct PeerIrohEndpoints {
152 pub name: String,
154 pub api_pk: iroh::PublicKey,
156 pub p2p_pk: iroh::PublicKey,
158}
159
160pub fn legacy_consensus_config_hash(cfg: &ServerConfigConsensus) -> sha256::Hash {
161 #[derive(Encodable)]
162 struct LegacyServerConfigConsensusHashMap {
163 code_version: String,
164 version: CoreConsensusVersion,
165 broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
166 broadcast_rounds_per_session: u16,
167 api_endpoints: BTreeMap<PeerId, PeerUrl>,
168 tls_certs: BTreeMap<PeerId, String>,
169 modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
170 meta: BTreeMap<String, String>,
171 }
172
173 LegacyServerConfigConsensusHashMap {
174 code_version: cfg.code_version.clone(),
175 version: cfg.version,
176 broadcast_public_keys: cfg.broadcast_public_keys.clone(),
177 broadcast_rounds_per_session: cfg.broadcast_rounds_per_session,
178 api_endpoints: cfg.api_endpoints.clone(),
179 tls_certs: cfg.tls_certs.clone(),
180 modules: cfg.modules.clone(),
181 meta: cfg.meta.clone(),
182 }
183 .consensus_hash_sha256()
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct ServerConfigLocal {
190 pub p2p_endpoints: BTreeMap<PeerId, PeerUrl>,
192 pub identity: PeerId,
194 pub max_connections: u32,
196 pub broadcast_round_delay_ms: u16,
201}
202
203#[derive(Debug, Clone)]
205pub struct ConfigGenSettings {
206 pub p2p_bind: SocketAddr,
208 pub api_bind: SocketAddr,
210 pub ui_bind: SocketAddr,
212 pub p2p_url: Option<SafeUrl>,
214 pub api_url: Option<SafeUrl>,
216 pub enable_iroh: bool,
218 pub iroh_dns: Option<SafeUrl>,
220 pub iroh_relays: Vec<SafeUrl>,
222 pub network: bitcoin::Network,
224 pub available_modules: BTreeSet<ModuleKind>,
226 pub default_modules: BTreeSet<ModuleKind>,
228}
229
230#[derive(Debug, Clone)]
231pub struct ConfigGenParams {
236 pub identity: PeerId,
238 pub tls_key: Option<Arc<rustls::pki_types::PrivateKeyDer<'static>>>,
240 pub iroh_api_sk: Option<iroh::SecretKey>,
242 pub iroh_p2p_sk: Option<iroh::SecretKey>,
244 pub peers: BTreeMap<PeerId, PeerSetupCode>,
246 pub meta: BTreeMap<String, String>,
248 pub disable_base_fees: bool,
250 pub enabled_modules: BTreeSet<ModuleKind>,
252 pub network: bitcoin::Network,
254}
255
256impl ServerConfigConsensus {
257 pub fn api_endpoints(&self) -> BTreeMap<PeerId, PeerUrl> {
258 if self.iroh_endpoints.is_empty() {
259 self.api_endpoints.clone()
260 } else {
261 self.iroh_endpoints
262 .iter()
263 .map(|(peer, endpoints)| {
264 let url = PeerUrl {
265 name: endpoints.name.clone(),
266 url: SafeUrl::parse(&format!("iroh://{}", endpoints.api_pk))
267 .expect("Failed to parse iroh url"),
268 };
269
270 (*peer, url)
271 })
272 .collect()
273 }
274 }
275
276 pub fn iter_module_instances(
277 &self,
278 ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
279 self.modules.iter().map(|(k, v)| (*k, &v.kind))
280 }
281
282 pub fn to_client_config(
283 &self,
284 module_config_gens: &ModuleInitRegistry<DynServerModuleInit>,
285 ) -> Result<ClientConfig, anyhow::Error> {
286 let client = ClientConfig {
287 global: GlobalClientConfig {
288 api_endpoints: self.api_endpoints(),
289 broadcast_public_keys: Some(self.broadcast_public_keys.clone()),
290 consensus_version: self.version,
291 meta: self.meta.clone(),
292 },
293 modules: self
294 .modules
295 .iter()
296 .map(|(k, v)| {
297 let r#gen = module_config_gens
298 .get(&v.kind)
299 .ok_or_else(|| format_err!("Module gen kind={} not found", v.kind))?;
300 Ok((*k, r#gen.get_client_config(*k, v)?))
301 })
302 .collect::<anyhow::Result<BTreeMap<_, _>>>()?,
303 };
304 Ok(client)
305 }
306}
307
308impl ServerConfig {
309 pub fn supported_api_versions() -> SupportedCoreApiVersions {
311 SupportedCoreApiVersions {
312 core_consensus: CORE_CONSENSUS_VERSION,
313 api: MultiApiVersion::try_from_iter([ApiVersion { major: 0, minor: 9 }])
314 .expect("not version conflicts"),
315 }
316 }
317 pub fn from(
320 params: ConfigGenParams,
321 identity: PeerId,
322 broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
323 broadcast_secret_key: SecretKey,
324 modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>,
325 code_version: String,
326 ) -> Self {
327 let consensus = ServerConfigConsensus {
328 code_version: fedimint_core::version::release_version(&code_version).to_owned(),
329 version: CORE_CONSENSUS_VERSION,
330 broadcast_public_keys,
331 broadcast_rounds_per_session: if is_running_in_test_env() {
332 DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION
333 } else {
334 DEFAULT_BROADCAST_ROUNDS_PER_SESSION
335 },
336 api_endpoints: params.api_urls(),
337 iroh_endpoints: params.iroh_endpoints(),
338 tls_certs: params.tls_certs(),
339 modules: modules
340 .iter()
341 .map(|(peer, cfg)| (*peer, cfg.consensus.clone()))
342 .collect(),
343 meta: params.meta.clone(),
344 };
345
346 let local = ServerConfigLocal {
347 p2p_endpoints: params.p2p_urls(),
348 identity,
349 max_connections: DEFAULT_MAX_CLIENT_CONNECTIONS,
350 broadcast_round_delay_ms: if is_running_in_test_env() {
351 DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS
352 } else {
353 DEFAULT_BROADCAST_ROUND_DELAY_MS
354 },
355 };
356
357 let private = ServerConfigPrivate {
358 tls_key: params
359 .tls_key
360 .map(|key| key.secret_der().to_vec().encode_hex()),
361 iroh_api_sk: params.iroh_api_sk,
362 iroh_p2p_sk: params.iroh_p2p_sk,
363 broadcast_secret_key,
364 modules: modules
365 .iter()
366 .map(|(peer, cfg)| (*peer, cfg.private.clone()))
367 .collect(),
368 };
369
370 Self {
371 consensus,
372 local,
373 private,
374 }
375 }
376
377 pub fn calculate_federation_id(&self) -> FederationId {
378 FederationId(self.consensus.api_endpoints().consensus_hash())
379 }
380
381 pub fn get_module_config_typed<T: TypedServerModuleConfig>(
383 &self,
384 id: ModuleInstanceId,
385 ) -> anyhow::Result<T> {
386 let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
387 let consensus = self
388 .consensus
389 .modules
390 .get(&id)
391 .ok_or_else(|| format_err!("Typed module {id} not found"))?
392 .clone();
393 let module = ServerModuleConfig::from(private, consensus);
394
395 module.to_typed()
396 }
397 pub fn get_module_id_by_kind(
398 &self,
399 kind: impl Into<ModuleKind>,
400 ) -> anyhow::Result<ModuleInstanceId> {
401 let kind = kind.into();
402 Ok(*self
403 .consensus
404 .modules
405 .iter()
406 .find(|(_, v)| v.kind == kind)
407 .ok_or_else(|| format_err!("Module {kind} not found"))?
408 .0)
409 }
410
411 pub fn get_module_config(&self, id: ModuleInstanceId) -> anyhow::Result<ServerModuleConfig> {
413 let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
414 let consensus = self
415 .consensus
416 .modules
417 .get(&id)
418 .ok_or_else(|| format_err!("Module config {id} not found"))?
419 .clone();
420 Ok(ServerModuleConfig::from(private, consensus))
421 }
422
423 fn get_module_cfg_by_instance_id(
424 json: &BTreeMap<ModuleInstanceId, JsonWithKind>,
425 id: ModuleInstanceId,
426 ) -> anyhow::Result<JsonWithKind> {
427 Ok(json
428 .get(&id)
429 .ok_or_else(|| format_err!("Module cfg {id} not found"))
430 .cloned()?
431 .with_fixed_empty_value())
432 }
433
434 pub fn validate_config(
435 &self,
436 identity: &PeerId,
437 module_config_gens: &ServerModuleInitRegistry,
438 ) -> anyhow::Result<()> {
439 let endpoints = self.consensus.api_endpoints().clone();
440 let consensus = self.consensus.clone();
441 let private = self.private.clone();
442
443 let my_public_key = private.broadcast_secret_key.public_key(&Secp256k1::new());
444
445 if Some(&my_public_key) != consensus.broadcast_public_keys.get(identity) {
446 bail!("Broadcast secret key doesn't match corresponding public key");
447 }
448 if endpoints.keys().max().copied().map(PeerId::to_usize) != Some(endpoints.len() - 1) {
449 bail!("Peer ids are not indexed from 0");
450 }
451 if endpoints.keys().min().copied() != Some(PeerId::from(0)) {
452 bail!("Peer ids are not indexed from 0");
453 }
454
455 for (module_id, module_kind) in &self
456 .consensus
457 .modules
458 .iter()
459 .map(|(id, config)| Ok((*id, config.kind.clone())))
460 .collect::<anyhow::Result<BTreeSet<_>>>()?
461 {
462 module_config_gens
463 .get(module_kind)
464 .ok_or_else(|| format_err!("module config gen not found {module_kind}"))?
465 .validate_config(identity, self.get_module_config(*module_id)?)?;
466 }
467
468 Ok(())
469 }
470
471 pub fn trusted_dealer_gen(
472 params: &BTreeMap<PeerId, ConfigGenParams>,
473 registry: &ServerModuleInitRegistry,
474 code_version_str: &str,
475 ) -> BTreeMap<PeerId, Self> {
476 let peer0 = ¶ms[&PeerId::from(0)];
477
478 let mut broadcast_pks = BTreeMap::new();
479 let mut broadcast_sks = BTreeMap::new();
480 for peer_id in peer0.peer_ids() {
481 let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
482 broadcast_pks.insert(peer_id, broadcast_pk);
483 broadcast_sks.insert(peer_id, broadcast_sk);
484 }
485
486 let args = ConfigGenModuleArgs {
487 network: peer0.network,
488 disable_base_fees: peer0.disable_base_fees,
489 };
490
491 let use_legacy_order = is_env_var_set("FM_BACKWARDS_COMPATIBILITY_TEST");
493 let module_iter: Vec<_> = if use_legacy_order {
494 registry.iter_legacy_order()
495 } else {
496 registry.iter().collect()
497 };
498
499 let module_configs: BTreeMap<_, _> = module_iter
500 .into_iter()
501 .filter(|(kind, _)| peer0.enabled_modules.contains(kind))
502 .enumerate()
503 .map(|(module_id, (_kind, module_init))| {
504 (
505 module_id as ModuleInstanceId,
506 module_init.trusted_dealer_gen(&peer0.peer_ids(), &args),
507 )
508 })
509 .collect();
510
511 let server_config: BTreeMap<_, _> = peer0
512 .peer_ids()
513 .iter()
514 .map(|&id| {
515 let config = ServerConfig::from(
516 params[&id].clone(),
517 id,
518 broadcast_pks.clone(),
519 *broadcast_sks.get(&id).expect("We created this entry"),
520 module_configs
521 .iter()
522 .map(|(module_id, cfgs)| (*module_id, cfgs[&id].clone()))
523 .collect(),
524 code_version_str.to_string(),
525 );
526 (id, config)
527 })
528 .collect();
529
530 server_config
531 }
532
533 pub async fn distributed_gen(
535 params: &ConfigGenParams,
536 registry: ServerModuleInitRegistry,
537 code_version_str: String,
538 connections: DynP2PConnections<P2PMessage>,
539 mut p2p_status_receivers: P2PStatusReceivers,
540 ) -> anyhow::Result<Self> {
541 let _timing = timing::TimeReporter::new("distributed-gen").info();
542
543 if params.peer_ids().len() == 1 {
545 let server = Self::trusted_dealer_gen(
546 &BTreeMap::from([(params.identity, params.clone())]),
547 ®istry,
548 &code_version_str,
549 );
550
551 return Ok(server[¶ms.identity].clone());
552 }
553
554 info!(
555 target: LOG_NET_PEER_DKG,
556 "Waiting for all p2p connections to open..."
557 );
558
559 loop {
560 let mut pending_connection_receivers: Vec<_> = p2p_status_receivers
561 .iter_mut()
562 .filter_map(|(p, r)| {
563 r.mark_unchanged();
564 r.borrow().connected.is_none().then_some((*p, r.clone()))
565 })
566 .collect();
567
568 if pending_connection_receivers.is_empty() {
569 break;
570 }
571
572 let disconnected_peers = pending_connection_receivers
573 .iter()
574 .map(|(peer, receiver)| {
575 let last_error = receiver.borrow().last_error.clone();
576
577 (*peer, last_error)
578 })
579 .collect::<Vec<_>>();
580
581 info!(
582 target: LOG_NET_PEER_DKG,
583 pending = ?disconnected_peers,
584 "Waiting for all p2p connections to open..."
585 );
586
587 select! {
588 _ = select_all(pending_connection_receivers.iter_mut().map(|r| Box::pin(r.1.changed()))) => {}
589 () = sleep(Duration::from_secs(10)) => {}
590 }
591 }
592
593 let checksum = params.peers.consensus_hash_sha256();
594
595 info!(
596 target: LOG_NET_PEER_DKG,
597 "Comparing connection codes checksum {checksum}..."
598 );
599
600 connections.send(Recipient::Everyone, P2PMessage::Checksum(checksum));
601
602 for peer in params
603 .peer_ids()
604 .into_iter()
605 .filter(|p| *p != params.identity)
606 {
607 let peer_message = receive_from_peer_with_progress(
608 &connections,
609 peer,
610 "connection code checksum message",
611 )
612 .await?;
613
614 if peer_message != P2PMessage::Checksum(checksum) {
615 error!(
616 target: LOG_NET_PEER_DKG,
617 expected = ?P2PMessage::Checksum(checksum),
618 received = ?peer_message,
619 "Peer {peer} has sent invalid connection code checksum message"
620 );
621
622 bail!("Peer {peer} has sent invalid connection code checksum message");
623 }
624
625 info!(
626 target: LOG_NET_PEER_DKG,
627 "Peer {peer} has sent valid connection code checksum message"
628 );
629 }
630
631 info!(
632 target: LOG_NET_PEER_DKG,
633 "Running config generation..."
634 );
635
636 let handle = PeerHandle::new(
637 params.peer_ids().to_num_peers(),
638 params.identity,
639 &connections,
640 );
641
642 let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
643
644 let broadcast_public_keys = handle.exchange_encodable(broadcast_pk).await?;
645
646 let args = ConfigGenModuleArgs {
647 network: params.network,
648 disable_base_fees: params.disable_base_fees,
649 };
650
651 let use_legacy_order = is_env_var_set("FM_BACKWARDS_COMPATIBILITY_TEST");
653 let module_iter: Vec<_> = if use_legacy_order {
654 registry.iter_legacy_order()
655 } else {
656 registry.iter().collect()
657 };
658
659 let mut module_cfgs = BTreeMap::new();
660
661 for (module_id, (kind, module_init)) in module_iter
662 .into_iter()
663 .filter(|(kind, _)| params.enabled_modules.contains(kind))
664 .enumerate()
665 {
666 info!(
667 target: LOG_NET_PEER_DKG,
668 "Running config generation for module of kind {kind}..."
669 );
670
671 let cfg = module_init.distributed_gen(&handle, &args).await?;
672
673 module_cfgs.insert(module_id as ModuleInstanceId, cfg);
674 }
675
676 let cfg = ServerConfig::from(
677 params.clone(),
678 params.identity,
679 broadcast_public_keys,
680 broadcast_sk,
681 module_cfgs,
682 code_version_str,
683 );
684
685 let checksum = cfg.consensus.consensus_hash_sha256();
686
687 info!(
688 target: LOG_NET_PEER_DKG,
689 "Comparing consensus config checksum {checksum}..."
690 );
691
692 connections.send(Recipient::Everyone, P2PMessage::Checksum(checksum));
693
694 for peer in params
695 .peer_ids()
696 .into_iter()
697 .filter(|p| *p != params.identity)
698 {
699 let peer_message =
700 receive_from_peer_with_progress(&connections, peer, "consensus config checksum")
701 .await?;
702
703 if peer_message != P2PMessage::Checksum(checksum) {
704 warn!(
705 target: LOG_NET_PEER_DKG,
706 expected = ?P2PMessage::Checksum(checksum),
707 received = ?peer_message,
708 config = ?cfg.consensus,
709 "Peer {peer} has sent invalid consensus config checksum message"
710 );
711
712 bail!("Peer {peer} has sent invalid consensus config checksum message");
713 }
714
715 info!(
716 target: LOG_NET_PEER_DKG,
717 "Peer {peer} has sent valid consensus config checksum message"
718 );
719 }
720
721 info!(
722 target: LOG_NET_PEER_DKG,
723 "Config generation has completed successfully!"
724 );
725
726 Ok(cfg)
727 }
728}
729
730async fn receive_from_peer_with_progress(
731 connections: &DynP2PConnections<P2PMessage>,
732 peer: PeerId,
733 message_description: &'static str,
734) -> anyhow::Result<P2PMessage> {
735 let start = Instant::now();
736
737 loop {
738 select! {
739 peer_message = connections.receive_from_peer(peer) => {
743 return peer_message.context("Unexpected shutdown of p2p connections");
744 }
745 () = sleep(Duration::from_secs(10)) => {
746 info!(
747 target: LOG_NET_PEER_DKG,
748 %peer,
749 message = message_description,
750 elapsed_secs = start.elapsed().as_secs(),
751 "Still waiting for peer message"
752 );
753 }
754 }
755 }
756}
757
758impl ServerConfig {
759 pub fn tls_config(&self) -> TlsConfig {
760 TlsConfig {
761 private_key: Arc::new(
762 rustls::pki_types::PrivateKeyDer::try_from(
763 Vec::from_hex(self.private.tls_key.clone().unwrap()).unwrap(),
764 )
765 .expect("Failed to parse private key"),
766 ),
767 certificates: self
768 .consensus
769 .tls_certs
770 .iter()
771 .map(|(peer, cert)| {
772 (
773 *peer,
774 rustls::pki_types::CertificateDer::from(Vec::from_hex(cert).unwrap()),
775 )
776 })
777 .collect(),
778 peer_names: self
779 .local
780 .p2p_endpoints
781 .iter()
782 .map(|(id, endpoint)| (*id, endpoint.name.clone()))
783 .collect(),
784 }
785 }
786}
787
788impl ConfigGenParams {
789 pub fn peer_ids(&self) -> Vec<PeerId> {
790 self.peers.keys().copied().collect()
791 }
792
793 pub fn tls_config(&self) -> TlsConfig {
794 TlsConfig {
795 private_key: self.tls_key.clone().unwrap(),
796 certificates: self
797 .tls_certs()
798 .iter()
799 .map(|(peer, cert)| {
800 (
801 *peer,
802 rustls::pki_types::CertificateDer::from(Vec::from_hex(cert).unwrap()),
803 )
804 })
805 .collect(),
806 peer_names: self
807 .p2p_urls()
808 .into_iter()
809 .map(|(id, peer)| (id, peer.name))
810 .collect(),
811 }
812 }
813
814 pub fn tls_certs(&self) -> BTreeMap<PeerId, String> {
815 self.peers
816 .iter()
817 .filter_map(|(id, peer)| {
818 match peer.endpoints.clone() {
819 PeerEndpoints::Tcp { cert, .. } => Some(cert.encode_hex()),
820 PeerEndpoints::Iroh { .. } => None,
821 }
822 .map(|peer| (*id, peer))
823 })
824 .collect()
825 }
826
827 pub fn p2p_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
828 self.peers
829 .iter()
830 .filter_map(|(id, peer)| {
831 match peer.endpoints.clone() {
832 PeerEndpoints::Tcp { p2p_url, .. } => Some(PeerUrl {
833 name: peer.name.clone(),
834 url: p2p_url.clone(),
835 }),
836 PeerEndpoints::Iroh { .. } => None,
837 }
838 .map(|peer| (*id, peer))
839 })
840 .collect()
841 }
842
843 pub fn api_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
844 self.peers
845 .iter()
846 .filter_map(|(id, peer)| {
847 match peer.endpoints.clone() {
848 PeerEndpoints::Tcp { api_url, .. } => Some(PeerUrl {
849 name: peer.name.clone(),
850 url: api_url.clone(),
851 }),
852 PeerEndpoints::Iroh { .. } => None,
853 }
854 .map(|peer| (*id, peer))
855 })
856 .collect()
857 }
858
859 pub fn iroh_endpoints(&self) -> BTreeMap<PeerId, PeerIrohEndpoints> {
860 self.peers
861 .iter()
862 .filter_map(|(id, peer)| {
863 match peer.endpoints.clone() {
864 PeerEndpoints::Tcp { .. } => None,
865 PeerEndpoints::Iroh { api_pk, p2p_pk } => Some(PeerIrohEndpoints {
866 name: peer.name.clone(),
867 api_pk,
868 p2p_pk,
869 }),
870 }
871 .map(|peer| (*id, peer))
872 })
873 .collect()
874 }
875}