1use std::collections::{BTreeMap, BTreeSet, HashMap};
2use std::env;
3use std::net::SocketAddr;
4use std::time::Duration;
5
6use anyhow::{bail, format_err};
7use fedimint_core::admin_client::ConfigGenParamsConsensus;
8pub 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};
13use fedimint_core::core::{ModuleInstanceId, ModuleKind, MODULE_INSTANCE_ID_GLOBAL};
14use fedimint_core::envs::is_running_in_test_env;
15use fedimint_core::invite_code::InviteCode;
16use fedimint_core::module::{
17 ApiAuth, ApiVersion, CoreConsensusVersion, DynServerModuleInit, MultiApiVersion, PeerHandle,
18 SupportedApiVersionsSummary, SupportedCoreApiVersions, CORE_CONSENSUS_VERSION,
19};
20use fedimint_core::net::peers::{IMuxPeerConnections, IPeerConnections, PeerConnections};
21use fedimint_core::task::{timeout, Cancelled, Elapsed, TaskGroup};
22use fedimint_core::{secp256k1, timing, PeerId};
23use fedimint_logging::{LOG_NET_PEER, LOG_NET_PEER_DKG};
24use futures::future::join_all;
25use rand::rngs::OsRng;
26use secp256k1::{PublicKey, Secp256k1, SecretKey};
27use serde::de::DeserializeOwned;
28use serde::{Deserialize, Serialize};
29use tokio_rustls::rustls;
30use tracing::{error, info};
31
32use crate::config::api::ConfigGenParamsLocal;
33use crate::config::distributedgen::{DkgRunner, PeerHandleOps};
34use crate::envs::FM_MAX_CLIENT_CONNECTIONS_ENV;
35use crate::fedimint_core::encoding::Encodable;
36use crate::fedimint_core::NumPeersExt;
37use crate::multiplexed::PeerConnectionMultiplexer;
38use crate::net::connect::{dns_sanitize, Connector, TlsConfig};
39use crate::net::peers::{DelayCalculator, NetworkConfig};
40use crate::net::peers_reliable::ReconnectPeerConnectionsReliable;
41use crate::TlsTcpConnector;
42
43pub mod api;
44pub mod distributedgen;
45pub mod io;
46
47const DEFAULT_MAX_CLIENT_CONNECTIONS: u32 = 1000;
49
50const DEFAULT_BROADCAST_ROUND_DELAY_MS: u16 = 50;
52const DEFAULT_BROADCAST_ROUNDS_PER_SESSION: u16 = 3600;
53
54fn default_broadcast_rounds_per_session() -> u16 {
55 DEFAULT_BROADCAST_ROUNDS_PER_SESSION
56}
57
58const DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS: u16 = 50;
60const DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION: u16 = 200;
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ServerConfig {
65 pub consensus: ServerConfigConsensus,
67 pub local: ServerConfigLocal,
69 pub private: ServerConfigPrivate,
72}
73
74impl ServerConfig {
75 pub fn iter_module_instances(
76 &self,
77 ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
78 self.consensus.iter_module_instances()
79 }
80
81 pub(crate) fn supported_api_versions_summary(
82 modules: &BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
83 module_inits: &ServerModuleInitRegistry,
84 ) -> SupportedApiVersionsSummary {
85 SupportedApiVersionsSummary {
86 core: Self::supported_api_versions(),
87 modules: modules
88 .iter()
89 .map(|(&id, config)| {
90 (
91 id,
92 module_inits
93 .get(&config.kind)
94 .expect("missing module kind gen")
95 .supported_api_versions(),
96 )
97 })
98 .collect(),
99 }
100 }
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct ServerConfigPrivate {
105 pub api_auth: ApiAuth,
107 #[serde(with = "serde_tls_key")]
109 pub tls_key: rustls::PrivateKey,
110 pub broadcast_secret_key: SecretKey,
112 pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, Encodable)]
117pub struct ServerConfigConsensus {
118 pub code_version: String,
120 pub version: CoreConsensusVersion,
122 pub broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
124 #[serde(default = "default_broadcast_rounds_per_session")]
126 pub broadcast_rounds_per_session: u16,
127 pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
129 #[serde(with = "serde_tls_cert_map")]
131 pub tls_certs: BTreeMap<PeerId, rustls::Certificate>,
132 pub modules: BTreeMap<ModuleInstanceId, ServerModuleConsensusConfig>,
134 pub meta: BTreeMap<String, String>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ServerConfigLocal {
142 pub p2p_endpoints: BTreeMap<PeerId, PeerUrl>,
144 pub identity: PeerId,
146 pub max_connections: u32,
148 pub broadcast_round_delay_ms: u16,
153 pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
155}
156
157#[derive(Debug, Clone)]
158pub struct ConfigGenParams {
163 pub local: ConfigGenParamsLocal,
164 pub consensus: ConfigGenParamsConsensus,
165}
166
167impl ServerConfigConsensus {
168 pub fn iter_module_instances(
169 &self,
170 ) -> impl Iterator<Item = (ModuleInstanceId, &ModuleKind)> + '_ {
171 self.modules.iter().map(|(k, v)| (*k, &v.kind))
172 }
173
174 pub fn to_client_config(
175 &self,
176 module_config_gens: &ModuleInitRegistry<DynServerModuleInit>,
177 ) -> Result<ClientConfig, anyhow::Error> {
178 let client = ClientConfig {
179 global: GlobalClientConfig {
180 api_endpoints: self.api_endpoints.clone(),
181 broadcast_public_keys: Some(self.broadcast_public_keys.clone()),
182 consensus_version: self.version,
183 meta: self.meta.clone(),
184 },
185 modules: self
186 .modules
187 .iter()
188 .map(|(k, v)| {
189 let gen = module_config_gens
190 .get(&v.kind)
191 .ok_or_else(|| format_err!("Module gen kind={} not found", v.kind))?;
192 Ok((*k, gen.get_client_config(*k, v)?))
193 })
194 .collect::<anyhow::Result<BTreeMap<_, _>>>()?,
195 };
196 Ok(client)
197 }
198}
199
200impl ServerConfig {
201 pub fn supported_api_versions() -> SupportedCoreApiVersions {
203 SupportedCoreApiVersions {
204 core_consensus: CORE_CONSENSUS_VERSION,
205 api: MultiApiVersion::try_from_iter([ApiVersion { major: 0, minor: 4 }])
206 .expect("not version conflicts"),
207 }
208 }
209 pub fn from(
212 params: ConfigGenParams,
213 identity: PeerId,
214 broadcast_public_keys: BTreeMap<PeerId, PublicKey>,
215 broadcast_secret_key: SecretKey,
216 modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>,
217 code_version_str: String,
218 ) -> Self {
219 let private = ServerConfigPrivate {
220 api_auth: params.local.api_auth.clone(),
221 tls_key: params.local.our_private_key.clone(),
222 broadcast_secret_key,
223 modules: BTreeMap::new(),
224 };
225 let local = ServerConfigLocal {
226 p2p_endpoints: params.p2p_urls(),
227 identity,
228 max_connections: DEFAULT_MAX_CLIENT_CONNECTIONS,
229 broadcast_round_delay_ms: if is_running_in_test_env() {
230 DEFAULT_TEST_BROADCAST_ROUND_DELAY_MS
231 } else {
232 DEFAULT_BROADCAST_ROUND_DELAY_MS
233 },
234 modules: BTreeMap::new(),
235 };
236 let consensus = ServerConfigConsensus {
237 code_version: code_version_str,
238 version: CORE_CONSENSUS_VERSION,
239 broadcast_public_keys,
240 broadcast_rounds_per_session: if is_running_in_test_env() {
241 DEFAULT_TEST_BROADCAST_ROUNDS_PER_SESSION
242 } else {
243 DEFAULT_BROADCAST_ROUNDS_PER_SESSION
244 },
245 api_endpoints: params.api_urls(),
246 tls_certs: params.tls_certs(),
247 modules: BTreeMap::new(),
248 meta: params.consensus.meta,
249 };
250 let mut cfg = Self {
251 consensus,
252 local,
253 private,
254 };
255 cfg.add_modules(modules);
256 cfg
257 }
258
259 pub fn get_invite_code(&self, api_secret: Option<String>) -> InviteCode {
260 InviteCode::new(
261 self.consensus.api_endpoints[&self.local.identity]
262 .url
263 .clone(),
264 self.local.identity,
265 self.calculate_federation_id(),
266 api_secret,
267 )
268 }
269
270 pub fn calculate_federation_id(&self) -> FederationId {
271 FederationId(self.consensus.api_endpoints.consensus_hash())
272 }
273
274 pub fn add_modules(&mut self, modules: BTreeMap<ModuleInstanceId, ServerModuleConfig>) {
275 for (name, config) in modules {
276 let ServerModuleConfig {
277 local,
278 private,
279 consensus,
280 } = config;
281
282 self.local.modules.insert(name, local);
283 self.private.modules.insert(name, private);
284 self.consensus.modules.insert(name, consensus);
285 }
286 }
287
288 pub fn get_module_config_typed<T: TypedServerModuleConfig>(
290 &self,
291 id: ModuleInstanceId,
292 ) -> anyhow::Result<T> {
293 let local = Self::get_module_cfg_by_instance_id(&self.local.modules, id)?;
294 let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
295 let consensus = self
296 .consensus
297 .modules
298 .get(&id)
299 .ok_or_else(|| format_err!("Module {id} not found"))?
300 .clone();
301 let module = ServerModuleConfig::from(local, private, consensus);
302
303 module.to_typed()
304 }
305 pub fn get_module_id_by_kind(
306 &self,
307 kind: impl Into<ModuleKind>,
308 ) -> anyhow::Result<ModuleInstanceId> {
309 let kind = kind.into();
310 Ok(*self
311 .consensus
312 .modules
313 .iter()
314 .find(|(_, v)| v.kind == kind)
315 .ok_or_else(|| format_err!("Module {kind} not found"))?
316 .0)
317 }
318
319 pub fn get_module_config(&self, id: ModuleInstanceId) -> anyhow::Result<ServerModuleConfig> {
321 let local = Self::get_module_cfg_by_instance_id(&self.local.modules, id)?;
322 let private = Self::get_module_cfg_by_instance_id(&self.private.modules, id)?;
323 let consensus = self
324 .consensus
325 .modules
326 .get(&id)
327 .ok_or_else(|| format_err!("Module {id} not found"))?
328 .clone();
329 Ok(ServerModuleConfig::from(local, private, consensus))
330 }
331
332 fn get_module_cfg_by_instance_id(
333 json: &BTreeMap<ModuleInstanceId, JsonWithKind>,
334 id: ModuleInstanceId,
335 ) -> anyhow::Result<JsonWithKind> {
336 Ok(json
337 .get(&id)
338 .ok_or_else(|| format_err!("Module {id} not found"))
339 .cloned()?
340 .with_fixed_empty_value())
341 }
342
343 pub fn validate_config(
344 &self,
345 identity: &PeerId,
346 module_config_gens: &ServerModuleInitRegistry,
347 ) -> anyhow::Result<()> {
348 let peers = self.local.p2p_endpoints.clone();
349 let consensus = self.consensus.clone();
350 let private = self.private.clone();
351
352 let my_public_key = private.broadcast_secret_key.public_key(&Secp256k1::new());
353
354 if Some(&my_public_key) != consensus.broadcast_public_keys.get(identity) {
355 bail!("Broadcast secret key doesn't match corresponding public key");
356 }
357 if peers.keys().max().copied().map(PeerId::to_usize) != Some(peers.len() - 1) {
358 bail!("Peer ids are not indexed from 0");
359 }
360 if peers.keys().min().copied() != Some(PeerId::from(0)) {
361 bail!("Peer ids are not indexed from 0");
362 }
363
364 for (module_id, module_kind) in &self
365 .consensus
366 .modules
367 .iter()
368 .map(|(id, config)| Ok((*id, config.kind.clone())))
369 .collect::<anyhow::Result<BTreeSet<_>>>()?
370 {
371 module_config_gens
372 .get(module_kind)
373 .ok_or_else(|| format_err!("module config gen not found {module_kind}"))?
374 .validate_config(identity, self.get_module_config(*module_id)?)?;
375 }
376
377 Ok(())
378 }
379
380 pub fn trusted_dealer_gen(
381 params: &HashMap<PeerId, ConfigGenParams>,
382 registry: &ServerModuleInitRegistry,
383 code_version_str: &str,
384 ) -> BTreeMap<PeerId, Self> {
385 let peer0 = ¶ms[&PeerId::from(0)];
386
387 let mut broadcast_pks = BTreeMap::new();
388 let mut broadcast_sks = BTreeMap::new();
389 for peer_id in peer0.peer_ids() {
390 let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
391 broadcast_pks.insert(peer_id, broadcast_pk);
392 broadcast_sks.insert(peer_id, broadcast_sk);
393 }
394
395 let modules = peer0.consensus.modules.iter_modules();
396 let module_configs: BTreeMap<_, _> = modules
397 .map(|(module_id, kind, module_params)| {
398 (
399 module_id,
400 registry
401 .get(kind)
402 .expect("Module not registered")
403 .trusted_dealer_gen(&peer0.peer_ids(), module_params),
404 )
405 })
406 .collect();
407
408 let server_config: BTreeMap<_, _> = peer0
409 .peer_ids()
410 .iter()
411 .map(|&id| {
412 let config = ServerConfig::from(
413 params[&id].clone(),
414 id,
415 broadcast_pks.clone(),
416 *broadcast_sks.get(&id).expect("We created this entry"),
417 module_configs
418 .iter()
419 .map(|(module_id, cfgs)| (*module_id, cfgs[&id].clone()))
420 .collect(),
421 code_version_str.to_string(),
422 );
423 (id, config)
424 })
425 .collect();
426
427 server_config
428 }
429
430 pub async fn distributed_gen(
432 p2p_bind_addr: SocketAddr,
433 params: &ConfigGenParams,
434 registry: ServerModuleInitRegistry,
435 delay_calculator: DelayCalculator,
436 task_group: &TaskGroup,
437 code_version_str: String,
438 ) -> DkgResult<Self> {
439 let _timing = timing::TimeReporter::new("distributed-gen").info();
440 let server_conn = connect(
441 params.p2p_network(p2p_bind_addr),
442 params.tls_config(),
443 delay_calculator,
444 task_group,
445 )
446 .await;
447 let connections = PeerConnectionMultiplexer::new(server_conn).into_dyn();
448
449 let peers = ¶ms.peer_ids();
450 let our_id = ¶ms.local.our_id;
451
452 let broadcast_keys_exchange = PeerHandle::new(
453 &connections,
454 MODULE_INSTANCE_ID_GLOBAL,
455 *our_id,
456 peers.clone(),
457 );
458
459 let (broadcast_sk, broadcast_pk) = secp256k1::generate_keypair(&mut OsRng);
460
461 let broadcast_public_keys = broadcast_keys_exchange
462 .exchange_pubkeys("broadcast".to_string(), broadcast_pk)
463 .await?;
464
465 if peers.len() == 1 {
467 let server = Self::trusted_dealer_gen(
468 &HashMap::from([(*our_id, params.clone())]),
469 ®istry,
470 &code_version_str,
471 );
472 return Ok(server[our_id].clone());
473 }
474 info!(
475 target: LOG_NET_PEER_DKG,
476 "Peer {} running distributed key generation...", our_id
477 );
478
479 let mut dkg = DkgRunner::new(
481 KeyType::Bft,
482 peers.to_num_peers().one_honest(),
483 our_id,
484 peers,
485 );
486 dkg.add(KeyType::Auth, peers.to_num_peers().threshold());
487 dkg.add(KeyType::Epoch, peers.to_num_peers().threshold());
488
489 let mut registered_modules = registry.kinds();
490 let mut module_cfgs: BTreeMap<ModuleInstanceId, ServerModuleConfig> = BTreeMap::new();
491 let modules = params.consensus.modules.iter_modules();
492 let modules_runner = modules.map(|(module_instance_id, kind, module_params)| {
493 let dkg = PeerHandle::new(&connections, module_instance_id, *our_id, peers.clone());
494 let registry = registry.clone();
495
496 async move {
497 let result = match registry.get(kind) {
498 None => Err(DkgError::ModuleNotFound(kind.clone())),
499 Some(gen) => gen.distributed_gen(&dkg, module_params).await,
500 };
501 (module_instance_id, result)
502 }
503 });
504 for (module_instance_id, config) in join_all(modules_runner).await {
505 let config = config?;
506 registered_modules.remove(&config.consensus.kind);
507 module_cfgs.insert(module_instance_id, config);
508 }
509 if !registered_modules.is_empty() {
510 return Err(DkgError::ParamsNotFound(registered_modules));
511 }
512
513 info!(
514 target: LOG_NET_PEER_DKG,
515 "Sending confirmations to other peers."
516 );
517 let dkg_done = "DKG DONE".to_string();
521 connections
522 .send(
523 peers,
524 (MODULE_INSTANCE_ID_GLOBAL, dkg_done.clone()),
525 DkgPeerMsg::Done,
526 )
527 .await?;
528
529 info!(
530 target: LOG_NET_PEER_DKG,
531 "Waiting for confirmations from other peers."
532 );
533 if let Err(Elapsed) = timeout(Duration::from_secs(30), async {
534 let mut done_peers = BTreeSet::from([*our_id]);
535
536 while done_peers.len() < peers.len() {
537 match connections.receive((MODULE_INSTANCE_ID_GLOBAL, dkg_done.clone())).await {
538 Ok((peer_id, DkgPeerMsg::Done)) => {
539 info!(
540 target: LOG_NET_PEER_DKG,
541 peer_id = %peer_id,
542 "Got completion confirmation"
543 );
544 done_peers.insert(peer_id);
545 },
546 Ok((peer_id, msg)) => {
547 error!(target: LOG_NET_PEER_DKG, %peer_id, ?msg, "Received incorrect message after dkg was supposed to be finished. Probably dkg multiplexing bug.");
548 },
549 Err(Cancelled) => {},
550 }
551 }
552 })
553 .await
554 {
555 error!(target: LOG_NET_PEER_DKG, "Timeout waiting for dkg completion confirmation from other peers");
556 };
557
558 let server = ServerConfig::from(
559 params.clone(),
560 *our_id,
561 broadcast_public_keys,
562 broadcast_sk,
563 module_cfgs,
564 code_version_str,
565 );
566
567 info!(
568 target: LOG_NET_PEER,
569 "Distributed key generation has completed successfully!"
570 );
571
572 Ok(server)
573 }
574}
575
576#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
578pub enum KeyType {
579 Bft,
580 Epoch,
581 Auth,
582}
583
584impl ServerConfig {
585 pub fn network_config(&self, p2p_bind_addr: SocketAddr) -> NetworkConfig {
586 NetworkConfig {
587 identity: self.local.identity,
588 peers: self
589 .local
590 .p2p_endpoints
591 .iter()
592 .map(|(&id, endpoint)| (id, endpoint.url.clone()))
593 .collect(),
594 p2p_bind_addr,
595 }
596 }
597
598 pub fn tls_config(&self) -> TlsConfig {
599 TlsConfig {
600 our_private_key: self.private.tls_key.clone(),
601 peer_certs: self.consensus.tls_certs.clone(),
602 peer_names: self
603 .local
604 .p2p_endpoints
605 .iter()
606 .map(|(id, endpoint)| (*id, endpoint.name.to_string()))
607 .collect(),
608 }
609 }
610
611 pub fn get_incoming_count(&self) -> u16 {
612 self.local.identity.into()
613 }
614}
615
616impl ConfigGenParams {
617 pub fn peer_ids(&self) -> Vec<PeerId> {
618 self.consensus.peers.keys().copied().collect()
619 }
620
621 pub fn p2p_network(&self, p2p_bind_addr: SocketAddr) -> NetworkConfig {
622 NetworkConfig {
623 identity: self.local.our_id,
624 p2p_bind_addr,
625 peers: self
626 .p2p_urls()
627 .into_iter()
628 .map(|(id, peer)| (id, peer.url))
629 .collect(),
630 }
631 }
632
633 pub fn tls_config(&self) -> TlsConfig {
634 TlsConfig {
635 our_private_key: self.local.our_private_key.clone(),
636 peer_certs: self.tls_certs(),
637 peer_names: self
638 .p2p_urls()
639 .into_iter()
640 .map(|(id, peer)| (id, peer.name))
641 .collect(),
642 }
643 }
644
645 pub fn tls_certs(&self) -> BTreeMap<PeerId, rustls::Certificate> {
646 self.consensus
647 .peers
648 .iter()
649 .map(|(id, peer)| (*id, peer.cert.clone()))
650 .collect::<BTreeMap<_, _>>()
651 }
652
653 pub fn p2p_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
654 self.consensus
655 .peers
656 .iter()
657 .map(|(id, peer)| {
658 (
659 *id,
660 PeerUrl {
661 name: peer.name.clone(),
662 url: peer.p2p_url.clone(),
663 },
664 )
665 })
666 .collect::<BTreeMap<_, _>>()
667 }
668
669 pub fn api_urls(&self) -> BTreeMap<PeerId, PeerUrl> {
670 self.consensus
671 .peers
672 .iter()
673 .map(|(id, peer)| {
674 (
675 *id,
676 PeerUrl {
677 name: peer.name.clone(),
678 url: peer.api_url.clone(),
679 },
680 )
681 })
682 .collect::<BTreeMap<_, _>>()
683 }
684}
685
686pub fn max_connections() -> u32 {
688 env::var(FM_MAX_CLIENT_CONNECTIONS_ENV)
689 .ok()
690 .and_then(|s| s.parse().ok())
691 .unwrap_or(DEFAULT_MAX_CLIENT_CONNECTIONS)
692}
693
694pub async fn connect<T>(
695 network: NetworkConfig,
696 certs: TlsConfig,
697 delay_calculator: DelayCalculator,
698 task_group: &TaskGroup,
699) -> PeerConnections<T>
700where
701 T: std::fmt::Debug + Clone + Serialize + DeserializeOwned + Unpin + Send + Sync + 'static,
702{
703 let connector = TlsTcpConnector::new(certs, network.identity).into_dyn();
704 let (connections, _) =
705 ReconnectPeerConnectionsReliable::new(network, delay_calculator, connector, task_group)
706 .await;
707 connections.into_dyn()
708}
709
710pub fn gen_cert_and_key(
711 name: &str,
712) -> Result<(rustls::Certificate, rustls::PrivateKey), anyhow::Error> {
713 let keypair = rcgen::KeyPair::generate()?;
714 let keypair_ser = keypair.serialize_der();
715 let mut params = rcgen::CertificateParams::new(vec![dns_sanitize(name)])?;
716
717 params.is_ca = rcgen::IsCa::NoCa;
718 params
719 .distinguished_name
720 .push(rcgen::DnType::CommonName, dns_sanitize(name));
721
722 let cert = params.self_signed(&keypair)?;
723
724 Ok((
725 rustls::Certificate(cert.der().to_vec()),
726 rustls::PrivateKey(keypair_ser),
727 ))
728}
729
730mod serde_tls_cert_map {
731 use std::borrow::Cow;
732 use std::collections::BTreeMap;
733
734 use fedimint_core::PeerId;
735 use hex::{FromHex, ToHex};
736 use serde::de::Error;
737 use serde::ser::SerializeMap;
738 use serde::{Deserialize, Deserializer, Serializer};
739 use tokio_rustls::rustls;
740
741 pub fn serialize<S>(
742 certs: &BTreeMap<PeerId, rustls::Certificate>,
743 serializer: S,
744 ) -> Result<S::Ok, S::Error>
745 where
746 S: Serializer,
747 {
748 let mut serializer = serializer.serialize_map(Some(certs.len()))?;
749 for (key, value) in certs {
750 serializer.serialize_key(key)?;
751 let hex_str = value.0.encode_hex::<String>();
752 serializer.serialize_value(&hex_str)?;
753 }
754 serializer.end()
755 }
756
757 pub fn deserialize<'de, D>(
758 deserializer: D,
759 ) -> Result<BTreeMap<PeerId, rustls::Certificate>, D::Error>
760 where
761 D: Deserializer<'de>,
762 {
763 let map: BTreeMap<PeerId, Cow<str>> = Deserialize::deserialize(deserializer)?;
764 let mut certs = BTreeMap::new();
765
766 for (key, value) in map {
767 let cert =
768 rustls::Certificate(Vec::from_hex(value.as_ref()).map_err(D::Error::custom)?);
769 certs.insert(key, cert);
770 }
771 Ok(certs)
772 }
773}
774
775mod serde_tls_key {
776 use std::borrow::Cow;
777
778 use hex::{FromHex, ToHex};
779 use serde::{Deserialize, Deserializer, Serialize, Serializer};
780 use tokio_rustls::rustls;
781
782 pub fn serialize<S>(key: &rustls::PrivateKey, serializer: S) -> Result<S::Ok, S::Error>
783 where
784 S: Serializer,
785 {
786 let hex_str = key.0.encode_hex::<String>();
787 Serialize::serialize(&hex_str, serializer)
788 }
789
790 pub fn deserialize<'de, D>(deserializer: D) -> Result<rustls::PrivateKey, D::Error>
791 where
792 D: Deserializer<'de>,
793 {
794 let hex_str: Cow<str> = Deserialize::deserialize(deserializer)?;
795 let bytes = Vec::from_hex(hex_str.as_ref()).map_err(serde::de::Error::custom)?;
796 Ok(rustls::PrivateKey(bytes))
797 }
798}