bee_network/
config.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4#![cfg(feature = "full")]
5
6use crate::alias;
7
8use libp2p::{multiaddr::Protocol, Multiaddr, PeerId};
9use serde::Deserialize;
10
11use std::{borrow::Cow, collections::HashSet};
12
13const DEFAULT_BIND_MULTIADDR: &str = "/ip4/0.0.0.0/tcp/15600";
14
15pub const DEFAULT_RECONNECT_INTERVAL_SECS: u64 = 30;
16const MIN_RECONNECT_INTERVAL_SECS: u64 = 1;
17
18pub const DEFAULT_MAX_UNKOWN_PEERS: usize = 4;
19
20/// [`NetworkConfigBuilder`] errors.
21#[derive(Debug, thiserror::Error)]
22pub enum Error {
23    /// The provided [`Multiaddr`] has too few protocols in it.
24    #[error("Multiaddr is underspecified.")]
25    MultiaddrUnderspecified,
26
27    /// The provided [`Multiaddr`] has too many protocols in it.
28    #[error("Multiaddr is overspecified.")]
29    MultiaddrOverspecified,
30
31    /// The provided [`Protocol`] is invalid.
32    #[error("Invalid Multiaddr protocol at {}.", .0)]
33    InvalidProtocol(usize),
34
35    /// The provided address is invalid.
36    #[error("Invalid address protocol.")]
37    InvalidAddressProtocol,
38
39    /// The provided port is invalid.
40    #[error("Invalid port protocol.")]
41    InvalidPortProtocol,
42
43    /// The peer was already added.
44    #[error("Static peer {} already added.", alias!(.0))]
45    DuplicateStaticPeer(PeerId),
46
47    /// The domain was unresolvable.
48    #[error("Domain name '{}' couldn't be resolved to an IP address", .0)]
49    UnresolvableDomain(String),
50
51    /// Parsing of a [`Multiaddr`] failed.
52    #[error("Parsing of '{}' to a Multiaddr failed.", 0)]
53    ParsingFailed(String),
54
55    /// The provided [`Multiaddr`] lacks the P2p [`Protocol`].
56    #[error("Invalid P2p Multiaddr. Did you forget to add '.../p2p/12D3Koo...'?")]
57    MissingP2pProtocol,
58}
59
60/// The network configuration.
61#[derive(Clone)]
62pub struct NetworkConfig {
63    pub(crate) bind_multiaddr: Multiaddr,
64    pub(crate) reconnect_interval_secs: u64,
65    pub(crate) max_unknown_peers: usize,
66    pub(crate) static_peers: HashSet<Peer>,
67}
68
69impl NetworkConfig {
70    /// Creates a new [`NetworkConfig`].
71    pub fn new() -> Self {
72        Self::default()
73    }
74
75    /// Returns a [`NetworkConfigBuilder`] to construct a [`NetworkConfig`] iteratively.
76    pub fn build() -> NetworkConfigBuilder {
77        NetworkConfigBuilder::new()
78    }
79
80    /// Returns an in-memory config builder to construct a [`NetworkConfig`] iteratively.
81    #[cfg(test)]
82    pub fn build_in_memory() -> InMemoryNetworkConfigBuilder {
83        InMemoryNetworkConfigBuilder::new()
84    }
85
86    /// Replaces the address, but keeps the port of the bind address.
87    ///
88    /// The argument `addr` must be either the `Ip4`, `Ip6`, or `Dns` variant of [`Protocol`].
89    pub fn replace_addr(&mut self, mut addr: Protocol) -> Result<(), Error> {
90        if !matches!(addr, Protocol::Ip4(_) | Protocol::Ip6(_) | Protocol::Dns(_)) {
91            return Err(Error::InvalidAddressProtocol);
92        }
93
94        if let Protocol::Dns(dns) = addr {
95            addr = resolve_dns_multiaddr(dns)?;
96        }
97
98        // Panic:
99        // The builder ensures that the following unwraps are fine.
100        let port = self.bind_multiaddr.pop().unwrap();
101
102        let _ = self.bind_multiaddr.pop().unwrap();
103
104        self.bind_multiaddr.push(addr);
105        self.bind_multiaddr.push(port);
106
107        Ok(())
108    }
109
110    /// Replaces the port of the bind address.
111    ///
112    /// The argument `port` must be the TCP variant of [`Protocol`].
113    pub fn replace_port(&mut self, port: Protocol) -> Result<(), Error> {
114        if !matches!(port, Protocol::Tcp(_)) {
115            return Err(Error::InvalidPortProtocol);
116        }
117
118        self.bind_multiaddr.pop();
119        self.bind_multiaddr.push(port);
120
121        Ok(())
122    }
123
124    /// Adds a static peer.
125    pub fn add_static_peer(
126        &mut self,
127        peer_id: PeerId,
128        multiaddr: Multiaddr,
129        alias: Option<String>,
130    ) -> Result<(), Error> {
131        if !self.static_peers.insert(Peer {
132            peer_id,
133            multiaddr,
134            alias,
135        }) {
136            return Err(Error::DuplicateStaticPeer(peer_id));
137        }
138
139        Ok(())
140    }
141
142    /// Returns the configured bind address as a [`Multiaddr`].
143    pub fn bind_multiaddr(&self) -> &Multiaddr {
144        &self.bind_multiaddr
145    }
146
147    /// Returns the number of seconds at which reconnect attempts occur.
148    pub fn reconnect_interval_secs(&self) -> u64 {
149        self.reconnect_interval_secs
150    }
151
152    /// Returns the maximum number of unknown peers that are allowed to connect.
153    pub fn max_unknown_peers(&self) -> usize {
154        self.max_unknown_peers
155    }
156
157    /// Returns the statically configured peers.
158    pub fn static_peers(&self) -> &HashSet<Peer> {
159        &self.static_peers
160    }
161}
162
163fn resolve_dns_multiaddr(dns: Cow<'_, str>) -> Result<Protocol, Error> {
164    use std::net::{IpAddr, ToSocketAddrs};
165
166    match dns
167        .to_socket_addrs()
168        .map_err(|_| Error::UnresolvableDomain(dns.to_string()))?
169        .next()
170        .ok_or_else(|| Error::UnresolvableDomain(dns.to_string()))?
171        .ip()
172    {
173        IpAddr::V4(ip4) => return Ok(Protocol::Ip4(ip4)),
174        IpAddr::V6(ip6) => return Ok(Protocol::Ip6(ip6)),
175    }
176}
177
178impl Default for NetworkConfig {
179    fn default() -> Self {
180        Self {
181            // Panic:
182            // Unwrapping is fine, because we made sure that the default is parsable.
183            bind_multiaddr: DEFAULT_BIND_MULTIADDR.parse().unwrap(),
184            reconnect_interval_secs: DEFAULT_RECONNECT_INTERVAL_SECS,
185            max_unknown_peers: DEFAULT_MAX_UNKOWN_PEERS,
186            static_peers: Default::default(),
187        }
188    }
189}
190
191/// A network configuration builder.
192#[derive(Default, Deserialize)]
193pub struct NetworkConfigBuilder {
194    #[serde(rename = "bind_address")]
195    bind_multiaddr: Option<Multiaddr>,
196    reconnect_interval_secs: Option<u64>,
197    max_unknown_peers: Option<usize>,
198    peering: PeeringConfigBuilder,
199}
200
201impl NetworkConfigBuilder {
202    /// Creates a new default builder.
203    pub fn new() -> Self {
204        Self::default()
205    }
206
207    /// Specifies the bind addresses.
208    pub fn with_bind_multiaddr(mut self, mut multiaddr: Multiaddr) -> Result<Self, Error> {
209        let mut valid = false;
210        let mut is_dns = false;
211
212        for (i, p) in multiaddr.iter().enumerate() {
213            match i {
214                0 => {
215                    if !matches!(p, Protocol::Ip4(_) | Protocol::Ip6(_) | Protocol::Dns(_)) {
216                        return Err(Error::InvalidProtocol(0));
217                    }
218
219                    if matches!(p, Protocol::Dns(_)) {
220                        is_dns = true;
221                    }
222                }
223                1 => {
224                    if !matches!(p, Protocol::Tcp(_)) {
225                        return Err(Error::InvalidProtocol(1));
226                    }
227                    valid = true;
228                }
229                _ => return Err(Error::MultiaddrOverspecified),
230            }
231        }
232        if !valid {
233            return Err(Error::MultiaddrUnderspecified);
234        }
235
236        if is_dns {
237            let port = multiaddr.pop().unwrap();
238            let port = if let Protocol::Tcp(port) = port {
239                port
240            } else {
241                unreachable!("already checked");
242            };
243            // Panic:
244            // We know at this point, that `multiaddr` is valid, so unwrapping is fine.
245            let ip = if let Protocol::Dns(dns) = multiaddr.pop().unwrap() {
246                let socket_dns = {
247                    let mut socket_addr = String::with_capacity(16);
248                    socket_addr.push_str(&dns);
249                    socket_addr.push(':');
250                    socket_addr.push_str(&port.to_string());
251                    socket_addr
252                };
253
254                resolve_dns_multiaddr(socket_dns.into())?
255            } else {
256                unreachable!("already checked");
257            };
258
259            multiaddr.push(ip);
260            multiaddr.push(Protocol::Tcp(port));
261        }
262
263        self.bind_multiaddr.replace(multiaddr);
264        Ok(self)
265    }
266
267    /// Specifies the interval (in seconds) at which known peers are automatically reconnected if possible.
268    ///
269    /// The allowed minimum value for the `secs` argument is `1`.
270    pub fn with_reconnect_interval_secs(mut self, secs: u64) -> Self {
271        let secs = secs.max(MIN_RECONNECT_INTERVAL_SECS);
272        self.reconnect_interval_secs.replace(secs);
273        self
274    }
275
276    /// Specifies the maximum number of gossip connections with unknown peers.
277    pub fn with_max_unknown_peers(mut self, n: usize) -> Self {
278        self.max_unknown_peers.replace(n);
279        self
280    }
281
282    /// Builds the network config.
283    pub fn finish(self) -> Result<NetworkConfig, Error> {
284        Ok(NetworkConfig {
285            bind_multiaddr: self
286                .bind_multiaddr
287                // Panic:
288                // We made sure that the default is parsable.
289                .unwrap_or_else(|| DEFAULT_BIND_MULTIADDR.parse().unwrap()),
290            reconnect_interval_secs: self.reconnect_interval_secs.unwrap_or(DEFAULT_RECONNECT_INTERVAL_SECS),
291            max_unknown_peers: self.max_unknown_peers.unwrap_or(DEFAULT_MAX_UNKOWN_PEERS),
292            static_peers: self.peering.finish()?.peers,
293        })
294    }
295}
296
297/// An in-memory network config builder, that becomes useful as part of integration testing.
298#[cfg(test)]
299#[derive(Default)]
300pub struct InMemoryNetworkConfigBuilder {
301    bind_multiaddr: Option<Multiaddr>,
302}
303
304#[cfg(test)]
305impl InMemoryNetworkConfigBuilder {
306    /// Creates a new default builder.
307    pub fn new() -> Self {
308        Self::default()
309    }
310
311    /// Specifies the bind addresses.
312    pub fn with_bind_multiaddr(mut self, multiaddr: Multiaddr) -> Self {
313        for (i, p) in multiaddr.iter().enumerate() {
314            match i {
315                0 => {
316                    if !matches!(p, Protocol::Memory(_)) {
317                        panic!("Invalid Multiaddr")
318                    }
319                }
320                _ => panic!("Invalid Multiaddr"),
321            }
322        }
323        self.bind_multiaddr.replace(multiaddr);
324        self
325    }
326
327    /// Builds the in-memory network config.
328    pub fn finish(self) -> NetworkConfig {
329        const DEFAULT_BIND_MULTIADDR_MEM: &str = "/memory/0";
330
331        NetworkConfig {
332            bind_multiaddr: self
333                .bind_multiaddr
334                .unwrap_or_else(|| DEFAULT_BIND_MULTIADDR_MEM.parse().unwrap()),
335            reconnect_interval_secs: DEFAULT_RECONNECT_INTERVAL_SECS,
336            max_unknown_peers: DEFAULT_MAX_UNKOWN_PEERS,
337            static_peers: Default::default(),
338        }
339    }
340}
341
342#[derive(Clone)]
343pub struct PeeringConfig {
344    pub peers: HashSet<Peer>,
345}
346
347#[derive(Clone)]
348pub struct Peer {
349    pub peer_id: PeerId,
350    pub multiaddr: Multiaddr,
351    pub alias: Option<String>,
352}
353
354impl Eq for Peer {}
355impl PartialEq for Peer {
356    fn eq(&self, other: &Self) -> bool {
357        self.peer_id.eq(&other.peer_id)
358    }
359}
360impl std::hash::Hash for Peer {
361    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
362        self.peer_id.hash(state)
363    }
364}
365
366#[derive(Default, Deserialize)]
367pub struct PeeringConfigBuilder {
368    pub peers: Option<Vec<PeerBuilder>>,
369}
370
371impl PeeringConfigBuilder {
372    pub fn finish(self) -> Result<PeeringConfig, Error> {
373        let peers = match self.peers {
374            None => Default::default(),
375            Some(peer_builders) => {
376                // NOTE: Switch back to combinators once `map_while` is stable.
377
378                let mut peers = HashSet::with_capacity(peer_builders.len());
379
380                for builder in peer_builders {
381                    let (multiaddr, peer_id) = split_multiaddr(&builder.multiaddr)?;
382                    if !peers.insert(Peer {
383                        peer_id,
384                        multiaddr,
385                        alias: builder.alias,
386                    }) {
387                        return Err(Error::DuplicateStaticPeer(peer_id));
388                    }
389                }
390
391                peers
392            }
393        };
394
395        Ok(PeeringConfig { peers })
396    }
397}
398
399fn split_multiaddr(multiaddr: &str) -> Result<(Multiaddr, PeerId), Error> {
400    let mut multiaddr: Multiaddr = multiaddr
401        .parse()
402        .map_err(|_| Error::ParsingFailed(multiaddr.to_string()))?;
403
404    if let Protocol::P2p(multihash) = multiaddr.pop().ok_or(Error::MultiaddrUnderspecified)? {
405        Ok((
406            multiaddr,
407            PeerId::from_multihash(multihash).expect("Invalid peer Multiaddr: Make sure your peer's Id is complete."),
408        ))
409    } else {
410        Err(Error::MissingP2pProtocol)
411    }
412}
413
414#[derive(Deserialize)]
415pub struct PeerBuilder {
416    #[serde(rename = "address")]
417    multiaddr: String,
418    alias: Option<String>,
419}
420
421#[cfg(test)]
422mod tests {
423    use super::*;
424
425    #[test]
426    fn create_default_network_config() {
427        let config = NetworkConfig::default();
428
429        assert_eq!(
430            config.bind_multiaddr(),
431            &DEFAULT_BIND_MULTIADDR.parse::<Multiaddr>().unwrap()
432        );
433    }
434
435    #[test]
436    #[should_panic]
437    fn create_with_builder_and_too_short_bind_address() {
438        let _config = NetworkConfig::build()
439            .with_bind_multiaddr("/ip4/127.0.0.1".parse().unwrap())
440            .unwrap()
441            .finish();
442    }
443
444    #[test]
445    #[should_panic]
446    fn create_with_builder_and_too_long_bind_address() {
447        let _config = NetworkConfig::build()
448            .with_bind_multiaddr(
449                "/ip4/127.0.0.1/p2p/12D3KooWJWEKvSFbben74C7H4YtKjhPMTDxd7gP7zxWSUEeF27st"
450                    .parse()
451                    .unwrap(),
452            )
453            .unwrap()
454            .finish();
455    }
456
457    #[test]
458    fn create_with_builder_and_valid_ip_bind_address() {
459        let _config = NetworkConfig::build()
460            .with_bind_multiaddr("/ip4/127.0.0.1/tcp/1337".parse().unwrap())
461            .unwrap()
462            .finish();
463    }
464
465    #[test]
466    fn create_with_builder_and_valid_dns_bind_address() {
467        let _config = NetworkConfig::build()
468            .with_bind_multiaddr("/dns/localhost/tcp/1337".parse().unwrap())
469            .unwrap()
470            .finish();
471    }
472
473    #[test]
474    #[should_panic]
475    fn create_with_mem_builder_and_non_mem_multiaddr() {
476        let _config = NetworkConfig::build_in_memory()
477            .with_bind_multiaddr("/ip4/127.0.0.1/tcp/1337".parse().unwrap())
478            .finish();
479    }
480
481    #[test]
482    fn create_with_mem_builder_and_valid_mem_multiaddr() {
483        let _config = NetworkConfig::build_in_memory()
484            .with_bind_multiaddr("/memory/1337".parse().unwrap())
485            .finish();
486    }
487}