Skip to main content

calimero_network_primitives/
config.rs

1use core::fmt::{self, Formatter};
2use core::time::Duration;
3
4use libp2p::identity::Keypair;
5use libp2p::rendezvous::Namespace;
6use multiaddr::{Multiaddr, Protocol};
7use serde::de::{Error as SerdeError, SeqAccess, Visitor};
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10pub const DEFAULT_PORT: u16 = 2428; // CHAT in T9
11
12// https://github.com/ipfs/kubo/blob/efdef7fdcfeeb30e2f1ce3dbf65b6460b58afaaf/config/bootstrap_peers.go#L17-L24
13pub const IPFS_BOOT_NODES: &[&str] = &[
14    "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
15    "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
16    "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
17    "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
18    "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
19    "/ip4/104.131.131.82/udp/4001/quic-v1/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
20];
21
22pub const CALIMERO_DEV_BOOT_NODES: &[&str] = &[
23    "/ip4/63.181.86.34/udp/4001/quic-v1/p2p/12D3KooWR5V4zmisVtVdGE6i8jfFwtgRNq5t8eDGxfckKuhXu7Eh",
24    "/ip4/63.181.86.34/tcp/4001/p2p/12D3KooWR5V4zmisVtVdGE6i8jfFwtgRNq5t8eDGxfckKuhXu7Eh",
25];
26
27#[derive(Debug)]
28#[non_exhaustive]
29pub struct NetworkConfig {
30    pub identity: Keypair,
31    pub swarm: SwarmConfig,
32    pub bootstrap: BootstrapConfig,
33    pub discovery: DiscoveryConfig,
34}
35
36impl NetworkConfig {
37    #[must_use]
38    pub const fn new(
39        identity: Keypair,
40        swarm: SwarmConfig,
41        bootstrap: BootstrapConfig,
42        discovery: DiscoveryConfig,
43    ) -> Self {
44        Self {
45            identity,
46            swarm,
47            bootstrap,
48            discovery,
49        }
50    }
51}
52
53#[derive(Debug, Deserialize, Serialize)]
54#[non_exhaustive]
55pub struct SwarmConfig {
56    pub listen: Vec<Multiaddr>,
57}
58
59impl SwarmConfig {
60    #[must_use]
61    pub const fn new(listen: Vec<Multiaddr>) -> Self {
62        Self { listen }
63    }
64}
65
66#[derive(Debug, Default, Deserialize, Serialize)]
67#[non_exhaustive]
68pub struct BootstrapConfig {
69    #[serde(default)]
70    pub nodes: BootstrapNodes,
71}
72
73impl BootstrapConfig {
74    #[must_use]
75    pub const fn new(nodes: BootstrapNodes) -> Self {
76        Self { nodes }
77    }
78}
79
80#[derive(Debug, Default, Deserialize, Serialize)]
81#[serde(transparent)]
82#[non_exhaustive]
83pub struct BootstrapNodes {
84    #[serde(deserialize_with = "deserialize_bootstrap")]
85    pub list: Vec<Multiaddr>,
86}
87
88impl BootstrapNodes {
89    #[must_use]
90    pub const fn new(list: Vec<Multiaddr>) -> Self {
91        Self { list }
92    }
93
94    #[must_use]
95    pub fn ipfs() -> Self {
96        Self {
97            list: IPFS_BOOT_NODES
98                .iter()
99                .map(|s| s.parse().expect("invalid multiaddr"))
100                .collect(),
101        }
102    }
103
104    #[must_use]
105    pub fn calimero_dev() -> Self {
106        Self {
107            list: CALIMERO_DEV_BOOT_NODES
108                .iter()
109                .map(|s| s.parse().expect("invalid multiaddr"))
110                .collect(),
111        }
112    }
113}
114
115#[derive(Debug, Deserialize, Serialize)]
116#[non_exhaustive]
117pub struct DiscoveryConfig {
118    #[serde(default = "calimero_primitives::common::bool_true")]
119    pub mdns: bool,
120
121    pub advertise_address: bool,
122
123    pub rendezvous: RendezvousConfig,
124
125    pub relay: RelayConfig,
126
127    pub autonat: AutonatConfig,
128}
129
130impl DiscoveryConfig {
131    #[must_use]
132    pub const fn new(
133        mdns: bool,
134        advertise_address: bool,
135        rendezvous: RendezvousConfig,
136        relay: RelayConfig,
137        autonat: AutonatConfig,
138    ) -> Self {
139        Self {
140            mdns,
141            advertise_address,
142            rendezvous,
143            relay,
144            autonat,
145        }
146    }
147}
148
149impl Default for DiscoveryConfig {
150    fn default() -> Self {
151        Self {
152            mdns: true,
153            advertise_address: false,
154            rendezvous: RendezvousConfig::default(),
155            relay: RelayConfig::default(),
156            autonat: AutonatConfig::default(),
157        }
158    }
159}
160
161#[derive(Clone, Debug, Deserialize, Serialize)]
162#[non_exhaustive]
163pub struct RelayConfig {
164    pub registrations_limit: usize,
165}
166
167impl RelayConfig {
168    #[must_use]
169    pub const fn new(registrations_limit: usize) -> Self {
170        Self {
171            registrations_limit,
172        }
173    }
174}
175
176impl Default for RelayConfig {
177    fn default() -> Self {
178        Self {
179            registrations_limit: 3,
180        }
181    }
182}
183
184#[derive(Clone, Debug, Deserialize, Serialize)]
185#[non_exhaustive]
186pub struct AutonatConfig {
187    pub max_candidates: usize,
188    pub probe_interval: Duration,
189}
190
191impl AutonatConfig {
192    #[must_use]
193    pub const fn new(max_candidates: usize, probe_interval: Duration) -> Self {
194        Self {
195            max_candidates,
196            probe_interval,
197        }
198    }
199}
200
201impl Default for AutonatConfig {
202    fn default() -> Self {
203        Self {
204            max_candidates: 5,
205            probe_interval: Duration::from_secs(10),
206        }
207    }
208}
209
210#[derive(Clone, Debug, Deserialize, Serialize)]
211#[non_exhaustive]
212pub struct RendezvousConfig {
213    #[serde(
214        serialize_with = "serialize_rendezvous_namespace",
215        deserialize_with = "deserialize_rendezvous_namespace"
216    )]
217    pub namespace: Namespace,
218
219    pub discovery_rpm: f32,
220
221    pub discovery_interval: Duration,
222
223    pub registrations_limit: usize,
224}
225
226impl RendezvousConfig {
227    #[must_use]
228    pub fn new(registrations_limit: usize) -> Self {
229        let default = Self::default();
230        Self {
231            namespace: default.namespace,
232            discovery_rpm: default.discovery_rpm,
233            discovery_interval: default.discovery_interval,
234            registrations_limit,
235        }
236    }
237}
238
239impl Default for RendezvousConfig {
240    fn default() -> Self {
241        Self {
242            namespace: Namespace::from_static("/calimero/devnet/global"),
243            discovery_rpm: 0.5,
244            discovery_interval: Duration::from_secs(90),
245            registrations_limit: 3,
246        }
247    }
248}
249
250fn serialize_rendezvous_namespace<S>(
251    namespace: &Namespace,
252    serializer: S,
253) -> Result<S::Ok, S::Error>
254where
255    S: Serializer,
256{
257    let namespace_str = namespace.to_string();
258    serializer.serialize_str(&namespace_str)
259}
260
261fn deserialize_rendezvous_namespace<'de, D>(deserializer: D) -> Result<Namespace, D::Error>
262where
263    D: Deserializer<'de>,
264{
265    let namespace_str = String::deserialize(deserializer)?;
266    Namespace::new(namespace_str).map_err(SerdeError::custom)
267}
268
269fn deserialize_bootstrap<'de, D>(deserializer: D) -> Result<Vec<Multiaddr>, D::Error>
270where
271    D: Deserializer<'de>,
272{
273    struct BootstrapVisitor;
274
275    impl<'de> Visitor<'de> for BootstrapVisitor {
276        type Value = Vec<Multiaddr>;
277
278        fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
279            formatter.write_str("a list of multiaddresses")
280        }
281
282        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
283        where
284            A: SeqAccess<'de>,
285        {
286            let mut addrs = Vec::new();
287
288            while let Some(addr) = seq.next_element::<Multiaddr>()? {
289                let Some(Protocol::P2p(_)) = addr.iter().last() else {
290                    return Err(SerdeError::custom("peer ID not allowed"));
291                };
292
293                addrs.push(addr);
294            }
295
296            Ok(addrs)
297        }
298    }
299
300    deserializer.deserialize_seq(BootstrapVisitor)
301}