diff --git a/Cargo.lock b/Cargo.lock index 33d9578bbd..f221bbbff3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -291,7 +291,7 @@ dependencies = [ [[package]] name = "bee-network" -version = "0.2.2" +version = "0.3.0" dependencies = [ "async-trait", "bee-runtime", @@ -326,6 +326,7 @@ dependencies = [ name = "bee-protocol" version = "0.1.1" dependencies = [ + "bee-autopeering", "bee-message", "bee-network", ] diff --git a/bee-network/CHANGELOG.md b/bee-network/CHANGELOG.md index 76ca7dd860..dcfe19f07f 100644 --- a/bee-network/CHANGELOG.md +++ b/bee-network/CHANGELOG.md @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security --> +## 0.3.0 - 2021-11-xx + +- New PeerRelation variant: Discovered + ## 0.2.2 - 2021-08-26 ### Changed diff --git a/bee-network/Cargo.toml b/bee-network/Cargo.toml index 3e7f228822..8f19351a6a 100644 --- a/bee-network/Cargo.toml +++ b/bee-network/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bee-network" -version = "0.2.2" +version = "0.3.0" authors = [ "IOTA Stiftung" ] edition = "2021" description = """ diff --git a/bee-network/src/config.rs b/bee-network/src/config.rs index c4581a7f42..2036ddb3f4 100644 --- a/bee-network/src/config.rs +++ b/bee-network/src/config.rs @@ -64,6 +64,7 @@ pub struct NetworkConfig { pub(crate) bind_multiaddr: Multiaddr, pub(crate) reconnect_interval_secs: u64, pub(crate) max_unknown_peers: usize, + pub(crate) max_discovered_peers: usize, pub(crate) static_peers: HashSet, } @@ -155,6 +156,11 @@ impl NetworkConfig { self.max_unknown_peers } + /// Returns the maximum number of discovered peers that are allowed to connect. + pub fn max_discovered_peers(&self) -> usize { + self.max_discovered_peers + } + /// Returns the statically configured peers. pub fn static_peers(&self) -> &HashSet { &self.static_peers @@ -184,6 +190,7 @@ impl Default for NetworkConfig { bind_multiaddr: DEFAULT_BIND_MULTIADDR.parse().unwrap(), reconnect_interval_secs: DEFAULT_RECONNECT_INTERVAL_SECS, max_unknown_peers: DEFAULT_MAX_UNKNOWN_PEERS, + max_discovered_peers: DEFAULT_MAX_DISCOVERED_PEERS, static_peers: Default::default(), } } @@ -196,7 +203,8 @@ pub struct NetworkConfigBuilder { bind_multiaddr: Option, reconnect_interval_secs: Option, max_unknown_peers: Option, - peering: PeeringConfigBuilder, + max_discovered_peers: Option, + peering: ManualPeeringConfigBuilder, } impl NetworkConfigBuilder { @@ -280,6 +288,12 @@ impl NetworkConfigBuilder { self } + /// Specifies the maximum number of gossip connections with discovered peers. + pub fn with_max_discovered_peers(mut self, n: usize) -> Self { + self.max_discovered_peers.replace(n); + self + } + /// Builds the network config. pub fn finish(self) -> Result { Ok(NetworkConfig { @@ -290,6 +304,7 @@ impl NetworkConfigBuilder { .unwrap_or_else(|| DEFAULT_BIND_MULTIADDR.parse().unwrap()), reconnect_interval_secs: self.reconnect_interval_secs.unwrap_or(DEFAULT_RECONNECT_INTERVAL_SECS), max_unknown_peers: self.max_unknown_peers.unwrap_or(DEFAULT_MAX_UNKNOWN_PEERS), + max_discovered_peers: self.max_discovered_peers.unwrap_or(DEFAULT_MAX_DISCOVERED_PEERS), static_peers: self.peering.finish()?.peers, }) } @@ -335,13 +350,14 @@ impl InMemoryNetworkConfigBuilder { .unwrap_or_else(|| DEFAULT_BIND_MULTIADDR_MEM.parse().unwrap()), reconnect_interval_secs: DEFAULT_RECONNECT_INTERVAL_SECS, max_unknown_peers: DEFAULT_MAX_UNKNOWN_PEERS, + max_discovered_peers: DEFAULT_MAX_DISCOVERED_PEERS, static_peers: Default::default(), } } } #[derive(Clone)] -pub struct PeeringConfig { +pub struct ManualPeeringConfig { pub peers: HashSet, } @@ -365,12 +381,12 @@ impl std::hash::Hash for Peer { } #[derive(Default, Deserialize)] -pub struct PeeringConfigBuilder { +pub struct ManualPeeringConfigBuilder { pub peers: Option>, } -impl PeeringConfigBuilder { - pub fn finish(self) -> Result { +impl ManualPeeringConfigBuilder { + pub fn finish(self) -> Result { let peers = match self.peers { None => Default::default(), Some(peer_builders) => { @@ -393,7 +409,7 @@ impl PeeringConfigBuilder { } }; - Ok(PeeringConfig { peers }) + Ok(ManualPeeringConfig { peers }) } } diff --git a/bee-network/src/init.rs b/bee-network/src/init.rs index f85266bffe..ce8f3d4a6c 100644 --- a/bee-network/src/init.rs +++ b/bee-network/src/init.rs @@ -34,6 +34,7 @@ pub mod global { static RECONNECT_INTERVAL_SECS: OnceCell = OnceCell::new(); static NETWORK_ID: OnceCell = OnceCell::new(); static MAX_UNKNOWN_PEERS: OnceCell = OnceCell::new(); + static MAX_DISCOVERED_PEERS: OnceCell = OnceCell::new(); pub fn set_reconnect_interval_secs(reconnect_interval_secs: u64) { if cfg!(test) { @@ -69,6 +70,17 @@ pub mod global { pub fn max_unknown_peers() -> usize { *MAX_UNKNOWN_PEERS.get().expect("oncecell get") } + + pub fn set_max_discovered_peers(max_discovered_peers: usize) { + if cfg!(test) { + let _ = MAX_DISCOVERED_PEERS.set(max_discovered_peers); + } else { + MAX_DISCOVERED_PEERS.set(max_discovered_peers).expect("oncecell set"); + } + } + pub fn max_discovered_peers() -> usize { + *MAX_DISCOVERED_PEERS.get().expect("oncecell get") + } } /// Initializes a "standalone" version of the network layer. @@ -150,12 +162,14 @@ fn init( bind_multiaddr, reconnect_interval_secs, max_unknown_peers, + max_discovered_peers, static_peers: peers, } = config; global::set_reconnect_interval_secs(reconnect_interval_secs); global::set_network_id(network_id); global::set_max_unknown_peers(max_unknown_peers); + global::set_max_discovered_peers(max_discovered_peers); let (command_sender, command_receiver) = command_channel(); let (internal_command_sender, internal_command_receiver) = command_channel(); diff --git a/bee-network/src/peer/error.rs b/bee-network/src/peer/error.rs index 5c08ecd4c7..e0a98c734d 100644 --- a/bee-network/src/peer/error.rs +++ b/bee-network/src/peer/error.rs @@ -55,4 +55,8 @@ pub enum Error { /// A failure due to hitting the maximum number of allowed unknown peers. #[error("Tried to add more unknown peers than defined in the config ({0}).")] ExceedsUnknownPeerLimit(usize), + + /// A failure due to hitting the maximum number of allowed unknown peers. + #[error("Tried to add more discovered peers than defined in the config ({0}).")] + ExceedsDiscoveredPeerLimit(usize), } diff --git a/bee-network/src/peer/info.rs b/bee-network/src/peer/info.rs index 2fe792b896..f968b974af 100644 --- a/bee-network/src/peer/info.rs +++ b/bee-network/src/peer/info.rs @@ -17,10 +17,18 @@ pub struct PeerInfo { /// Describes the relation with a peer. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum PeerRelation { - /// Represents a persistent peer. If the connection to such a peer drops, the network will try to reconnect. + /// Represents a known peer. + /// + /// If the connection to such a peer drops, the network will try to reconnect. Known, - /// Represents an ephemeral peer. If the connection to such a peer drops, the network won't try to reconnect. + /// Represents an unknown peer. + /// + /// If the connection to such a peer drops, the network won't try to reconnect. Unknown, + /// Represents a discovered peer. + /// + /// If the connection to such a peer drops, the network won't try to reconnect. + Discovered, } impl PeerRelation { @@ -34,15 +42,25 @@ impl PeerRelation { matches!(self, Self::Unknown) } - /// Sets the relation to `PeerRelation::Known`. + /// Returns whether the peer is discovered. + pub fn is_discovered(&self) -> bool { + matches!(self, Self::Discovered) + } + + /// Sets the relation to "known". pub fn set_known(&mut self) { *self = Self::Known; } - /// Sets the relation to `PeerRelation::Unknown`. + /// Sets the relation to "unknown". pub fn set_unknown(&mut self) { *self = Self::Unknown; } + + /// Sets the relation to "discovered". + pub fn set_discovered(&mut self) { + *self = Self::Discovered; + } } #[cfg(test)] @@ -59,5 +77,8 @@ mod tests { pr.set_unknown(); assert!(pr.is_unknown()); + + pr.set_discovered(); + assert!(pr.is_discovered()) } } diff --git a/bee-network/src/peer/list.rs b/bee-network/src/peer/list.rs index 0d67ac4066..2492a93811 100644 --- a/bee-network/src/peer/list.rs +++ b/bee-network/src/peer/list.rs @@ -178,7 +178,11 @@ impl PeerList { self.peers.iter().fold( 0, |count, (_, (info, state))| { - if predicate(info, state) { count + 1 } else { count } + if predicate(info, state) { + count + 1 + } else { + count + } }, ) } @@ -277,6 +281,10 @@ impl PeerList { && self.filter_count(|info, _| info.relation.is_unknown()) >= global::max_unknown_peers() { Err(Error::ExceedsUnknownPeerLimit(global::max_unknown_peers())) + } else if !self.contains(peer_id) + && self.filter_count(|info, _| info.relation.is_discovered()) >= global::max_discovered_peers() + { + Err(Error::ExceedsDiscoveredPeerLimit(global::max_discovered_peers())) } else { // All checks passed! Accept that peer. Ok(()) @@ -314,6 +322,10 @@ impl PeerList { && self.filter_count(|info, _| info.relation.is_unknown()) >= global::max_unknown_peers() { Err(Error::ExceedsUnknownPeerLimit(global::max_unknown_peers())) + } else if peer_info.relation.is_discovered() + && self.filter_count(|info, _| info.relation.is_discovered()) >= global::max_discovered_peers() + { + Err(Error::ExceedsDiscoveredPeerLimit(global::max_discovered_peers())) } else { // All checks passed! Allow dialing that peer. Ok(()) @@ -363,13 +375,12 @@ mod tests { let mut pl = PeerList::new(local_id); for i in 1..=3 { - assert!( - pl.insert_peer( + assert!(pl + .insert_peer( gen_random_peer_id(), gen_deterministic_peer_info(i, PeerRelation::Known) ) - .is_ok() - ); + .is_ok()); assert_eq!(pl.len(), i as usize); } } diff --git a/bee-protocol/Cargo.toml b/bee-protocol/Cargo.toml index 36c0de4bf7..a8298af8d3 100644 --- a/bee-protocol/Cargo.toml +++ b/bee-protocol/Cargo.toml @@ -11,5 +11,6 @@ keywords = [ "iota", "tangle", "bee", "framework", "protocol" ] homepage = "https://www.iota.org" [dependencies] +bee-autopeering = { version = "0.1.0", path = "../bee-autopeering", default-features = false } bee-message = { version = "0.1.5", path = "../bee-message", default-features = false, features = [ "serde" ] } -bee-network = { version = "0.2.2", path = "../bee-network", default-features = false } +bee-network = { version = "0.3.0", path = "../bee-network", default-features = false }