From f2801fb86a4c9d3b59e52fec80bd3ff9e83b953f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 20 Mar 2023 20:44:49 +0100 Subject: [PATCH 001/105] Report changes in supported protocols back to `ConnectionHandler` --- protocols/dcutr/src/handler/direct.rs | 3 +- protocols/dcutr/src/handler/relayed.rs | 2 +- protocols/gossipsub/src/handler.rs | 4 +- protocols/identify/src/handler.rs | 4 +- protocols/kad/src/handler.rs | 4 +- protocols/perf/src/client/handler.rs | 2 +- protocols/perf/src/server/handler.rs | 2 +- protocols/ping/src/handler.rs | 4 +- protocols/relay/src/behaviour/handler.rs | 2 +- protocols/relay/src/priv_client/handler.rs | 2 +- protocols/rendezvous/src/substream_handler.rs | 3 +- protocols/request-response/src/handler.rs | 2 +- swarm/src/behaviour/toggle.rs | 3 + swarm/src/connection.rs | 158 +++++++++++++++++- swarm/src/dummy.rs | 4 +- swarm/src/handler.rs | 8 + swarm/src/handler/either.rs | 6 + swarm/src/handler/multi.rs | 5 + swarm/src/handler/one_shot.rs | 4 +- swarm/src/handler/pending.rs | 3 +- swarm/src/handler/select.rs | 6 + swarm/src/keep_alive.rs | 3 +- 22 files changed, 213 insertions(+), 21 deletions(-) diff --git a/protocols/dcutr/src/handler/direct.rs b/protocols/dcutr/src/handler/direct.rs index aab212483eb..521a351fdf9 100644 --- a/protocols/dcutr/src/handler/direct.rs +++ b/protocols/dcutr/src/handler/direct.rs @@ -92,7 +92,8 @@ impl ConnectionHandler for Handler { | ConnectionEvent::FullyNegotiatedOutbound(_) | ConnectionEvent::DialUpgradeError(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::AddressChange(_) => {} + | ConnectionEvent::AddressChange(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/dcutr/src/handler/relayed.rs b/protocols/dcutr/src/handler/relayed.rs index aefaaeec933..a0ec68f7494 100644 --- a/protocols/dcutr/src/handler/relayed.rs +++ b/protocols/dcutr/src/handler/relayed.rs @@ -419,7 +419,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/gossipsub/src/handler.rs b/protocols/gossipsub/src/handler.rs index 2c921286553..fa131feecc6 100644 --- a/protocols/gossipsub/src/handler.rs +++ b/protocols/gossipsub/src/handler.rs @@ -576,7 +576,9 @@ impl ConnectionHandler for Handler { warn!("Dial upgrade error {:?}", e); self.upgrade_errors.push_back(e); } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index c0bd9d928eb..653d9d058fc 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -344,7 +344,9 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 22c1253dd1e..86e47ac9e27 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -786,7 +786,9 @@ where ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/perf/src/client/handler.rs b/protocols/perf/src/client/handler.rs index f75a43f0a4e..93f71ea48fb 100644 --- a/protocols/perf/src/client/handler.rs +++ b/protocols/perf/src/client/handler.rs @@ -127,7 +127,7 @@ impl ConnectionHandler for Handler { .boxed(), ), - ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} ConnectionEvent::DialUpgradeError(DialUpgradeError { info: Command { id, .. }, error, diff --git a/protocols/perf/src/server/handler.rs b/protocols/perf/src/server/handler.rs index 2946b6d4a4c..357f8aa1203 100644 --- a/protocols/perf/src/server/handler.rs +++ b/protocols/perf/src/server/handler.rs @@ -104,7 +104,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(DialUpgradeError { info, .. }) => { void::unreachable(info) } - ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} ConnectionEvent::ListenUpgradeError(ListenUpgradeError { info: (), error }) => { match error { ConnectionHandlerUpgrErr::Timeout => {} diff --git a/protocols/ping/src/handler.rs b/protocols/ping/src/handler.rs index 2703b274c77..4c063f1912d 100644 --- a/protocols/ping/src/handler.rs +++ b/protocols/ping/src/handler.rs @@ -411,7 +411,9 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/relay/src/behaviour/handler.rs b/protocols/relay/src/behaviour/handler.rs index 3acbda0eff5..0608d2cae3d 100644 --- a/protocols/relay/src/behaviour/handler.rs +++ b/protocols/relay/src/behaviour/handler.rs @@ -954,7 +954,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/relay/src/priv_client/handler.rs b/protocols/relay/src/priv_client/handler.rs index 3d7bc6c4d1e..c736d219865 100644 --- a/protocols/relay/src/priv_client/handler.rs +++ b/protocols/relay/src/priv_client/handler.rs @@ -613,7 +613,7 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index 16a493ccc3a..57946e7d237 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -396,7 +396,8 @@ where // TODO: Handle upgrade errors properly ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::DialUpgradeError(_) => {} + | ConnectionEvent::DialUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } diff --git a/protocols/request-response/src/handler.rs b/protocols/request-response/src/handler.rs index 50cd6adb055..8c8290bd79a 100644 --- a/protocols/request-response/src/handler.rs +++ b/protocols/request-response/src/handler.rs @@ -425,7 +425,7 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } - ConnectionEvent::AddressChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/swarm/src/behaviour/toggle.rs b/swarm/src/behaviour/toggle.rs index bd4678a5e58..e1b5a2a0688 100644 --- a/swarm/src/behaviour/toggle.rs +++ b/swarm/src/behaviour/toggle.rs @@ -368,6 +368,9 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } + ConnectionEvent::ProtocolsChange(_) => { + todo!() + } } } } diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index e7e71fdfebc..5f9f68b054d 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -29,9 +29,9 @@ pub use error::{ use crate::handler::{ AddressChange, ConnectionEvent, ConnectionHandler, DialUpgradeError, FullyNegotiatedInbound, - FullyNegotiatedOutbound, ListenUpgradeError, + FullyNegotiatedOutbound, ListenUpgradeError, ProtocolsChange, }; -use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper}; +use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, UpgradeInfoSend}; use crate::{ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, SubstreamProtocol}; use futures::stream::FuturesUnordered; use futures::FutureExt; @@ -43,7 +43,7 @@ use libp2p_core::multiaddr::Multiaddr; use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerEvent, StreamMuxerExt, SubstreamBox}; use libp2p_core::upgrade::{InboundUpgradeApply, OutboundUpgradeApply}; use libp2p_core::Endpoint; -use libp2p_core::{upgrade, UpgradeError}; +use libp2p_core::{upgrade, ProtocolName as _, UpgradeError}; use libp2p_identity::PeerId; use std::future::Future; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -145,6 +145,8 @@ where requested_substreams: FuturesUnordered< SubstreamRequested, >, + + supported_protocols: Vec, } impl fmt::Debug for Connection @@ -182,6 +184,7 @@ where substream_upgrade_protocol_override, max_negotiating_inbound_streams, requested_substreams: Default::default(), + supported_protocols: vec![], } } @@ -211,6 +214,7 @@ where shutdown, max_negotiating_inbound_streams, substream_upgrade_protocol_override, + supported_protocols, } = self.get_mut(); loop { @@ -355,6 +359,23 @@ where Poll::Ready(substream) => { let protocol = handler.listen_protocol(); + let mut new_protocols = protocol + .upgrade() + .protocol_info() + .filter_map(|i| String::from_utf8(i.protocol_name().to_vec()).ok()) + .collect::>(); + + new_protocols.sort(); + + if supported_protocols != &new_protocols { + handler.on_connection_event(ConnectionEvent::ProtocolsChange( + ProtocolsChange { + protocols: &new_protocols, + }, + )); + *supported_protocols = new_protocols; + } + negotiating_in.push(SubstreamUpgrade::new_inbound(substream, protocol)); continue; // Go back to the top, handler can potentially make progress again. @@ -610,9 +631,10 @@ enum Shutdown { mod tests { use super::*; use crate::keep_alive; + use futures::future; use futures::AsyncRead; use futures::AsyncWrite; - use libp2p_core::upgrade::DeniedUpgrade; + use libp2p_core::upgrade::{DeniedUpgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; use libp2p_core::StreamMuxer; use quickcheck::*; use std::sync::{Arc, Weak}; @@ -673,6 +695,33 @@ mod tests { )) } + #[test] + fn propagates_changes_to_supported_inbound_protocols() { + let mut connection = Connection::new( + StreamMuxerBox::new(DummyStreamMuxer { + counter: Arc::new(()), + }), + ConfigurableProtocolConnectionHandler::default(), + None, + 2, + ); + connection.handler.active_protocols = vec!["/foo"]; + + // DummyStreamMuxer will yield a new stream + let _ = Pin::new(&mut connection) + .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + assert_eq!(connection.handler.reported_protocols, vec!["/foo"]); + + connection.handler.active_protocols = vec!["/foo", "/bar"]; + connection.negotiating_in.clear(); // Hack to request more substreams from the muxer. + + // DummyStreamMuxer will yield a new stream + let _ = Pin::new(&mut connection) + .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + + assert_eq!(connection.handler.reported_protocols, vec!["/bar", "/foo"]) + } + struct DummyStreamMuxer { counter: Arc<()>, } @@ -790,6 +839,12 @@ mod tests { } } + #[derive(Default)] + struct ConfigurableProtocolConnectionHandler { + active_protocols: Vec<&'static str>, + reported_protocols: Vec, + } + impl ConnectionHandler for MockConnectionHandler { type InEvent = Void; type OutEvent = Void; @@ -826,7 +881,9 @@ mod tests { ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { self.error = Some(error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } @@ -860,6 +917,97 @@ mod tests { Poll::Pending } } + + impl ConnectionHandler for ConfigurableProtocolConnectionHandler { + type InEvent = Void; + type OutEvent = Void; + type Error = Void; + type InboundProtocol = ManyProtocolsUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol( + &self, + ) -> SubstreamProtocol { + SubstreamProtocol::new( + ManyProtocolsUpgrade { + protocols: self.active_protocols.clone(), + }, + (), + ) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + if let ConnectionEvent::ProtocolsChange(ProtocolsChange { protocols }) = event { + self.reported_protocols = protocols + .to_vec(); + } + } + + fn on_behaviour_event(&mut self, event: Self::InEvent) { + void::unreachable(event) + } + + fn connection_keep_alive(&self) -> KeepAlive { + KeepAlive::Yes + } + + fn poll( + &mut self, + _: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::OutEvent, + Self::Error, + >, + > { + Poll::Pending + } + } + + struct ManyProtocolsUpgrade { + protocols: Vec<&'static str>, + } + + impl UpgradeInfo for ManyProtocolsUpgrade { + type Info = &'static str; + type InfoIter = std::vec::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + self.protocols.clone().into_iter() + } + } + + impl InboundUpgrade for ManyProtocolsUpgrade { + type Output = C; + type Error = Void; + type Future = future::Ready>; + + fn upgrade_inbound(self, stream: C, _: Self::Info) -> Self::Future { + future::ready(Ok(stream)) + } + } + + impl OutboundUpgrade for ManyProtocolsUpgrade { + type Output = C; + type Error = Void; + type Future = future::Ready>; + + fn upgrade_outbound(self, stream: C, _: Self::Info) -> Self::Future { + future::ready(Ok(stream)) + } + } } /// The endpoint roles associated with a pending peer-to-peer connection. diff --git a/swarm/src/dummy.rs b/swarm/src/dummy.rs index 4497540a42b..bd1666da3ac 100644 --- a/swarm/src/dummy.rs +++ b/swarm/src/dummy.rs @@ -139,7 +139,9 @@ impl crate::handler::ConnectionHandler for ConnectionHandler { unreachable!("Denied upgrade does not support any protocols") } }, - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 4093ecaee32..7ae92fa9d10 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -210,6 +210,8 @@ pub enum ConnectionEvent<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IO DialUpgradeError(DialUpgradeError), /// Informs the handler that upgrading an inbound substream to the given protocol has failed. ListenUpgradeError(ListenUpgradeError), + /// The [`ConnectionHandler`] now supports a different set of protocols. + ProtocolsChange(ProtocolsChange<'a>), } /// [`ConnectionEvent`] variant that informs the handler about @@ -239,6 +241,12 @@ pub struct AddressChange<'a> { pub new_address: &'a Multiaddr, } +/// [`ConnectionEvent`] variant that informs the handler about a change in the address of the remote. +#[derive(Clone, Copy)] +pub struct ProtocolsChange<'a> { + pub protocols: &'a [String], +} + /// [`ConnectionEvent`] variant that informs the handler /// that upgrading an outbound substream to the given protocol has failed. pub struct DialUpgradeError { diff --git a/swarm/src/handler/either.rs b/swarm/src/handler/either.rs index 92d82371163..0b8c2f7f14e 100644 --- a/swarm/src/handler/either.rs +++ b/swarm/src/handler/either.rs @@ -322,6 +322,12 @@ where handler.on_connection_event(ConnectionEvent::AddressChange(address_change)) } }, + ConnectionEvent::ProtocolsChange(supported_protocols) => match self { + Either::Left(handler) => handler + .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)), + Either::Right(handler) => handler + .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)), + }, } } } diff --git a/swarm/src/handler/multi.rs b/swarm/src/handler/multi.rs index 146a2a96895..8cd6e4c916a 100644 --- a/swarm/src/handler/multi.rs +++ b/swarm/src/handler/multi.rs @@ -318,6 +318,11 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } + ConnectionEvent::ProtocolsChange(supported_protocols) => { + for h in self.handlers.values_mut() { + h.on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)); + } + } } } diff --git a/swarm/src/handler/one_shot.rs b/swarm/src/handler/one_shot.rs index 29ba45ab678..bc46f05d4b5 100644 --- a/swarm/src/handler/one_shot.rs +++ b/swarm/src/handler/one_shot.rs @@ -217,7 +217,9 @@ where self.keep_alive = KeepAlive::No; } } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/swarm/src/handler/pending.rs b/swarm/src/handler/pending.rs index a39e498c3f2..843988c8fd3 100644 --- a/swarm/src/handler/pending.rs +++ b/swarm/src/handler/pending.rs @@ -99,7 +99,8 @@ impl ConnectionHandler for PendingConnectionHandler { } ConnectionEvent::AddressChange(_) | ConnectionEvent::DialUpgradeError(_) - | ConnectionEvent::ListenUpgradeError(_) => {} + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } diff --git a/swarm/src/handler/select.rs b/swarm/src/handler/select.rs index edb9a9154b1..c584b236d40 100644 --- a/swarm/src/handler/select.rs +++ b/swarm/src/handler/select.rs @@ -477,6 +477,12 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } + ConnectionEvent::ProtocolsChange(supported_protocols) => { + self.proto1 + .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)); + self.proto2 + .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)); + } } } } diff --git a/swarm/src/keep_alive.rs b/swarm/src/keep_alive.rs index c22a926afe4..366f2614f71 100644 --- a/swarm/src/keep_alive.rs +++ b/swarm/src/keep_alive.rs @@ -136,7 +136,8 @@ impl crate::handler::ConnectionHandler for ConnectionHandler { }) => void::unreachable(protocol), ConnectionEvent::DialUpgradeError(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::AddressChange(_) => {} + | ConnectionEvent::AddressChange(_) + | ConnectionEvent::ProtocolsChange(_) => {} } } } From 9a28cd25fe96a2e2bc00bf88b513630a3a86626c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 22 Mar 2023 22:38:34 +0100 Subject: [PATCH 002/105] Allow reporting of supported protocols by remote --- protocols/dcutr/src/handler/direct.rs | 3 +- protocols/dcutr/src/handler/relayed.rs | 4 ++- protocols/gossipsub/src/handler.rs | 3 +- protocols/identify/src/handler.rs | 3 +- protocols/kad/src/handler.rs | 21 ++++++++++++- protocols/perf/src/client/handler.rs | 3 +- protocols/perf/src/server/handler.rs | 4 ++- protocols/ping/src/handler.rs | 3 +- protocols/relay/src/behaviour/handler.rs | 4 ++- protocols/relay/src/priv_client/handler.rs | 4 ++- protocols/rendezvous/src/substream_handler.rs | 3 +- protocols/request-response/src/handler.rs | 4 ++- swarm/src/behaviour/toggle.rs | 5 ++- swarm/src/connection.rs | 18 ++++++++--- swarm/src/dummy.rs | 3 +- swarm/src/handler.rs | 31 ++++++++++++++++--- swarm/src/handler/either.rs | 20 +++++++++--- swarm/src/handler/map_out.rs | 3 ++ swarm/src/handler/multi.rs | 13 ++++++-- swarm/src/handler/one_shot.rs | 3 +- swarm/src/handler/pending.rs | 3 +- swarm/src/handler/select.rs | 26 ++++++++++++++-- swarm/src/keep_alive.rs | 3 +- 23 files changed, 151 insertions(+), 36 deletions(-) diff --git a/protocols/dcutr/src/handler/direct.rs b/protocols/dcutr/src/handler/direct.rs index 521a351fdf9..2cdb3f5a3fa 100644 --- a/protocols/dcutr/src/handler/direct.rs +++ b/protocols/dcutr/src/handler/direct.rs @@ -93,7 +93,8 @@ impl ConnectionHandler for Handler { | ConnectionEvent::DialUpgradeError(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::AddressChange(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/dcutr/src/handler/relayed.rs b/protocols/dcutr/src/handler/relayed.rs index a0ec68f7494..df84ee004de 100644 --- a/protocols/dcutr/src/handler/relayed.rs +++ b/protocols/dcutr/src/handler/relayed.rs @@ -419,7 +419,9 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/gossipsub/src/handler.rs b/protocols/gossipsub/src/handler.rs index fa131feecc6..f56e0072230 100644 --- a/protocols/gossipsub/src/handler.rs +++ b/protocols/gossipsub/src/handler.rs @@ -578,7 +578,8 @@ impl ConnectionHandler for Handler { } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 653d9d058fc..3c39652c04c 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -346,7 +346,8 @@ impl ConnectionHandler for Handler { } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) => {} + ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 86e47ac9e27..e7979271a87 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -788,7 +788,26 @@ where } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) => {} + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange { protocols }) => { + // TODO: We should cache this / it will get simpler with #2831. + let kademlia_protocols = self + .config + .protocol_config + .protocol_names() + .iter() + .filter_map(|b| String::from_utf8(b.to_vec()).ok()) + .collect::>(); + + let remote_supports_our_kademlia_protocols = + kademlia_protocols.iter().all(|p| protocols.contains(p)); + + if remote_supports_our_kademlia_protocols { + self.protocol_status = ProtocolStatus::Confirmed; + } else { + self.protocol_status = ProtocolStatus::NotSupported; + } + } } } } diff --git a/protocols/perf/src/client/handler.rs b/protocols/perf/src/client/handler.rs index 93f71ea48fb..96da11c2e24 100644 --- a/protocols/perf/src/client/handler.rs +++ b/protocols/perf/src/client/handler.rs @@ -127,7 +127,7 @@ impl ConnectionHandler for Handler { .boxed(), ), - ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::LocalProtocolsChange(_) => {} ConnectionEvent::DialUpgradeError(DialUpgradeError { info: Command { id, .. }, error, @@ -147,6 +147,7 @@ impl ConnectionHandler for Handler { }, } } + ConnectionEvent::RemoteProtocolsChange(_) => {} } } diff --git a/protocols/perf/src/server/handler.rs b/protocols/perf/src/server/handler.rs index 357f8aa1203..ffcf62ceff0 100644 --- a/protocols/perf/src/server/handler.rs +++ b/protocols/perf/src/server/handler.rs @@ -104,7 +104,9 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(DialUpgradeError { info, .. }) => { void::unreachable(info) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} ConnectionEvent::ListenUpgradeError(ListenUpgradeError { info: (), error }) => { match error { ConnectionHandlerUpgrErr::Timeout => {} diff --git a/protocols/ping/src/handler.rs b/protocols/ping/src/handler.rs index 4c063f1912d..7085bb36b68 100644 --- a/protocols/ping/src/handler.rs +++ b/protocols/ping/src/handler.rs @@ -413,7 +413,8 @@ impl ConnectionHandler for Handler { } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/relay/src/behaviour/handler.rs b/protocols/relay/src/behaviour/handler.rs index 0608d2cae3d..bc9ee109ec4 100644 --- a/protocols/relay/src/behaviour/handler.rs +++ b/protocols/relay/src/behaviour/handler.rs @@ -954,7 +954,9 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/relay/src/priv_client/handler.rs b/protocols/relay/src/priv_client/handler.rs index c736d219865..389e38733be 100644 --- a/protocols/relay/src/priv_client/handler.rs +++ b/protocols/relay/src/priv_client/handler.rs @@ -613,7 +613,9 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/rendezvous/src/substream_handler.rs b/protocols/rendezvous/src/substream_handler.rs index 57946e7d237..f954ac685b2 100644 --- a/protocols/rendezvous/src/substream_handler.rs +++ b/protocols/rendezvous/src/substream_handler.rs @@ -397,7 +397,8 @@ where ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::DialUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } diff --git a/protocols/request-response/src/handler.rs b/protocols/request-response/src/handler.rs index 8c8290bd79a..4ba4f437f05 100644 --- a/protocols/request-response/src/handler.rs +++ b/protocols/request-response/src/handler.rs @@ -425,7 +425,9 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/swarm/src/behaviour/toggle.rs b/swarm/src/behaviour/toggle.rs index e1b5a2a0688..b6533411250 100644 --- a/swarm/src/behaviour/toggle.rs +++ b/swarm/src/behaviour/toggle.rs @@ -368,7 +368,10 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } - ConnectionEvent::ProtocolsChange(_) => { + ConnectionEvent::LocalProtocolsChange(_) => { + todo!() + } + ConnectionEvent::RemoteProtocolsChange(_) => { todo!() } } diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 5f9f68b054d..089768f46ea 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -248,6 +248,14 @@ where Poll::Ready(ConnectionHandlerEvent::Close(err)) => { return Poll::Ready(Err(ConnectionError::Handler(err))); } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }) => { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( + ProtocolsChange { + protocols: &protocols, + }, + )); + continue; + } } // In case the [`ConnectionHandler`] can not make any more progress, poll the negotiating outbound streams. @@ -368,7 +376,7 @@ where new_protocols.sort(); if supported_protocols != &new_protocols { - handler.on_connection_event(ConnectionEvent::ProtocolsChange( + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( ProtocolsChange { protocols: &new_protocols, }, @@ -883,7 +891,8 @@ mod tests { } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } @@ -947,9 +956,8 @@ mod tests { Self::OutboundOpenInfo, >, ) { - if let ConnectionEvent::ProtocolsChange(ProtocolsChange { protocols }) = event { - self.reported_protocols = protocols - .to_vec(); + if let ConnectionEvent::LocalProtocolsChange(ProtocolsChange { protocols }) = event { + self.reported_protocols = protocols.to_vec(); } } diff --git a/swarm/src/dummy.rs b/swarm/src/dummy.rs index bd1666da3ac..f476a7d5013 100644 --- a/swarm/src/dummy.rs +++ b/swarm/src/dummy.rs @@ -141,7 +141,8 @@ impl crate::handler::ConnectionHandler for ConnectionHandler { }, ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 7ae92fa9d10..0dcc819a69a 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -210,8 +210,10 @@ pub enum ConnectionEvent<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IO DialUpgradeError(DialUpgradeError), /// Informs the handler that upgrading an inbound substream to the given protocol has failed. ListenUpgradeError(ListenUpgradeError), - /// The [`ConnectionHandler`] now supports a different set of protocols. - ProtocolsChange(ProtocolsChange<'a>), + /// The local [`ConnectionHandler`] now supports a different set of protocols. + LocalProtocolsChange(ProtocolsChange<'a>), + /// The remote [`ConnectionHandler`] now supports a different set of protocols. + RemoteProtocolsChange(ProtocolsChange<'a>), } /// [`ConnectionEvent`] variant that informs the handler about @@ -241,7 +243,7 @@ pub struct AddressChange<'a> { pub new_address: &'a Multiaddr, } -/// [`ConnectionEvent`] variant that informs the handler about a change in the address of the remote. +/// [`ConnectionEvent`] variant that informs the handler about a change in the protocols supported on the connection. #[derive(Clone, Copy)] pub struct ProtocolsChange<'a> { pub protocols: &'a [String], @@ -338,7 +340,7 @@ impl SubstreamProtocol { } /// Event produced by a handler. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum ConnectionHandlerEvent { /// Request a new outbound substream to be opened with the remote. OutboundSubstreamRequest { @@ -356,6 +358,15 @@ pub enum ConnectionHandlerEvent }, + /// Other event. Custom(TCustom), } @@ -381,6 +392,9 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(val), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(val), + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + } } } @@ -401,6 +415,9 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(val), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(val), + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + } } } @@ -418,6 +435,9 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(map(val)), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(val), + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + } } } @@ -435,6 +455,9 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(val), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(map(val)), + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + } } } } diff --git a/swarm/src/handler/either.rs b/swarm/src/handler/either.rs index 0b8c2f7f14e..50c16628e19 100644 --- a/swarm/src/handler/either.rs +++ b/swarm/src/handler/either.rs @@ -322,11 +322,21 @@ where handler.on_connection_event(ConnectionEvent::AddressChange(address_change)) } }, - ConnectionEvent::ProtocolsChange(supported_protocols) => match self { - Either::Left(handler) => handler - .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)), - Either::Right(handler) => handler - .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)), + ConnectionEvent::LocalProtocolsChange(supported_protocols) => match self { + Either::Left(handler) => handler.on_connection_event( + ConnectionEvent::LocalProtocolsChange(supported_protocols), + ), + Either::Right(handler) => handler.on_connection_event( + ConnectionEvent::LocalProtocolsChange(supported_protocols), + ), + }, + ConnectionEvent::RemoteProtocolsChange(supported_protocols) => match self { + Either::Left(handler) => handler.on_connection_event( + ConnectionEvent::RemoteProtocolsChange(supported_protocols), + ), + Either::Right(handler) => handler.on_connection_event( + ConnectionEvent::RemoteProtocolsChange(supported_protocols), + ), }, } } diff --git a/swarm/src/handler/map_out.rs b/swarm/src/handler/map_out.rs index 773df2b6681..3c3f3827dfa 100644 --- a/swarm/src/handler/map_out.rs +++ b/swarm/src/handler/map_out.rs @@ -81,6 +81,9 @@ where ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } => { ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } } + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { + ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + } }) } diff --git a/swarm/src/handler/multi.rs b/swarm/src/handler/multi.rs index 8cd6e4c916a..16b11977f6d 100644 --- a/swarm/src/handler/multi.rs +++ b/swarm/src/handler/multi.rs @@ -318,9 +318,18 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } - ConnectionEvent::ProtocolsChange(supported_protocols) => { + ConnectionEvent::LocalProtocolsChange(supported_protocols) => { for h in self.handlers.values_mut() { - h.on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)); + h.on_connection_event(ConnectionEvent::LocalProtocolsChange( + supported_protocols, + )); + } + } + ConnectionEvent::RemoteProtocolsChange(supported_protocols) => { + for h in self.handlers.values_mut() { + h.on_connection_event(ConnectionEvent::RemoteProtocolsChange( + supported_protocols, + )); } } } diff --git a/swarm/src/handler/one_shot.rs b/swarm/src/handler/one_shot.rs index bc46f05d4b5..01e20e85a87 100644 --- a/swarm/src/handler/one_shot.rs +++ b/swarm/src/handler/one_shot.rs @@ -219,7 +219,8 @@ where } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/swarm/src/handler/pending.rs b/swarm/src/handler/pending.rs index 843988c8fd3..7cf8b9209fa 100644 --- a/swarm/src/handler/pending.rs +++ b/swarm/src/handler/pending.rs @@ -100,7 +100,8 @@ impl ConnectionHandler for PendingConnectionHandler { ConnectionEvent::AddressChange(_) | ConnectionEvent::DialUpgradeError(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/swarm/src/handler/select.rs b/swarm/src/handler/select.rs index c584b236d40..2389bc933d6 100644 --- a/swarm/src/handler/select.rs +++ b/swarm/src/handler/select.rs @@ -400,6 +400,9 @@ where .map_info(Either::Left), }); } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }) => { + return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }); + } Poll::Pending => (), }; @@ -417,6 +420,9 @@ where .map_info(Either::Right), }); } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }) => { + return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }); + } Poll::Pending => (), }; @@ -477,11 +483,25 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } - ConnectionEvent::ProtocolsChange(supported_protocols) => { + ConnectionEvent::LocalProtocolsChange(supported_protocols) => { + self.proto1 + .on_connection_event(ConnectionEvent::LocalProtocolsChange( + supported_protocols, + )); + self.proto2 + .on_connection_event(ConnectionEvent::LocalProtocolsChange( + supported_protocols, + )); + } + ConnectionEvent::RemoteProtocolsChange(supported_protocols) => { self.proto1 - .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)); + .on_connection_event(ConnectionEvent::RemoteProtocolsChange( + supported_protocols, + )); self.proto2 - .on_connection_event(ConnectionEvent::ProtocolsChange(supported_protocols)); + .on_connection_event(ConnectionEvent::RemoteProtocolsChange( + supported_protocols, + )); } } } diff --git a/swarm/src/keep_alive.rs b/swarm/src/keep_alive.rs index 366f2614f71..aa4da2db826 100644 --- a/swarm/src/keep_alive.rs +++ b/swarm/src/keep_alive.rs @@ -137,7 +137,8 @@ impl crate::handler::ConnectionHandler for ConnectionHandler { ConnectionEvent::DialUpgradeError(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::AddressChange(_) - | ConnectionEvent::ProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } From e536deaded5235755422cc09990795e5d528e86d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 24 Mar 2023 15:41:16 +0100 Subject: [PATCH 003/105] Consume supported protocols in identify --- protocols/identify/src/behaviour.rs | 13 +------------ protocols/identify/src/handler.rs | 17 +++++++++-------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 8ba50e2db4b..c5cbc12e8c6 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -323,7 +323,7 @@ impl NetworkBehaviour for Behaviour { fn poll( &mut self, _cx: &mut Context<'_>, - params: &mut impl PollParameters, + _: &mut impl PollParameters, ) -> Poll>> { if let Some(event) = self.events.pop_front() { return Poll::Ready(event); @@ -344,7 +344,6 @@ impl NetworkBehaviour for Behaviour { .chain(self.external_addresses.iter()) .cloned() .collect(), - supported_protocols: supported_protocols(params), protocol: Protocol::Push, }, }), @@ -361,7 +360,6 @@ impl NetworkBehaviour for Behaviour { .chain(self.external_addresses.iter()) .cloned() .collect(), - supported_protocols: supported_protocols(params), protocol: Protocol::Identify(connection_id), }, }), @@ -488,15 +486,6 @@ pub enum Event { }, } -fn supported_protocols(params: &impl PollParameters) -> Vec { - // The protocol names can be bytes, but the identify protocol except UTF-8 strings. - // There's not much we can do to solve this conflict except strip non-UTF-8 characters. - params - .supported_protocols() - .map(|p| String::from_utf8_lossy(&p).to_string()) - .collect() -} - /// If there is a given peer_id in the multiaddr, make sure it is the same as /// the given peer_id. If there is no peer_id for the peer in the mutiaddr, this returns true. fn multiaddr_matches_peer_id(addr: &Multiaddr, peer_id: &PeerId) -> bool { diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 3c39652c04c..45092b06e5a 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -32,6 +32,7 @@ use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, + ProtocolsChange, }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, @@ -83,6 +84,8 @@ pub struct Handler { /// Address observed by or for the remote. observed_addr: Multiaddr, + + local_supported_protocols: Vec, } /// An event from `Behaviour` with the information requested by the `Handler`. @@ -91,9 +94,6 @@ pub struct InEvent { /// The addresses that the peer is listening on. pub listen_addrs: Vec, - /// The list of protocols supported by the peer, e.g. `/ipfs/ping/1.0.0`. - pub supported_protocols: Vec, - /// The protocol w.r.t. the information requested. pub protocol: Protocol, } @@ -138,6 +138,7 @@ impl Handler { protocol_version, agent_version, observed_addr, + local_supported_protocols: vec![], } } @@ -238,7 +239,6 @@ impl ConnectionHandler for Handler { &mut self, InEvent { listen_addrs, - supported_protocols, protocol, }: Self::InEvent, ) { @@ -247,7 +247,7 @@ impl ConnectionHandler for Handler { protocol_version: self.protocol_version.clone(), agent_version: self.agent_version.clone(), listen_addrs, - protocols: supported_protocols, + protocols: self.local_supported_protocols.clone(), observed_addr: self.observed_addr.clone(), }; @@ -344,9 +344,10 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) - | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::LocalProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::LocalProtocolsChange(ProtocolsChange { protocols }) => { + self.local_supported_protocols = protocols.to_vec(); + } ConnectionEvent::RemoteProtocolsChange(_) => {} } } From 7699a1e2edf66c7064078cecdde8d7c8df4be502 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 24 Mar 2023 15:50:26 +0100 Subject: [PATCH 004/105] Report a remote's protocols to other handlers --- protocols/identify/src/handler.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 45092b06e5a..bae3a35b8fa 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -187,6 +187,10 @@ impl Handler { ) { match output { future::Either::Left(remote_info) => { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols { + protocols: remote_info.protocols.clone(), + }); self.events .push(ConnectionHandlerEvent::Custom(Event::Identified( remote_info, @@ -307,6 +311,10 @@ impl ConnectionHandler for Handler { self.inbound_identify_push.take(); if let Ok(info) = res { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols { + protocols: info.protocols.clone(), + }); return Poll::Ready(ConnectionHandlerEvent::Custom(Event::Identified(info))); } } From 450fc1ec69dd45ff525f19c68d9b7fb59d45f03e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 24 Mar 2023 16:46:40 +0100 Subject: [PATCH 005/105] Add test for kademlia client mode --- Cargo.lock | 3 ++ protocols/kad/Cargo.toml | 4 ++ protocols/kad/src/behaviour.rs | 22 ++++++++++- protocols/kad/src/handler.rs | 20 +--------- protocols/kad/src/lib.rs | 2 +- protocols/kad/tests/client_mode.rs | 59 ++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 protocols/kad/tests/client_mode.rs diff --git a/Cargo.lock b/Cargo.lock index cef78ac2a4d..ae797e51433 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2464,6 +2464,7 @@ name = "libp2p-kad" version = "0.43.1" dependencies = [ "arrayvec", + "async-std", "asynchronous-codec", "bytes", "either", @@ -2473,9 +2474,11 @@ dependencies = [ "futures-timer", "instant", "libp2p-core", + "libp2p-identify", "libp2p-identity", "libp2p-noise", "libp2p-swarm", + "libp2p-swarm-test", "libp2p-yamux", "log", "quick-protobuf", diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index aa76253fe88..1b58ba2a0a7 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -34,9 +34,13 @@ serde = { version = "1.0", optional = true, features = ["derive"] } thiserror = "1" [dev-dependencies] +async-std = { version = "1.12.0", features = ["attributes"] } env_logger = "0.10.0" futures-timer = "3.0" +libp2p-identify = { path = "../identify" } libp2p-noise = { path = "../../transports/noise" } +libp2p-swarm = { path = "../../swarm", features = ["macros"] } +libp2p-swarm-test = { path = "../../swarm-test" } libp2p-yamux = { path = "../../muxers/yamux" } quickcheck = { package = "quickcheck-ext", path = "../../misc/quickcheck-ext" } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 8d1ee716973..42ce824a5b5 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -116,6 +116,7 @@ pub struct Kademlia { /// The record storage. store: TStore, + mode: Mode, } /// The configurable strategies for the insertion of peers @@ -182,6 +183,7 @@ pub struct KademliaConfig { connection_idle_timeout: Duration, kbucket_inserts: KademliaBucketInserts, caching: KademliaCaching, + mode: Mode, } impl Default for KademliaConfig { @@ -199,6 +201,7 @@ impl Default for KademliaConfig { connection_idle_timeout: Duration::from_secs(10), kbucket_inserts: KademliaBucketInserts::OnConnected, caching: KademliaCaching::Enabled { max_peers: 1 }, + mode: Mode::Server, } } } @@ -399,6 +402,14 @@ impl KademliaConfig { self.caching = c; self } + + /// Sets the mode. + /// + /// TODO: More docs. + pub fn set_mode(&mut self, m: Mode) -> &mut Self { + self.mode = m; + self + } } impl Kademlia @@ -453,6 +464,7 @@ where connection_idle_timeout: config.connection_idle_timeout, external_addresses: Default::default(), local_peer_id: id, + mode: config.mode, } } @@ -1976,7 +1988,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: true, + allow_listening: self.mode == Mode::Server, idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Listener { @@ -1997,7 +2009,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: true, + allow_listening: self.mode == Mode::Server, idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Dialer { @@ -3190,3 +3202,9 @@ pub enum RoutingUpdate { /// peer ID). Failed, } + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Mode { + Client, + Server, +} diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index e7979271a87..20b45dab4a7 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -789,25 +789,7 @@ where ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::LocalProtocolsChange(_) => {} - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange { protocols }) => { - // TODO: We should cache this / it will get simpler with #2831. - let kademlia_protocols = self - .config - .protocol_config - .protocol_names() - .iter() - .filter_map(|b| String::from_utf8(b.to_vec()).ok()) - .collect::>(); - - let remote_supports_our_kademlia_protocols = - kademlia_protocols.iter().all(|p| protocols.contains(p)); - - if remote_supports_our_kademlia_protocols { - self.protocol_status = ProtocolStatus::Confirmed; - } else { - self.protocol_status = ProtocolStatus::NotSupported; - } - } + ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index 3e9dcecc0e4..1480609ccd0 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -61,7 +61,7 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, NoKnownPeers, PeerRecord, PutRecordContext, + GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, QueryResult, QueryStats, }; diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs new file mode 100644 index 00000000000..9c4efc1199f --- /dev/null +++ b/protocols/kad/tests/client_mode.rs @@ -0,0 +1,59 @@ +use libp2p_identify as identify; +use libp2p_identity as identity; +use libp2p_kad::store::MemoryStore; +use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent, Mode}; +use libp2p_swarm::Swarm; +use libp2p_swarm_test::SwarmExt; + +#[async_std::test] +async fn connection_to_node_in_client_mode_does_not_update_routing_table() { + let mut client = Swarm::new_ephemeral(MyBehaviour::client); + let mut server = Swarm::new_ephemeral(MyBehaviour::server); + + server.listen().await; + client.connect(&mut server).await; + + let server_peer_id = *server.local_peer_id(); + + match libp2p_swarm_test::drive(&mut client, &mut server).await { + ( + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer, .. })], + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_)], + ) => { + assert_eq!(peer, server_peer_id) + } + other => panic!("Unexpected events: {other:?}"), + } +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct MyBehaviour { + identify: identify::Behaviour, + kad: Kademlia, +} + +impl MyBehaviour { + fn client(k: identity::Keypair) -> Self { + let mut config = KademliaConfig::default(); + config.set_mode(Mode::Client); + + Self::with_config(k, config) + } + + fn server(k: identity::Keypair) -> Self { + Self::with_config(k, KademliaConfig::default()) + } + + fn with_config(k: identity::Keypair, config: KademliaConfig) -> MyBehaviour { + let local_peer_id = k.public().to_peer_id(); + + Self { + identify: identify::Behaviour::new(identify::Config::new( + "/test/1.0.0".to_owned(), + k.public(), + )), + kad: Kademlia::with_config(local_peer_id, MemoryStore::new(local_peer_id), config), + } + } +} From a972b9ccaf246881febc3c1940fcd25e26845b27 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 24 Mar 2023 16:09:29 +0100 Subject: [PATCH 006/105] Implement kademlia client-mode --- protocols/kad/src/behaviour.rs | 8 +++++++ protocols/kad/src/handler.rs | 41 +++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 42ce824a5b5..ad2b30b7a55 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2074,6 +2074,14 @@ where self.connection_updated(source, address, NodeStatus::Connected); } + KademliaHandlerEvent::ProtocolNotSupported { endpoint } => { + let address = match endpoint { + ConnectedPoint::Dialer { address, .. } => Some(address), + ConnectedPoint::Listener { .. } => None, + }; + self.connection_updated(source, address, NodeStatus::Disconnected); + } + KademliaHandlerEvent::FindNodeReq { key, request_id } => { let closer_peers = self.find_closest(&kbucket::Key::new(key), &source); diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 20b45dab4a7..5f660088531 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -31,6 +31,7 @@ use libp2p_core::{upgrade, ConnectedPoint}; use libp2p_identity::PeerId; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, + ProtocolsChange, }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, @@ -92,10 +93,12 @@ pub struct KademliaHandler { enum ProtocolStatus { /// It is as yet unknown whether the remote supports the /// configured protocol name. - Unconfirmed, + Unknown, /// The configured protocol name has been confirmed by the remote /// but has not yet been reported to the `Kademlia` behaviour. Confirmed, + /// The configured protocol name(s) are not or no longer supported by the remote. + NotSupported, /// The configured protocol has been confirmed by the remote /// and the confirmation reported to the `Kademlia` behaviour. Reported, @@ -226,13 +229,11 @@ impl InboundSubstreamState { #[derive(Debug)] pub enum KademliaHandlerEvent { /// The configured protocol name has been confirmed by the peer through - /// a successfully negotiated substream. - /// - /// This event is only emitted once by a handler upon the first - /// successfully negotiated inbound or outbound substream and - /// indicates that the connected peer participates in the Kademlia - /// overlay network identified by the configured protocol name. + /// a successfully negotiated substream or by learning the supported protocols of the remote. ProtocolConfirmed { endpoint: ConnectedPoint }, + /// The configured protocol name(s) are not or no longer supported by the peer on the provided + /// connection and it should be removed from the routing table. + ProtocolNotSupported { endpoint: ConnectedPoint }, /// Request for the list of nodes whose IDs are the closest to `key`. The number of nodes /// returned is not specified, but should be around 20. @@ -501,7 +502,7 @@ where num_requested_outbound_streams: 0, requested_streams: Default::default(), keep_alive, - protocol_status: ProtocolStatus::Unconfirmed, + protocol_status: ProtocolStatus::Unknown, } } @@ -520,7 +521,7 @@ where protocol, msg, user_data, )); self.num_requested_outbound_streams -= 1; - if let ProtocolStatus::Unconfirmed = self.protocol_status { + if let ProtocolStatus::Unknown = self.protocol_status { // Upon the first successfully negotiated substream, we know that the // remote is configured with the same protocol name and we want // the behaviour to add this peer to the routing table, if possible. @@ -542,7 +543,7 @@ where future::Either::Right(p) => void::unreachable(p), }; - if let ProtocolStatus::Unconfirmed = self.protocol_status { + if let ProtocolStatus::Unknown = self.protocol_status { // Upon the first successfully negotiated substream, we know that the // remote is configured with the same protocol name and we want // the behaviour to add this peer to the routing table, if possible. @@ -789,7 +790,25 @@ where ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::LocalProtocolsChange(_) => {} - ConnectionEvent::RemoteProtocolsChange(_) => {} + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange { protocols }) => { + // TODO: We should cache this / it will get simpler with #2831. + let kademlia_protocols = self + .config + .protocol_config + .protocol_names() + .iter() + .filter_map(|b| String::from_utf8(b.to_vec()).ok()) + .collect::>(); + + let remote_supports_our_kademlia_protocols = + kademlia_protocols.iter().all(|p| protocols.contains(p)); + + if remote_supports_our_kademlia_protocols { + self.protocol_status = ProtocolStatus::Confirmed; + } else { + self.protocol_status = ProtocolStatus::NotSupported; + } + } } } } From f9cf33f99279680a9598391c3c8896376bcc1386 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Fri, 24 Mar 2023 16:55:35 +0100 Subject: [PATCH 007/105] Add tests for two servers connecting --- protocols/kad/tests/client_mode.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index 9c4efc1199f..c2ddd9aa555 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -26,6 +26,31 @@ async fn connection_to_node_in_client_mode_does_not_update_routing_table() { } } +#[async_std::test] +async fn two_servers_add_each_other_to_routing_table() { + let mut server1 = Swarm::new_ephemeral(MyBehaviour::server); + let mut server2 = Swarm::new_ephemeral(MyBehaviour::server); + + server1.listen().await; + server2.listen().await; + + server1.connect(&mut server2).await; + + let server1_peer_id = *server1.local_peer_id(); + let server2_peer_id = *server2.local_peer_id(); + + match libp2p_swarm_test::drive(&mut server1, &mut server2).await { + ( + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer: peer1, .. })], + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::UnroutablePeer { peer: peer2, .. })], // Unroutable because server2 did not dial. + ) => { + assert_eq!(peer1, server2_peer_id); + assert_eq!(peer2, server1_peer_id); + } + other => panic!("Unexpected events: {other:?}"), + } +} + #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct MyBehaviour { From deb30f3b5d96d4a7fed420dd2eb0c57baf4bc179 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 13 Apr 2023 19:56:15 +0200 Subject: [PATCH 008/105] Report additions and removals of protocols instead --- protocols/identify/src/handler.rs | 61 +++++++++++++++++-------- protocols/kad/src/handler.rs | 50 ++++++++++++-------- swarm/src/connection.rs | 76 ++++++++++++++++++++++--------- swarm/src/handler.rs | 70 ++++++++++++++++++++-------- swarm/src/handler/map_out.rs | 4 +- swarm/src/handler/multi.rs | 4 +- swarm/src/handler/select.rs | 12 ++--- 7 files changed, 190 insertions(+), 87 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index bae3a35b8fa..880930243a3 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -30,17 +30,14 @@ use libp2p_core::upgrade::SelectUpgrade; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; -use libp2p_swarm::handler::{ - ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, - ProtocolsChange, -}; +use libp2p_swarm::handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange, ProtocolSupport}; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, NegotiatedSubstream, SubstreamProtocol, }; use log::warn; use smallvec::SmallVec; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; use std::{io, pin::Pin, task::Context, task::Poll, time::Duration}; /// Protocol handler for sending and receiving identification requests. @@ -85,7 +82,8 @@ pub struct Handler { /// Address observed by or for the remote. observed_addr: Multiaddr, - local_supported_protocols: Vec, + local_supported_protocols: HashSet, + remote_supported_protocols: HashSet, } /// An event from `Behaviour` with the information requested by the `Handler`. @@ -138,7 +136,8 @@ impl Handler { protocol_version, agent_version, observed_addr, - local_supported_protocols: vec![], + local_supported_protocols: HashSet::new(), + remote_supported_protocols: HashSet::new(), } } @@ -187,10 +186,30 @@ impl Handler { ) { match output { future::Either::Left(remote_info) => { - self.events - .push(ConnectionHandlerEvent::ReportRemoteProtocols { - protocols: remote_info.protocols.clone(), - }); + let new_remote_protocols = HashSet::from_iter(remote_info.protocols.clone()); + + let remote_added_protocols = new_remote_protocols + .difference(&self.remote_supported_protocols) + .cloned() + .collect::>(); + let remote_removed_protocols = self + .remote_supported_protocols + .difference(&new_remote_protocols) + .cloned() + .collect::>(); + + if !remote_added_protocols.is_empty() { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols(ProtocolSupport::Added(remote_added_protocols))); + } + + if !remote_removed_protocols.is_empty() { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols(ProtocolSupport::Removed(remote_removed_protocols))); + } + + self.remote_supported_protocols = new_remote_protocols; + self.events .push(ConnectionHandlerEvent::Custom(Event::Identified( remote_info, @@ -251,7 +270,7 @@ impl ConnectionHandler for Handler { protocol_version: self.protocol_version.clone(), agent_version: self.agent_version.clone(), listen_addrs, - protocols: self.local_supported_protocols.clone(), + protocols: Vec::from_iter(self.local_supported_protocols.clone()), observed_addr: self.observed_addr.clone(), }; @@ -311,10 +330,11 @@ impl ConnectionHandler for Handler { self.inbound_identify_push.take(); if let Ok(info) = res { - self.events - .push(ConnectionHandlerEvent::ReportRemoteProtocols { - protocols: info.protocols.clone(), - }); + // TODO: report new protocols + // self.events + // .push(ConnectionHandlerEvent::ReportRemoteProtocols { + // protocols: info.protocols.clone(), + // }); return Poll::Ready(ConnectionHandlerEvent::Custom(Event::Identified(info))); } } @@ -353,8 +373,13 @@ impl ConnectionHandler for Handler { self.on_dial_upgrade_error(dial_upgrade_error) } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} - ConnectionEvent::LocalProtocolsChange(ProtocolsChange { protocols }) => { - self.local_supported_protocols = protocols.to_vec(); + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { + self.local_supported_protocols.extend(added.cloned()); + } + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { + for p in removed { + self.local_supported_protocols.remove(p); + } } ConnectionEvent::RemoteProtocolsChange(_) => {} } diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 5f660088531..73f0a0670c0 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -38,7 +38,7 @@ use libp2p_swarm::{ NegotiatedSubstream, SubstreamProtocol, }; use log::trace; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; use std::task::Waker; use std::{ error, fmt, io, marker::PhantomData, pin::Pin, task::Context, task::Poll, time::Duration, @@ -86,6 +86,8 @@ pub struct KademliaHandler { /// The current state of protocol confirmation. protocol_status: ProtocolStatus, + + remote_supported_protocols: HashSet, } /// The states of protocol confirmation that a connection @@ -503,6 +505,7 @@ where requested_streams: Default::default(), keep_alive, protocol_status: ProtocolStatus::Unknown, + remote_supported_protocols: Default::default(), } } @@ -790,26 +793,37 @@ where ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::LocalProtocolsChange(_) => {} - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange { protocols }) => { - // TODO: We should cache this / it will get simpler with #2831. - let kademlia_protocols = self - .config - .protocol_config - .protocol_names() - .iter() - .filter_map(|b| String::from_utf8(b.to_vec()).ok()) - .collect::>(); - - let remote_supports_our_kademlia_protocols = - kademlia_protocols.iter().all(|p| protocols.contains(p)); - - if remote_supports_our_kademlia_protocols { - self.protocol_status = ProtocolStatus::Confirmed; - } else { - self.protocol_status = ProtocolStatus::NotSupported; + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(added)) => { + for p in added { + self.remote_supported_protocols.insert(p.to_owned()); + } + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(removed)) => { + for p in removed { + self.remote_supported_protocols.remove(p); } } } + + // TODO: We should cache this / it will get simpler with #2831. + let our_kademlia_protocols = self + .config + .protocol_config + .protocol_names() + .iter() + .filter_map(|b| String::from_utf8(b.to_vec()).ok()) + .collect::>(); + + let remote_supports_our_kademlia_protocols = self + .remote_supported_protocols + .iter() + .any(|p| our_kademlia_protocols.contains(p)); + + if remote_supports_our_kademlia_protocols { + self.protocol_status = ProtocolStatus::Confirmed; + } else { + self.protocol_status = ProtocolStatus::NotSupported; + } } } diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 089768f46ea..9eef892456f 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -29,7 +29,8 @@ pub use error::{ use crate::handler::{ AddressChange, ConnectionEvent, ConnectionHandler, DialUpgradeError, FullyNegotiatedInbound, - FullyNegotiatedOutbound, ListenUpgradeError, ProtocolsChange, + FullyNegotiatedOutbound, ListenUpgradeError, ProtocolSupport, ProtocolsAdded, ProtocolsChange, + ProtocolsRemoved, }; use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, UpgradeInfoSend}; use crate::{ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, SubstreamProtocol}; @@ -45,6 +46,7 @@ use libp2p_core::upgrade::{InboundUpgradeApply, OutboundUpgradeApply}; use libp2p_core::Endpoint; use libp2p_core::{upgrade, ProtocolName as _, UpgradeError}; use libp2p_identity::PeerId; +use std::collections::HashSet; use std::future::Future; use std::sync::atomic::{AtomicUsize, Ordering}; use std::task::Waker; @@ -146,7 +148,7 @@ where SubstreamRequested, >, - supported_protocols: Vec, + supported_protocols: HashSet, } impl fmt::Debug for Connection @@ -184,7 +186,7 @@ where substream_upgrade_protocol_override, max_negotiating_inbound_streams, requested_substreams: Default::default(), - supported_protocols: vec![], + supported_protocols: HashSet::new(), } } @@ -248,11 +250,23 @@ where Poll::Ready(ConnectionHandlerEvent::Close(err)) => { return Poll::Ready(Err(ConnectionError::Handler(err))); } - Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }) => { + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Added(protocols), + )) => { handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange { - protocols: &protocols, - }, + ProtocolsChange::Added(ProtocolsAdded { + protocols: protocols.difference(&HashSet::new()).peekable(), // This is a bit of a hack to use the same type internally in `ProtocolsAdded`. + }), + )); + continue; + } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Removed(protocols), + )) => { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( + ProtocolsChange::Removed(ProtocolsRemoved { + protocols: protocols.difference(&HashSet::new()).peekable(), // This is a bit of a hack to use the same type internally in `ProtocolsRemoved`. + }), )); continue; } @@ -367,21 +381,33 @@ where Poll::Ready(substream) => { let protocol = handler.listen_protocol(); - let mut new_protocols = protocol + let new_protocols = protocol .upgrade() .protocol_info() .filter_map(|i| String::from_utf8(i.protocol_name().to_vec()).ok()) - .collect::>(); - - new_protocols.sort(); - - if supported_protocols != &new_protocols { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange { - protocols: &new_protocols, - }, - )); - *supported_protocols = new_protocols; + .collect::>(); + + if &new_protocols != supported_protocols { + let mut added_protocols = + new_protocols.difference(supported_protocols).peekable(); + let mut removed_protocols = + supported_protocols.difference(&new_protocols).peekable(); + + if added_protocols.peek().is_some() { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Removed(ProtocolsRemoved { + protocols: added_protocols, + }), + )); + } + + if removed_protocols.peek().is_some() { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Removed(ProtocolsRemoved { + protocols: removed_protocols, + }), + )); + } } negotiating_in.push(SubstreamUpgrade::new_inbound(substream, protocol)); @@ -956,8 +982,16 @@ mod tests { Self::OutboundOpenInfo, >, ) { - if let ConnectionEvent::LocalProtocolsChange(ProtocolsChange { protocols }) = event { - self.reported_protocols = protocols.to_vec(); + match event { + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { + self.reported_protocols.extend(added.cloned()); + } + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { + for protocol in removed { + self.reported_protocols.retain(|p| p != protocol); + } + } + _ => {} } } diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 0dcc819a69a..c2bec3de114 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -51,7 +51,11 @@ pub use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, U use instant::Instant; use libp2p_core::{upgrade::UpgradeError, ConnectedPoint, Multiaddr}; use libp2p_identity::PeerId; +use std::collections::hash_set::{Difference}; use std::{cmp::Ordering, error, fmt, task::Context, task::Poll, time::Duration}; +use std::collections::hash_map::RandomState; +use std::collections::HashSet; +use std::iter::Peekable; pub use map_in::MapInEvent; pub use map_out::MapOutEvent; @@ -244,9 +248,34 @@ pub struct AddressChange<'a> { } /// [`ConnectionEvent`] variant that informs the handler about a change in the protocols supported on the connection. -#[derive(Clone, Copy)] -pub struct ProtocolsChange<'a> { - pub protocols: &'a [String], +#[derive(Clone)] +pub enum ProtocolsChange<'a> { + Added(ProtocolsAdded<'a>), + Removed(ProtocolsRemoved<'a>), +} + +#[derive(Clone)] +pub struct ProtocolsAdded<'a> { + pub(crate) protocols: Peekable>, +} + +#[derive(Clone)] +pub struct ProtocolsRemoved<'a> { + pub(crate) protocols: Peekable>, +} + +impl<'a> Iterator for ProtocolsAdded<'a> { + type Item = &'a String; + fn next(&mut self) -> Option { + self.protocols.next() + } +} + +impl<'a> Iterator for ProtocolsRemoved<'a> { + type Item = &'a String; + fn next(&mut self) -> Option { + self.protocols.next() + } } /// [`ConnectionEvent`] variant that informs the handler @@ -357,20 +386,21 @@ pub enum ConnectionHandlerEvent }, + /// We learned something about the protocols supported by the remote. + ReportRemoteProtocols(ProtocolSupport), /// Other event. Custom(TCustom), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ProtocolSupport { + /// The remote now supports these protocols. + Added(HashSet), + /// The remote no longer supports these protocols. + Removed(HashSet), +} + /// Event produced by a handler. impl ConnectionHandlerEvent @@ -392,8 +422,8 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(val), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(val), - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) } } } @@ -415,8 +445,8 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(val), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(val), - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) } } } @@ -435,8 +465,8 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(map(val)), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(val), - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) } } } @@ -455,8 +485,8 @@ impl } ConnectionHandlerEvent::Custom(val) => ConnectionHandlerEvent::Custom(val), ConnectionHandlerEvent::Close(val) => ConnectionHandlerEvent::Close(map(val)), - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) } } } diff --git a/swarm/src/handler/map_out.rs b/swarm/src/handler/map_out.rs index 3c3f3827dfa..349aa553764 100644 --- a/swarm/src/handler/map_out.rs +++ b/swarm/src/handler/map_out.rs @@ -81,8 +81,8 @@ where ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } => { ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } } - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } => { - ConnectionHandlerEvent::ReportRemoteProtocols { protocols } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) } }) } diff --git a/swarm/src/handler/multi.rs b/swarm/src/handler/multi.rs index 16b11977f6d..ed8a434834c 100644 --- a/swarm/src/handler/multi.rs +++ b/swarm/src/handler/multi.rs @@ -321,14 +321,14 @@ where ConnectionEvent::LocalProtocolsChange(supported_protocols) => { for h in self.handlers.values_mut() { h.on_connection_event(ConnectionEvent::LocalProtocolsChange( - supported_protocols, + supported_protocols.clone(), )); } } ConnectionEvent::RemoteProtocolsChange(supported_protocols) => { for h in self.handlers.values_mut() { h.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - supported_protocols, + supported_protocols.clone(), )); } } diff --git a/swarm/src/handler/select.rs b/swarm/src/handler/select.rs index 2389bc933d6..3faea293734 100644 --- a/swarm/src/handler/select.rs +++ b/swarm/src/handler/select.rs @@ -400,8 +400,8 @@ where .map_info(Either::Left), }); } - Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }) => { - return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }); + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)) => { + return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)); } Poll::Pending => (), }; @@ -420,8 +420,8 @@ where .map_info(Either::Right), }); } - Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }) => { - return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols { protocols }); + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)) => { + return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)); } Poll::Pending => (), }; @@ -486,7 +486,7 @@ where ConnectionEvent::LocalProtocolsChange(supported_protocols) => { self.proto1 .on_connection_event(ConnectionEvent::LocalProtocolsChange( - supported_protocols, + supported_protocols.clone(), )); self.proto2 .on_connection_event(ConnectionEvent::LocalProtocolsChange( @@ -496,7 +496,7 @@ where ConnectionEvent::RemoteProtocolsChange(supported_protocols) => { self.proto1 .on_connection_event(ConnectionEvent::RemoteProtocolsChange( - supported_protocols, + supported_protocols.clone(), )); self.proto2 .on_connection_event(ConnectionEvent::RemoteProtocolsChange( From 3c1bf5f4a5dc5913f2ebc8668141acdbbc44cb5a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 13 Apr 2023 20:03:28 +0200 Subject: [PATCH 009/105] Report listen protocols on startup to connection --- protocols/identify/src/handler.rs | 13 ++++++++++--- swarm/src/connection.rs | 15 +++++++++++++++ swarm/src/handler.rs | 4 ++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 880930243a3..e9d587dc56b 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -30,7 +30,10 @@ use libp2p_core::upgrade::SelectUpgrade; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; -use libp2p_swarm::handler::{ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, ProtocolsChange, ProtocolSupport}; +use libp2p_swarm::handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, + ProtocolSupport, ProtocolsChange, +}; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, NegotiatedSubstream, SubstreamProtocol, @@ -200,12 +203,16 @@ impl Handler { if !remote_added_protocols.is_empty() { self.events - .push(ConnectionHandlerEvent::ReportRemoteProtocols(ProtocolSupport::Added(remote_added_protocols))); + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Added(remote_added_protocols), + )); } if !remote_removed_protocols.is_empty() { self.events - .push(ConnectionHandlerEvent::ReportRemoteProtocols(ProtocolSupport::Removed(remote_removed_protocols))); + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Removed(remote_removed_protocols), + )); } self.remote_supported_protocols = new_remote_protocols; diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 9eef892456f..8f68fb4a84b 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -219,6 +219,21 @@ where supported_protocols, } = self.get_mut(); + let protocol = handler.listen_protocol(); + + let new_protocols = protocol + .upgrade() + .protocol_info() + .filter_map(|i| String::from_utf8(i.protocol_name().to_vec()).ok()) + .collect::>(); + + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Added(ProtocolsAdded { + protocols: new_protocols.difference(&HashSet::new()).peekable(), + }), + )); + *supported_protocols = new_protocols; + loop { match requested_substreams.poll_next_unpin(cx) { Poll::Ready(Some(Ok(()))) => continue, diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index c2bec3de114..84d186f0786 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -51,11 +51,11 @@ pub use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, U use instant::Instant; use libp2p_core::{upgrade::UpgradeError, ConnectedPoint, Multiaddr}; use libp2p_identity::PeerId; -use std::collections::hash_set::{Difference}; -use std::{cmp::Ordering, error, fmt, task::Context, task::Poll, time::Duration}; use std::collections::hash_map::RandomState; +use std::collections::hash_set::Difference; use std::collections::HashSet; use std::iter::Peekable; +use std::{cmp::Ordering, error, fmt, task::Context, task::Poll, time::Duration}; pub use map_in::MapInEvent; pub use map_out::MapOutEvent; From 9e707297b1aefa0cd9b9a7cc5eb779bb73db95cf Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 13 Apr 2023 20:26:48 +0200 Subject: [PATCH 010/105] Introduce `SupportedProtocols` type --- protocols/identify/src/handler.rs | 21 +++---- swarm/src/connection.rs | 70 +++++++++++++++------ swarm/src/connection/supported_protocols.rs | 26 ++++++++ swarm/src/lib.rs | 2 +- 4 files changed, 85 insertions(+), 34 deletions(-) create mode 100644 swarm/src/connection/supported_protocols.rs diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index e9d587dc56b..4904dfb07e4 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -32,11 +32,11 @@ use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, - ProtocolSupport, ProtocolsChange, + ProtocolSupport, }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, - NegotiatedSubstream, SubstreamProtocol, + NegotiatedSubstream, SubstreamProtocol, SupportedProtocols, }; use log::warn; use smallvec::SmallVec; @@ -85,7 +85,7 @@ pub struct Handler { /// Address observed by or for the remote. observed_addr: Multiaddr, - local_supported_protocols: HashSet, + local_supported_protocols: SupportedProtocols, remote_supported_protocols: HashSet, } @@ -139,8 +139,8 @@ impl Handler { protocol_version, agent_version, observed_addr, - local_supported_protocols: HashSet::new(), - remote_supported_protocols: HashSet::new(), + local_supported_protocols: SupportedProtocols::default(), + remote_supported_protocols: HashSet::default(), } } @@ -277,7 +277,7 @@ impl ConnectionHandler for Handler { protocol_version: self.protocol_version.clone(), agent_version: self.agent_version.clone(), listen_addrs, - protocols: Vec::from_iter(self.local_supported_protocols.clone()), + protocols: Vec::from_iter(self.local_supported_protocols.iter().cloned()), observed_addr: self.observed_addr.clone(), }; @@ -380,13 +380,8 @@ impl ConnectionHandler for Handler { self.on_dial_upgrade_error(dial_upgrade_error) } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} - ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { - self.local_supported_protocols.extend(added.cloned()); - } - ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { - for p in removed { - self.local_supported_protocols.remove(p); - } + ConnectionEvent::LocalProtocolsChange(change) => { + self.local_supported_protocols.on_protocols_change(change); } ConnectionEvent::RemoteProtocolsChange(_) => {} } diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 8f68fb4a84b..7bc66225258 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -21,11 +21,13 @@ mod error; pub(crate) mod pool; +mod supported_protocols; pub use error::{ ConnectionError, PendingConnectionError, PendingInboundConnectionError, PendingOutboundConnectionError, }; +pub use supported_protocols::SupportedProtocols; use crate::handler::{ AddressChange, ConnectionEvent, ConnectionHandler, DialUpgradeError, FullyNegotiatedInbound, @@ -408,15 +410,15 @@ where let mut removed_protocols = supported_protocols.difference(&new_protocols).peekable(); - if added_protocols.peek().is_some() { + if dbg!(added_protocols.peek()).is_some() { handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Removed(ProtocolsRemoved { + ProtocolsChange::Added(ProtocolsAdded { protocols: added_protocols, }), )); } - if removed_protocols.peek().is_some() { + if dbg!(removed_protocols.peek()).is_some() { handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( ProtocolsChange::Removed(ProtocolsRemoved { protocols: removed_protocols, @@ -425,6 +427,8 @@ where } } + *supported_protocols = new_protocols; + negotiating_in.push(SubstreamUpgrade::new_inbound(substream, protocol)); continue; // Go back to the top, handler can potentially make progress again. @@ -679,6 +683,7 @@ enum Shutdown { #[cfg(test)] mod tests { use super::*; + use crate::connection::supported_protocols::SupportedProtocols; use crate::keep_alive; use futures::future; use futures::AsyncRead; @@ -754,21 +759,54 @@ mod tests { None, 2, ); - connection.handler.active_protocols = vec!["/foo"]; + connection.handler.active_protocols = HashSet::from(["/foo"]); + + // DummyStreamMuxer will yield a new stream + let _ = Pin::new(&mut connection) + .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + assert_eq!( + connection + .handler + .supported_local_protocols + .iter() + .cloned() + .collect::>(), + HashSet::from(["/foo".to_owned()]) + ); + + connection.handler.active_protocols = HashSet::from(["/foo", "/bar"]); + connection.negotiating_in.clear(); // Hack to request more substreams from the muxer. // DummyStreamMuxer will yield a new stream let _ = Pin::new(&mut connection) .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); - assert_eq!(connection.handler.reported_protocols, vec!["/foo"]); - connection.handler.active_protocols = vec!["/foo", "/bar"]; + assert_eq!( + connection + .handler + .supported_local_protocols + .iter() + .cloned() + .collect::>(), + HashSet::from(["/bar".to_owned(), "/foo".to_owned()]) + ); + + connection.handler.active_protocols = HashSet::from(["/bar"]); connection.negotiating_in.clear(); // Hack to request more substreams from the muxer. // DummyStreamMuxer will yield a new stream let _ = Pin::new(&mut connection) .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); - assert_eq!(connection.handler.reported_protocols, vec!["/bar", "/foo"]) + assert_eq!( + connection + .handler + .supported_local_protocols + .iter() + .cloned() + .collect::>(), + HashSet::from(["/bar".to_owned()]) + ); } struct DummyStreamMuxer { @@ -890,8 +928,8 @@ mod tests { #[derive(Default)] struct ConfigurableProtocolConnectionHandler { - active_protocols: Vec<&'static str>, - reported_protocols: Vec, + active_protocols: HashSet<&'static str>, + supported_local_protocols: SupportedProtocols, } impl ConnectionHandler for MockConnectionHandler { @@ -982,7 +1020,7 @@ mod tests { ) -> SubstreamProtocol { SubstreamProtocol::new( ManyProtocolsUpgrade { - protocols: self.active_protocols.clone(), + protocols: Vec::from_iter(self.active_protocols.clone()), }, (), ) @@ -997,16 +1035,8 @@ mod tests { Self::OutboundOpenInfo, >, ) { - match event { - ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { - self.reported_protocols.extend(added.cloned()); - } - ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { - for protocol in removed { - self.reported_protocols.retain(|p| p != protocol); - } - } - _ => {} + if let ConnectionEvent::LocalProtocolsChange(change) = event { + self.supported_local_protocols.on_protocols_change(change); } } diff --git a/swarm/src/connection/supported_protocols.rs b/swarm/src/connection/supported_protocols.rs new file mode 100644 index 00000000000..d607c432d6e --- /dev/null +++ b/swarm/src/connection/supported_protocols.rs @@ -0,0 +1,26 @@ +use crate::handler::ProtocolsChange; +use std::collections::HashSet; + +#[derive(Default, Clone, Debug)] +pub struct SupportedProtocols { + protocols: HashSet, +} + +impl SupportedProtocols { + pub fn on_protocols_change(&mut self, change: ProtocolsChange) { + match change { + ProtocolsChange::Added(added) => { + self.protocols.extend(added.cloned()); + } + ProtocolsChange::Removed(removed) => { + for p in removed { + self.protocols.remove(p); + } + } + } + } + + pub fn iter(&self) -> impl Iterator { + self.protocols.iter() + } +} diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 20a73b6c350..523a20d1ece 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -118,7 +118,7 @@ pub use behaviour::{ }; #[allow(deprecated)] pub use connection::pool::{ConnectionCounters, ConnectionLimits}; -pub use connection::{ConnectionError, ConnectionId}; +pub use connection::{ConnectionError, ConnectionId, SupportedProtocols}; pub use executor::Executor; #[allow(deprecated)] pub use handler::IntoConnectionHandler; From f1328c529c3641736e50b959cb228b71ce4a34d0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 19 Apr 2023 19:46:01 +0200 Subject: [PATCH 011/105] Deduplicate code and propagate supported protocols only once --- swarm/src/connection.rs | 50 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 7bc66225258..a1249a71e3b 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -175,10 +175,18 @@ where /// and connection handler. pub fn new( muxer: StreamMuxerBox, - handler: THandler, + mut handler: THandler, substream_upgrade_protocol_override: Option, max_negotiating_inbound_streams: usize, ) -> Self { + let initial_protocols = gather_supported_protocols(&handler); + + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Added(ProtocolsAdded { + protocols: initial_protocols.difference(&HashSet::new()).peekable(), + }), + )); + Connection { muxing: muxer, handler, @@ -188,7 +196,7 @@ where substream_upgrade_protocol_override, max_negotiating_inbound_streams, requested_substreams: Default::default(), - supported_protocols: HashSet::new(), + supported_protocols: initial_protocols, } } @@ -221,21 +229,6 @@ where supported_protocols, } = self.get_mut(); - let protocol = handler.listen_protocol(); - - let new_protocols = protocol - .upgrade() - .protocol_info() - .filter_map(|i| String::from_utf8(i.protocol_name().to_vec()).ok()) - .collect::>(); - - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: new_protocols.difference(&HashSet::new()).peekable(), - }), - )); - *supported_protocols = new_protocols; - loop { match requested_substreams.poll_next_unpin(cx) { Poll::Ready(Some(Ok(()))) => continue, @@ -396,13 +389,7 @@ where match muxing.poll_inbound_unpin(cx)? { Poll::Pending => {} Poll::Ready(substream) => { - let protocol = handler.listen_protocol(); - - let new_protocols = protocol - .upgrade() - .protocol_info() - .filter_map(|i| String::from_utf8(i.protocol_name().to_vec()).ok()) - .collect::>(); + let new_protocols = gather_supported_protocols(handler); if &new_protocols != supported_protocols { let mut added_protocols = @@ -410,7 +397,7 @@ where let mut removed_protocols = supported_protocols.difference(&new_protocols).peekable(); - if dbg!(added_protocols.peek()).is_some() { + if added_protocols.peek().is_some() { handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( ProtocolsChange::Added(ProtocolsAdded { protocols: added_protocols, @@ -418,7 +405,7 @@ where )); } - if dbg!(removed_protocols.peek()).is_some() { + if removed_protocols.peek().is_some() { handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( ProtocolsChange::Removed(ProtocolsRemoved { protocols: removed_protocols, @@ -429,6 +416,8 @@ where *supported_protocols = new_protocols; + let protocol = handler.listen_protocol(); + negotiating_in.push(SubstreamUpgrade::new_inbound(substream, protocol)); continue; // Go back to the top, handler can potentially make progress again. @@ -441,6 +430,15 @@ where } } +fn gather_supported_protocols(handler: &impl ConnectionHandler) -> HashSet { + handler + .listen_protocol() + .upgrade() + .protocol_info() + .filter_map(|i| String::from_utf8(i.protocol_name().to_vec()).ok()) + .collect() +} + /// Borrowed information about an incoming connection currently being negotiated. #[derive(Debug, Copy, Clone)] pub struct IncomingInfo<'a> { From a4fbcda7426c460bc5db5dea6405795c072a7e1f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 19 Apr 2023 20:01:03 +0200 Subject: [PATCH 012/105] Extend docs --- swarm/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 84d186f0786..1f4b562dcb7 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -395,7 +395,7 @@ pub enum ConnectionHandlerEvent), /// The remote no longer supports these protocols. Removed(HashSet), From 572ed906bea582e574f08175137ad08629e6ff85 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 19 Apr 2023 22:14:32 +0200 Subject: [PATCH 013/105] Fix compile errors --- protocols/gossipsub/src/handler.rs | 6 +++--- swarm/src/behaviour.rs | 3 +++ swarm/src/handler.rs | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/protocols/gossipsub/src/handler.rs b/protocols/gossipsub/src/handler.rs index 5f32346f0b9..928989fa028 100644 --- a/protocols/gossipsub/src/handler.rs +++ b/protocols/gossipsub/src/handler.rs @@ -559,9 +559,9 @@ impl ConnectionHandler for Handler { log::debug!("Protocol negotiation failed: {e}") } ConnectionEvent::AddressChange(_) - | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::LocalProtocolsChange(_) - | ConnectionEvent::RemoteProtocolsChange(_) => {} + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } Handler::Disabled(_) => {} diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 9fd014bdc47..92604f8fb07 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -294,6 +294,9 @@ pub trait PollParameters { /// The iterator's elements are the ASCII names as reported on the wire. /// /// Note that the list is computed once at initialization and never refreshed. + #[deprecated( + note = "Use `libp2p_swarm::SupportedProtocols` in your `ConnectionHandler` instead." + )] fn supported_protocols(&self) -> Self::SupportedProtocolsIter; /// Returns the list of the addresses we're listening on. diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 40fc49673c4..3cb52d56d37 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -231,6 +231,8 @@ impl<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IOI, OOI> } ConnectionEvent::FullyNegotiatedInbound(_) | ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) | ConnectionEvent::ListenUpgradeError(_) => false, } } @@ -247,6 +249,8 @@ impl<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IOI, OOI> ConnectionEvent::FullyNegotiatedOutbound(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) | ConnectionEvent::DialUpgradeError(_) => false, } } From 586394b4dea5e549f35ad5b0bd8b7a22defbd254 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 26 Apr 2023 20:22:28 +0200 Subject: [PATCH 014/105] Report changes to listen/external address set --- Cargo.lock | 1 + swarm/Cargo.toml | 1 + swarm/src/behaviour/external_addresses.rs | 52 ++++++++++++++++++-- swarm/src/behaviour/listen_addresses.rs | 58 +++++++++++++++++++++-- 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ce59e1e8a4..9c88d592fc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2866,6 +2866,7 @@ dependencies = [ "libp2p-swarm-test", "libp2p-yamux", "log", + "once_cell", "quickcheck-ext", "rand 0.8.5", "smallvec", diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index 820fabc3aac..fe1fd93abe5 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -51,6 +51,7 @@ libp2p-swarm-test = { path = "../swarm-test" } libp2p-yamux = { path = "../muxers/yamux" } quickcheck = { package = "quickcheck-ext", path = "../misc/quickcheck-ext" } void = "1" +once_cell = "1.17.1" [[test]] name = "swarm_derive" diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 2090d4b3481..6f1d523ef37 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -32,21 +32,67 @@ impl ExternalAddresses { } /// Feed a [`FromSwarm`] event to this struct. + /// + /// Returns whether the event changed our set of external addresses. #[allow(deprecated)] - pub fn on_swarm_event(&mut self, event: &FromSwarm) + pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool where THandler: IntoConnectionHandler, { match event { FromSwarm::NewExternalAddr(NewExternalAddr { addr, .. }) => { if self.addresses.len() < self.limit { - self.addresses.insert((*addr).clone()); + return self.addresses.insert((*addr).clone()); } } FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr, .. }) => { - self.addresses.remove(addr); + return self.addresses.remove(addr) } _ => {} } + + false + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dummy; + use libp2p_core::multiaddr::Protocol; + use once_cell::sync::Lazy; + + #[test] + fn new_external_addr_returns_correct_changed_value() { + let mut addresses = ExternalAddresses::default(); + + let changed = addresses.on_swarm_event(&new_external_addr()); + assert!(changed); + + let changed = addresses.on_swarm_event(&new_external_addr()); + assert!(!changed) + } + + #[test] + fn expired_external_addr_returns_correct_changed_value() { + let mut addresses = ExternalAddresses::default(); + addresses.on_swarm_event(&new_external_addr()); + + let changed = addresses.on_swarm_event(&expired_external_addr()); + assert!(changed); + + let changed = addresses.on_swarm_event(&expired_external_addr()); + assert!(!changed) + } + + fn new_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::NewExternalAddr(NewExternalAddr { addr: &MEMORY_ADDR }) + } + + fn expired_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr: &MEMORY_ADDR }) } + + static MEMORY_ADDR: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000))); } diff --git a/swarm/src/behaviour/listen_addresses.rs b/swarm/src/behaviour/listen_addresses.rs index 07bd003bc8d..f2b61582850 100644 --- a/swarm/src/behaviour/listen_addresses.rs +++ b/swarm/src/behaviour/listen_addresses.rs @@ -17,19 +17,69 @@ impl ListenAddresses { } /// Feed a [`FromSwarm`] event to this struct. + /// + /// Returns whether the event changed our set of listen addresses. #[allow(deprecated)] - pub fn on_swarm_event(&mut self, event: &FromSwarm) + pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool where THandler: IntoConnectionHandler, { match event { FromSwarm::NewListenAddr(NewListenAddr { addr, .. }) => { - self.addresses.insert((*addr).clone()); + self.addresses.insert((*addr).clone()) } FromSwarm::ExpiredListenAddr(ExpiredListenAddr { addr, .. }) => { - self.addresses.remove(addr); + self.addresses.remove(addr) } - _ => {} + _ => false, } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::dummy; + use libp2p_core::multiaddr::Protocol; + use once_cell::sync::Lazy; + + #[test] + fn new_listen_addr_returns_correct_changed_value() { + let mut addresses = ListenAddresses::default(); + + let changed = addresses.on_swarm_event(&new_listen_addr()); + assert!(changed); + + let changed = addresses.on_swarm_event(&new_listen_addr()); + assert!(!changed) + } + + #[test] + fn expired_listen_addr_returns_correct_changed_value() { + let mut addresses = ListenAddresses::default(); + addresses.on_swarm_event(&new_listen_addr()); + + let changed = addresses.on_swarm_event(&expired_listen_addr()); + assert!(changed); + + let changed = addresses.on_swarm_event(&expired_listen_addr()); + assert!(!changed) + } + + fn new_listen_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::NewListenAddr(NewListenAddr { + listener_id: Default::default(), + addr: &MEMORY_ADDR, + }) + } + + fn expired_listen_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::ExpiredListenAddr(ExpiredListenAddr { + listener_id: Default::default(), + addr: &MEMORY_ADDR, + }) + } + + static MEMORY_ADDR: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000))); +} From b329a09422cf1ff85a994fe4467e23b98a2b425e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 26 Apr 2023 21:02:48 +0200 Subject: [PATCH 015/105] Simplify identify protocol --- protocols/identify/src/behaviour.rs | 155 ++++++++++------------------ protocols/identify/src/handler.rs | 92 +++++++---------- protocols/identify/src/protocol.rs | 8 -- 3 files changed, 87 insertions(+), 168 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index c5cbc12e8c6..7a6b6713b74 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE. use crate::handler::{self, Handler, InEvent}; -use crate::protocol::{Info, Protocol, UpgradeError}; +use crate::protocol::{Info, UpgradeError}; use libp2p_core::{multiaddr, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_identity::PublicKey; @@ -50,10 +50,6 @@ pub struct Behaviour { config: Config, /// For each peer we're connected to, the observed address to send back to it. connected: HashMap>, - /// Pending requests to be fulfilled, either `Handler` requests for `Behaviour` info - /// to address identification requests, or push requests to peers - /// with current information about the local peer. - requests: Vec, /// Pending events to be emitted when polled. events: VecDeque>, /// The addresses of all peers that we have discovered. @@ -63,15 +59,6 @@ pub struct Behaviour { external_addresses: ExternalAddresses, } -/// A `Behaviour` request to be fulfilled, either `Handler` requests for `Behaviour` info -/// to address identification requests, or push requests to peers -/// with current information about the local peer. -#[derive(Debug, PartialEq, Eq)] -struct Request { - peer_id: PeerId, - protocol: Protocol, -} - /// Configuration for the [`identify::Behaviour`](Behaviour). #[non_exhaustive] #[derive(Debug, Clone)] @@ -179,7 +166,6 @@ impl Behaviour { Self { config, connected: HashMap::new(), - requests: Vec::new(), events: VecDeque::new(), discovered_peers, listen_addresses: Default::default(), @@ -193,17 +179,14 @@ impl Behaviour { I: IntoIterator, { for p in peers { - let request = Request { + self.events.push_back(ToSwarm::Dial { + opts: DialOpts::peer_id(p).build(), + }); + self.events.push_back(ToSwarm::NotifyHandler { peer_id: p, - protocol: Protocol::Push, - }; - if !self.requests.contains(&request) { - self.requests.push(request); - - self.events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(p).build(), - }); - } + handler: NotifyHandler::Any, + event: InEvent::Push, + }); } } @@ -233,6 +216,14 @@ impl Behaviour { } } } + + fn all_addresses(&self) -> HashSet { + self.listen_addresses + .iter() + .chain(self.external_addresses.iter()) + .cloned() + .collect() + } } impl NetworkBehaviour for Behaviour { @@ -254,6 +245,7 @@ impl NetworkBehaviour for Behaviour { self.config.protocol_version.clone(), self.config.agent_version.clone(), remote_addr.clone(), + self.all_addresses(), )) } @@ -272,13 +264,14 @@ impl NetworkBehaviour for Behaviour { self.config.protocol_version.clone(), self.config.agent_version.clone(), addr.clone(), // TODO: This is weird? That is the public address we dialed, shouldn't need to tell the other party? + self.all_addresses(), )) } fn on_connection_handler_event( &mut self, peer_id: PeerId, - connection_id: ConnectionId, + _: ConnectionId, event: THandlerOutEvent, ) { match event { @@ -307,12 +300,6 @@ impl NetworkBehaviour for Behaviour { self.events .push_back(ToSwarm::GenerateEvent(Event::Pushed { peer_id })); } - handler::Event::Identify => { - self.requests.push(Request { - peer_id, - protocol: Protocol::Identify(connection_id), - }); - } handler::Event::IdentificationError(error) => { self.events .push_back(ToSwarm::GenerateEvent(Event::Error { peer_id, error })); @@ -329,42 +316,7 @@ impl NetworkBehaviour for Behaviour { return Poll::Ready(event); } - // Check for pending requests. - match self.requests.pop() { - Some(Request { - peer_id, - protocol: Protocol::Push, - }) => Poll::Ready(ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: InEvent { - listen_addrs: self - .listen_addresses - .iter() - .chain(self.external_addresses.iter()) - .cloned() - .collect(), - protocol: Protocol::Push, - }, - }), - Some(Request { - peer_id, - protocol: Protocol::Identify(connection_id), - }) => Poll::Ready(ToSwarm::NotifyHandler { - peer_id, - handler: NotifyHandler::One(connection_id), - event: InEvent { - listen_addrs: self - .listen_addresses - .iter() - .chain(self.external_addresses.iter()) - .cloned() - .collect(), - protocol: Protocol::Identify(connection_id), - }, - }), - None => Poll::Pending, - } + Poll::Pending } fn handle_pending_outbound_connection( @@ -383,8 +335,35 @@ impl NetworkBehaviour for Behaviour { } fn on_swarm_event(&mut self, event: FromSwarm) { - self.listen_addresses.on_swarm_event(&event); - self.external_addresses.on_swarm_event(&event); + let listen_addr_changed = self.listen_addresses.on_swarm_event(&event); + let external_addr_changed = self.external_addresses.on_swarm_event(&event); + + if listen_addr_changed || external_addr_changed { + // notify all connected handlers about our changed addresses + let change_events = self + .connected + .iter() + .flat_map(|(peer, map)| map.keys().map(|id| (*peer, id))) + .map(|(peer_id, connection_id)| ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::One(*connection_id), + event: InEvent::AddressesChanged(self.all_addresses()), + }) + .collect::>(); + + self.events.extend(change_events) + } + + if listen_addr_changed && self.config.push_listen_addr_updates { + // trigger an identify push for all connected peers + let push_events = self.connected.keys().map(|peer| ToSwarm::NotifyHandler { + peer_id: *peer, + handler: NotifyHandler::Any, + event: InEvent::Push, + }); + + self.events.extend(push_events); + } match event { FromSwarm::ConnectionEstablished(connection_established) => { @@ -398,30 +377,11 @@ impl NetworkBehaviour for Behaviour { }) => { if remaining_established == 0 { self.connected.remove(&peer_id); - self.requests.retain(|request| { - request - != &Request { - peer_id, - protocol: Protocol::Push, - } - }); } else if let Some(addrs) = self.connected.get_mut(&peer_id) { addrs.remove(&connection_id); } } FromSwarm::DialFailure(DialFailure { peer_id, error, .. }) => { - if let Some(peer_id) = peer_id { - if !self.connected.contains_key(&peer_id) { - self.requests.retain(|request| { - request - != &Request { - peer_id, - protocol: Protocol::Push, - } - }); - } - } - if let Some(entry) = peer_id.and_then(|id| self.discovered_peers.get_mut(&id)) { if let DialError::Transport(errors) = error { for (addr, _error) in errors { @@ -430,20 +390,9 @@ impl NetworkBehaviour for Behaviour { } } } - FromSwarm::NewListenAddr(_) | FromSwarm::ExpiredListenAddr(_) => { - if self.config.push_listen_addr_updates { - for p in self.connected.keys() { - let request = Request { - peer_id: *p, - protocol: Protocol::Push, - }; - if !self.requests.contains(&request) { - self.requests.push(request); - } - } - } - } - FromSwarm::AddressChange(_) + FromSwarm::NewListenAddr(_) + | FromSwarm::ExpiredListenAddr(_) + | FromSwarm::AddressChange(_) | FromSwarm::ListenFailure(_) | FromSwarm::NewListener(_) | FromSwarm::ListenerError(_) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 4904dfb07e4..0b1ac1d65ea 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -18,9 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -use crate::protocol::{ - self, Identify, InboundPush, Info, OutboundPush, Protocol, Push, UpgradeError, -}; +use crate::protocol::{Identify, InboundPush, Info, OutboundPush, Push, UpgradeError}; use either::Either; use futures::future::BoxFuture; use futures::prelude::*; @@ -36,11 +34,11 @@ use libp2p_swarm::handler::{ }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, - NegotiatedSubstream, SubstreamProtocol, SupportedProtocols, + SubstreamProtocol, SupportedProtocols, }; use log::warn; use smallvec::SmallVec; -use std::collections::{HashSet, VecDeque}; +use std::collections::HashSet; use std::{io, pin::Pin, task::Context, task::Poll, time::Duration}; /// Protocol handler for sending and receiving identification requests. @@ -56,9 +54,6 @@ pub struct Handler { [ConnectionHandlerEvent>, (), Event, io::Error>; 4], >, - /// Streams awaiting `BehaviourInfo` to then send identify requests. - reply_streams: VecDeque, - /// Pending identification replies, awaiting being sent. pending_replies: FuturesUnordered>>, @@ -87,16 +82,14 @@ pub struct Handler { local_supported_protocols: SupportedProtocols, remote_supported_protocols: HashSet, + external_addresses: HashSet, } /// An event from `Behaviour` with the information requested by the `Handler`. #[derive(Debug)] -pub struct InEvent { - /// The addresses that the peer is listening on. - pub listen_addrs: Vec, - - /// The protocol w.r.t. the information requested. - pub protocol: Protocol, +pub enum InEvent { + AddressesChanged(HashSet), + Push, } /// Event produced by the `Handler`. @@ -109,8 +102,6 @@ pub enum Event { Identification(PeerId), /// We actively pushed our identification information to the remote. IdentificationPushed, - /// We received a request for identification. - Identify, /// Failed to identify the remote, or to reply to an identification request. IdentificationError(ConnectionHandlerUpgrErr), } @@ -125,12 +116,12 @@ impl Handler { protocol_version: String, agent_version: String, observed_addr: Multiaddr, + external_addresses: HashSet, ) -> Self { Self { remote_peer_id, inbound_identify_push: Default::default(), events: SmallVec::new(), - reply_streams: VecDeque::new(), pending_replies: FuturesUnordered::new(), trigger_next_identify: Delay::new(initial_delay), keep_alive: KeepAlive::Yes, @@ -141,6 +132,7 @@ impl Handler { observed_addr, local_supported_protocols: SupportedProtocols::default(), remote_supported_protocols: HashSet::default(), + external_addresses: external_addresses, } } @@ -155,16 +147,14 @@ impl Handler { ) { match output { future::Either::Left(substream) => { - self.events - .push(ConnectionHandlerEvent::Custom(Event::Identify)); - if !self.reply_streams.is_empty() { - warn!( - "New inbound identify request from {} while a previous one \ - is still pending. Queueing the new one.", - self.remote_peer_id, - ); - } - self.reply_streams.push_back(substream); + let peer_id = self.remote_peer_id; + let info = self.build_info(); + + self.pending_replies.push(Box::pin(async move { + crate::protocol::send(substream, info).await?; + + Ok(peer_id) + })); } future::Either::Right(fut) => { if self.inbound_identify_push.replace(fut).is_some() { @@ -250,6 +240,17 @@ impl Handler { self.keep_alive = KeepAlive::No; self.trigger_next_identify.reset(self.interval); } + + fn build_info(&mut self) -> Info { + Info { + public_key: self.public_key.clone(), + protocol_version: self.protocol_version.clone(), + agent_version: self.agent_version.clone(), + listen_addrs: Vec::from_iter(self.external_addresses.iter().cloned()), + protocols: Vec::from_iter(self.local_supported_protocols.iter().cloned()), + observed_addr: self.observed_addr.clone(), + } + } } impl ConnectionHandler for Handler { @@ -265,41 +266,18 @@ impl ConnectionHandler for Handler { SubstreamProtocol::new(SelectUpgrade::new(Identify, Push::inbound()), ()) } - fn on_behaviour_event( - &mut self, - InEvent { - listen_addrs, - protocol, - }: Self::InEvent, - ) { - let info = Info { - public_key: self.public_key.clone(), - protocol_version: self.protocol_version.clone(), - agent_version: self.agent_version.clone(), - listen_addrs, - protocols: Vec::from_iter(self.local_supported_protocols.iter().cloned()), - observed_addr: self.observed_addr.clone(), - }; - - match protocol { - Protocol::Push => { + fn on_behaviour_event(&mut self, event: Self::InEvent) { + match dbg!(event) { + InEvent::AddressesChanged(addresses) => { + self.external_addresses = addresses; + } + InEvent::Push => { + let info = self.build_info(); self.events .push(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(Either::Right(Push::outbound(info)), ()), }); } - Protocol::Identify(_) => { - let substream = self - .reply_streams - .pop_front() - .expect("A BehaviourInfo reply should have a matching substream."); - let peer = self.remote_peer_id; - let fut = Box::pin(async move { - protocol::send(substream, info).await?; - Ok(peer) - }); - self.pending_replies.push(fut); - } } } diff --git a/protocols/identify/src/protocol.rs b/protocols/identify/src/protocol.rs index 1a10b591278..160cfbda5aa 100644 --- a/protocols/identify/src/protocol.rs +++ b/protocols/identify/src/protocol.rs @@ -28,7 +28,6 @@ use libp2p_core::{ }; use libp2p_identity as identity; use libp2p_identity::PublicKey; -use libp2p_swarm::ConnectionId; use log::{debug, trace}; use std::convert::TryFrom; use std::{io, iter, pin::Pin}; @@ -41,13 +40,6 @@ pub const PROTOCOL_NAME: &[u8; 14] = b"/ipfs/id/1.0.0"; pub const PUSH_PROTOCOL_NAME: &[u8; 19] = b"/ipfs/id/push/1.0.0"; -/// The type of the Substream protocol. -#[derive(Debug, PartialEq, Eq)] -pub enum Protocol { - Identify(ConnectionId), - Push, -} - /// Substream upgrade protocol for `/ipfs/id/1.0.0`. #[derive(Debug, Clone)] pub struct Identify; From a63af89ee941f1ef743e080609b9f7dde1eeb44f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 26 Apr 2023 21:07:28 +0200 Subject: [PATCH 016/105] Check for changes in inbound protocols on every poll --- swarm/src/connection.rs | 64 ++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index a1249a71e3b..397d1c196ec 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -389,33 +389,6 @@ where match muxing.poll_inbound_unpin(cx)? { Poll::Pending => {} Poll::Ready(substream) => { - let new_protocols = gather_supported_protocols(handler); - - if &new_protocols != supported_protocols { - let mut added_protocols = - new_protocols.difference(supported_protocols).peekable(); - let mut removed_protocols = - supported_protocols.difference(&new_protocols).peekable(); - - if added_protocols.peek().is_some() { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: added_protocols, - }), - )); - } - - if removed_protocols.peek().is_some() { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Removed(ProtocolsRemoved { - protocols: removed_protocols, - }), - )); - } - } - - *supported_protocols = new_protocols; - let protocol = handler.listen_protocol(); negotiating_in.push(SubstreamUpgrade::new_inbound(substream, protocol)); @@ -425,6 +398,32 @@ where } } + let new_protocols = gather_supported_protocols(handler); + + if &new_protocols != supported_protocols { + let mut added_protocols = new_protocols.difference(supported_protocols).peekable(); + let mut removed_protocols = + supported_protocols.difference(&new_protocols).peekable(); + + if added_protocols.peek().is_some() { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Added(ProtocolsAdded { + protocols: added_protocols, + }), + )); + } + + if removed_protocols.peek().is_some() { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Removed(ProtocolsRemoved { + protocols: removed_protocols, + }), + )); + } + } + + *supported_protocols = new_protocols; + return Poll::Pending; // Nothing can make progress, return `Pending`. } } @@ -750,16 +749,13 @@ mod tests { #[test] fn propagates_changes_to_supported_inbound_protocols() { let mut connection = Connection::new( - StreamMuxerBox::new(DummyStreamMuxer { - counter: Arc::new(()), - }), + StreamMuxerBox::new(PendingStreamMuxer), ConfigurableProtocolConnectionHandler::default(), None, - 2, + 0, ); connection.handler.active_protocols = HashSet::from(["/foo"]); - // DummyStreamMuxer will yield a new stream let _ = Pin::new(&mut connection) .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); assert_eq!( @@ -773,9 +769,7 @@ mod tests { ); connection.handler.active_protocols = HashSet::from(["/foo", "/bar"]); - connection.negotiating_in.clear(); // Hack to request more substreams from the muxer. - // DummyStreamMuxer will yield a new stream let _ = Pin::new(&mut connection) .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); @@ -790,9 +784,7 @@ mod tests { ); connection.handler.active_protocols = HashSet::from(["/bar"]); - connection.negotiating_in.clear(); // Hack to request more substreams from the muxer. - // DummyStreamMuxer will yield a new stream let _ = Pin::new(&mut connection) .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); From 72510dd1185eb9b372ac8a82d793e7be7fabe228 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 26 Apr 2023 21:09:00 +0200 Subject: [PATCH 017/105] Fix clippy lints --- protocols/identify/src/handler.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 0b1ac1d65ea..5a8d9c3bd79 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -108,6 +108,7 @@ pub enum Event { impl Handler { /// Creates a new `Handler`. + #[allow(clippy::too_many_arguments)] pub fn new( initial_delay: Duration, interval: Duration, @@ -132,7 +133,7 @@ impl Handler { observed_addr, local_supported_protocols: SupportedProtocols::default(), remote_supported_protocols: HashSet::default(), - external_addresses: external_addresses, + external_addresses, } } From 8ffafddb7a9fb7a2cf56d67f075e60c887159c28 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 26 Apr 2023 21:31:05 +0200 Subject: [PATCH 018/105] Report changes in `SupportedProtocols` --- swarm/Cargo.toml | 2 +- swarm/src/connection.rs | 8 +-- swarm/src/connection/supported_protocols.rs | 66 ++++++++++++++++++++- swarm/src/handler.rs | 19 ++++++ 4 files changed, 85 insertions(+), 10 deletions(-) diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index fe1fd93abe5..5e3a5e1e413 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -25,6 +25,7 @@ smallvec = "1.6.1" void = "1" wasm-bindgen-futures = { version = "0.4.34", optional = true } getrandom = { version = "0.2.9", features = ["js"], optional = true } # Explicit dependency to be used in `wasm-bindgen` feature +once_cell = "1.17.1" [target.'cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))'.dependencies] async-std = { version = "1.6.2", optional = true } @@ -51,7 +52,6 @@ libp2p-swarm-test = { path = "../swarm-test" } libp2p-yamux = { path = "../muxers/yamux" } quickcheck = { package = "quickcheck-ext", path = "../misc/quickcheck-ext" } void = "1" -once_cell = "1.17.1" [[test]] name = "swarm_derive" diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 397d1c196ec..53f8c196fdf 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -264,9 +264,7 @@ where ProtocolSupport::Added(protocols), )) => { handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: protocols.difference(&HashSet::new()).peekable(), // This is a bit of a hack to use the same type internally in `ProtocolsAdded`. - }), + ProtocolsChange::Added(ProtocolsAdded::from_set(&protocols)), )); continue; } @@ -274,9 +272,7 @@ where ProtocolSupport::Removed(protocols), )) => { handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange::Removed(ProtocolsRemoved { - protocols: protocols.difference(&HashSet::new()).peekable(), // This is a bit of a hack to use the same type internally in `ProtocolsRemoved`. - }), + ProtocolsChange::Removed(ProtocolsRemoved::from_set(&protocols)), )); continue; } diff --git a/swarm/src/connection/supported_protocols.rs b/swarm/src/connection/supported_protocols.rs index d607c432d6e..7da964dc9f8 100644 --- a/swarm/src/connection/supported_protocols.rs +++ b/swarm/src/connection/supported_protocols.rs @@ -7,15 +7,25 @@ pub struct SupportedProtocols { } impl SupportedProtocols { - pub fn on_protocols_change(&mut self, change: ProtocolsChange) { + pub fn on_protocols_change(&mut self, change: ProtocolsChange) -> bool { match change { ProtocolsChange::Added(added) => { - self.protocols.extend(added.cloned()); + let mut changed = false; + + for p in added { + changed |= self.protocols.insert(p.clone()); + } + + changed } ProtocolsChange::Removed(removed) => { + let mut changed = false; + for p in removed { - self.protocols.remove(p); + changed |= self.protocols.remove(p); } + + changed } } } @@ -24,3 +34,53 @@ impl SupportedProtocols { self.protocols.iter() } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::handler::{ProtocolsAdded, ProtocolsRemoved}; + use once_cell::sync::Lazy; + + #[test] + fn protocols_change_added_returns_correct_changed_value() { + let mut protocols = SupportedProtocols::default(); + + let changed = protocols.on_protocols_change(add_foo()); + assert!(changed); + + let changed = protocols.on_protocols_change(add_foo()); + assert!(!changed); + + let changed = protocols.on_protocols_change(add_foo_bar()); + assert!(changed); + } + + #[test] + fn protocols_change_removed_returns_correct_changed_value() { + let mut protocols = SupportedProtocols::default(); + + let changed = protocols.on_protocols_change(remove_foo()); + assert!(!changed); + + protocols.on_protocols_change(add_foo()); + + let changed = protocols.on_protocols_change(remove_foo()); + assert!(changed); + } + + fn add_foo() -> ProtocolsChange<'static> { + ProtocolsChange::Added(ProtocolsAdded::from_set(&FOO_PROTOCOLS)) + } + + fn add_foo_bar() -> ProtocolsChange<'static> { + ProtocolsChange::Added(ProtocolsAdded::from_set(&FOO_BAR_PROTOCOLS)) + } + + fn remove_foo() -> ProtocolsChange<'static> { + ProtocolsChange::Removed(ProtocolsRemoved::from_set(&FOO_PROTOCOLS)) + } + + static FOO_PROTOCOLS: Lazy> = Lazy::new(|| HashSet::from(["foo".to_string()])); + static FOO_BAR_PROTOCOLS: Lazy> = + Lazy::new(|| HashSet::from(["foo".to_string(), "bar".to_string()])); +} diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 3cb52d56d37..af9013c95aa 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -51,6 +51,7 @@ pub use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, U use instant::Instant; use libp2p_core::{upgrade::UpgradeError, ConnectedPoint, Multiaddr}; use libp2p_identity::PeerId; +use once_cell::sync::Lazy; use std::collections::hash_map::RandomState; use std::collections::hash_set::Difference; use std::collections::HashSet; @@ -295,11 +296,27 @@ pub struct ProtocolsAdded<'a> { pub(crate) protocols: Peekable>, } +impl<'a> ProtocolsAdded<'a> { + pub(crate) fn from_set(protocols: &'a HashSet) -> Self { + ProtocolsAdded { + protocols: protocols.difference(&EMPTY_HASHSET).peekable(), + } + } +} + #[derive(Clone)] pub struct ProtocolsRemoved<'a> { pub(crate) protocols: Peekable>, } +impl<'a> ProtocolsRemoved<'a> { + pub(crate) fn from_set(protocols: &'a HashSet) -> Self { + ProtocolsRemoved { + protocols: protocols.difference(&EMPTY_HASHSET).peekable(), + } + } +} + impl<'a> Iterator for ProtocolsAdded<'a> { type Item = &'a String; fn next(&mut self) -> Option { @@ -668,3 +685,5 @@ impl Ord for KeepAlive { } } } + +static EMPTY_HASHSET: Lazy> = Lazy::new(HashSet::new); From ea1a08736d5b8489d92217ae30e1e561377445b4 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 26 Apr 2023 21:35:06 +0200 Subject: [PATCH 019/105] Only report changes to handler if there actually was a change --- swarm/src/connection.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 53f8c196fdf..054e28524be 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -150,7 +150,8 @@ where SubstreamRequested, >, - supported_protocols: HashSet, + local_supported_protocols: HashSet, + remote_supported_protocols: SupportedProtocols, } impl fmt::Debug for Connection @@ -196,7 +197,8 @@ where substream_upgrade_protocol_override, max_negotiating_inbound_streams, requested_substreams: Default::default(), - supported_protocols: initial_protocols, + local_supported_protocols: initial_protocols, + remote_supported_protocols: SupportedProtocols::default(), } } @@ -226,7 +228,7 @@ where shutdown, max_negotiating_inbound_streams, substream_upgrade_protocol_override, - supported_protocols, + local_supported_protocols: supported_protocols, } = self.get_mut(); loop { @@ -263,17 +265,31 @@ where Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( ProtocolSupport::Added(protocols), )) => { - handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded::from_set(&protocols)), - )); + let change = ProtocolsChange::Added(ProtocolsAdded::from_set(&protocols)); + + if self + .remote_supported_protocols + .on_protocols_change(change.clone()) + { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); + // TODO: Should we optimise this to be the _actual_ change? + } + continue; } Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( ProtocolSupport::Removed(protocols), )) => { - handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange::Removed(ProtocolsRemoved::from_set(&protocols)), - )); + let change = ProtocolsChange::Removed(ProtocolsRemoved::from_set(&protocols)); + + if self + .remote_supported_protocols + .on_protocols_change(change.clone()) + { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); + // TODO: Should we optimise this to be the _actual_ change? + } + continue; } } From 27bc50748d138eed61ed64fe20eed3770f6811f1 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 Apr 2023 15:04:08 +0200 Subject: [PATCH 020/105] Do not implicitly dial peers upon push --- protocols/identify/src/behaviour.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 7a6b6713b74..d25ed2537f3 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -179,9 +179,11 @@ impl Behaviour { I: IntoIterator, { for p in peers { - self.events.push_back(ToSwarm::Dial { - opts: DialOpts::peer_id(p).build(), - }); + if !self.connected.contains_key(&p) { + log::debug!("Not pushing to {p} because we are not connected"); + continue; + } + self.events.push_back(ToSwarm::NotifyHandler { peer_id: p, handler: NotifyHandler::Any, From 74dd94a2fd388483430821f393353ee676bf87a6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 Apr 2023 15:08:43 +0200 Subject: [PATCH 021/105] Remove unused import --- protocols/identify/src/behaviour.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index d25ed2537f3..4fab772390c 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -25,9 +25,8 @@ use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p_swarm::{ - dial_opts::DialOpts, AddressScore, ConnectionDenied, ConnectionHandlerUpgrErr, DialError, - ExternalAddresses, ListenAddresses, NetworkBehaviour, NotifyHandler, PollParameters, - THandlerInEvent, ToSwarm, + AddressScore, ConnectionDenied, ConnectionHandlerUpgrErr, DialError, ExternalAddresses, + ListenAddresses, NetworkBehaviour, NotifyHandler, PollParameters, THandlerInEvent, ToSwarm, }; use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent}; use lru::LruCache; From 628b519919d4f8fc2f8243751e7a046d9676d5fa Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 Apr 2023 15:08:50 +0200 Subject: [PATCH 022/105] Fix compile error --- swarm/src/connection.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index a4055076c3a..1f90b6319a0 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -229,6 +229,7 @@ where max_negotiating_inbound_streams, substream_upgrade_protocol_override, local_supported_protocols: supported_protocols, + remote_supported_protocols, } = self.get_mut(); loop { @@ -267,10 +268,7 @@ where )) => { let change = ProtocolsChange::Added(ProtocolsAdded::from_set(&protocols)); - if self - .remote_supported_protocols - .on_protocols_change(change.clone()) - { + if remote_supported_protocols.on_protocols_change(change.clone()) { handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); // TODO: Should we optimise this to be the _actual_ change? } @@ -282,10 +280,7 @@ where )) => { let change = ProtocolsChange::Removed(ProtocolsRemoved::from_set(&protocols)); - if self - .remote_supported_protocols - .on_protocols_change(change.clone()) - { + if remote_supported_protocols.on_protocols_change(change.clone()) { handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); // TODO: Should we optimise this to be the _actual_ change? } From c7b5011691c639a7a12b44ec9ff7fdb64ce49ba8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 Apr 2023 15:12:45 +0200 Subject: [PATCH 023/105] Remove dbg! --- protocols/identify/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 5a8d9c3bd79..3b8460022d7 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -268,7 +268,7 @@ impl ConnectionHandler for Handler { } fn on_behaviour_event(&mut self, event: Self::InEvent) { - match dbg!(event) { + match event { InEvent::AddressesChanged(addresses) => { self.external_addresses = addresses; } From a02ca55dfe59d2e0535c79f13eb063d2f3e2aa9d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 27 Apr 2023 23:17:41 +1000 Subject: [PATCH 024/105] Update swarm/src/handler.rs Co-authored-by: Max Inden --- swarm/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index af9013c95aa..ae05424024e 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -215,7 +215,7 @@ pub enum ConnectionEvent<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IO DialUpgradeError(DialUpgradeError), /// Informs the handler that upgrading an inbound substream to the given protocol has failed. ListenUpgradeError(ListenUpgradeError), - /// The local [`ConnectionHandler`] now supports a different set of protocols. + /// The local [`ConnectionHandler`] added or removed support for one or more protocols. LocalProtocolsChange(ProtocolsChange<'a>), /// The remote [`ConnectionHandler`] now supports a different set of protocols. RemoteProtocolsChange(ProtocolsChange<'a>), From f3e5e71b788a9fe629778c2653de89077831397d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 14:40:44 +0100 Subject: [PATCH 025/105] Combine match arms where possible --- protocols/identify/src/handler.rs | 5 +++-- protocols/perf/src/client/handler.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 3b8460022d7..95f1576d461 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -358,11 +358,12 @@ impl ConnectionHandler for Handler { ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} ConnectionEvent::LocalProtocolsChange(change) => { self.local_supported_protocols.on_protocols_change(change); } - ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/perf/src/client/handler.rs b/protocols/perf/src/client/handler.rs index 96da11c2e24..33bc3fcd888 100644 --- a/protocols/perf/src/client/handler.rs +++ b/protocols/perf/src/client/handler.rs @@ -127,7 +127,9 @@ impl ConnectionHandler for Handler { .boxed(), ), - ConnectionEvent::AddressChange(_) | ConnectionEvent::LocalProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} ConnectionEvent::DialUpgradeError(DialUpgradeError { info: Command { id, .. }, error, @@ -147,7 +149,6 @@ impl ConnectionHandler for Handler { }, } } - ConnectionEvent::RemoteProtocolsChange(_) => {} } } From b7fa7effa07c86b9b20b0eb37a8df9dd231fcaa9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 14:46:25 +0100 Subject: [PATCH 026/105] Add comment explaining static hashset --- swarm/src/handler.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index ae05424024e..344eca3c246 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -686,4 +686,7 @@ impl Ord for KeepAlive { } } +/// A statically declared, empty [`HashSet`] allows us to work around borrow-checker rules for +/// [`ProtocolsAdded::from_set`] and [`ProtocolsRemoved::from_set`]. Those have lifetime-constraints +/// which don't work unless we have a [`HashSet`] with a `'static' lifetime. static EMPTY_HASHSET: Lazy> = Lazy::new(HashSet::new); From 2bd9d73fd0d1a41451ebe5961eb8be44966073b7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 14:48:36 +0100 Subject: [PATCH 027/105] Add docs --- swarm/src/handler.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 344eca3c246..28d2038ca7d 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -291,6 +291,7 @@ pub enum ProtocolsChange<'a> { Removed(ProtocolsRemoved<'a>), } +/// An [`Iterator`] over all protocols that have been added. #[derive(Clone)] pub struct ProtocolsAdded<'a> { pub(crate) protocols: Peekable>, @@ -304,6 +305,7 @@ impl<'a> ProtocolsAdded<'a> { } } +/// An [`Iterator`] over all protocols that have been removed. #[derive(Clone)] pub struct ProtocolsRemoved<'a> { pub(crate) protocols: Peekable>, From e90c40de79eb249a2e43177eb14ed36ac2ad9cc0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 15:00:06 +0100 Subject: [PATCH 028/105] Update supported protocols for push messages --- protocols/identify/src/handler.rs | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 95f1576d461..6208e6a923b 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -180,38 +180,12 @@ impl Handler { ) { match output { future::Either::Left(remote_info) => { - let new_remote_protocols = HashSet::from_iter(remote_info.protocols.clone()); - - let remote_added_protocols = new_remote_protocols - .difference(&self.remote_supported_protocols) - .cloned() - .collect::>(); - let remote_removed_protocols = self - .remote_supported_protocols - .difference(&new_remote_protocols) - .cloned() - .collect::>(); - - if !remote_added_protocols.is_empty() { - self.events - .push(ConnectionHandlerEvent::ReportRemoteProtocols( - ProtocolSupport::Added(remote_added_protocols), - )); - } - - if !remote_removed_protocols.is_empty() { - self.events - .push(ConnectionHandlerEvent::ReportRemoteProtocols( - ProtocolSupport::Removed(remote_removed_protocols), - )); - } - - self.remote_supported_protocols = new_remote_protocols; - + self.update_supported_protocols_for_remote(&remote_info); self.events .push(ConnectionHandlerEvent::Custom(Event::Identified( remote_info, ))); + self.keep_alive = KeepAlive::No; } future::Either::Right(()) => self @@ -252,6 +226,36 @@ impl Handler { observed_addr: self.observed_addr.clone(), } } + + fn update_supported_protocols_for_remote(&mut self, remote_info: &Info) { + let new_remote_protocols = HashSet::from_iter(remote_info.protocols.clone()); + + let remote_added_protocols = new_remote_protocols + .difference(&self.remote_supported_protocols) + .cloned() + .collect::>(); + let remote_removed_protocols = self + .remote_supported_protocols + .difference(&new_remote_protocols) + .cloned() + .collect::>(); + + if !remote_added_protocols.is_empty() { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Added(remote_added_protocols), + )); + } + + if !remote_removed_protocols.is_empty() { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Removed(remote_removed_protocols), + )); + } + + self.remote_supported_protocols = new_remote_protocols; + } } impl ConnectionHandler for Handler { @@ -316,11 +320,7 @@ impl ConnectionHandler for Handler { self.inbound_identify_push.take(); if let Ok(info) = res { - // TODO: report new protocols - // self.events - // .push(ConnectionHandlerEvent::ReportRemoteProtocols { - // protocols: info.protocols.clone(), - // }); + self.update_supported_protocols_for_remote(&info); return Poll::Ready(ConnectionHandlerEvent::Custom(Event::Identified(info))); } } From 84979e45e50ebbbff8f2e87995603f2b8cbfb59b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 17:57:52 +0200 Subject: [PATCH 029/105] Use `pop` to avoid panicking branch in `remove` --- protocols/identify/src/handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 6208e6a923b..83bc88460b0 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -296,8 +296,8 @@ impl ConnectionHandler for Handler { ) -> Poll< ConnectionHandlerEvent, > { - if !self.events.is_empty() { - return Poll::Ready(self.events.remove(0)); + if let Some(event) = self.events.pop() { + return Poll::Ready(event); } // Poll the future that fires when we need to identify the node again. From dbfc7e7082047ed0c5e09885446aea74fa8ddc6e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 17:58:24 +0200 Subject: [PATCH 030/105] Use `poll_unpin` --- protocols/identify/src/handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 83bc88460b0..16362f75898 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -39,7 +39,7 @@ use libp2p_swarm::{ use log::warn; use smallvec::SmallVec; use std::collections::HashSet; -use std::{io, pin::Pin, task::Context, task::Poll, time::Duration}; +use std::{io, task::Context, task::Poll, time::Duration}; /// Protocol handler for sending and receiving identification requests. /// @@ -301,7 +301,7 @@ impl ConnectionHandler for Handler { } // Poll the future that fires when we need to identify the node again. - match Future::poll(Pin::new(&mut self.trigger_next_identify), cx) { + match self.trigger_next_identify.poll_unpin(cx) { Poll::Pending => {} Poll::Ready(()) => { self.trigger_next_identify.reset(self.interval); From f2d2c886a06d86004990c8efccc9545705cb49d8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 17:58:48 +0200 Subject: [PATCH 031/105] Use `if let` for consistency --- protocols/identify/src/handler.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 16362f75898..77633abf059 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -301,15 +301,12 @@ impl ConnectionHandler for Handler { } // Poll the future that fires when we need to identify the node again. - match self.trigger_next_identify.poll_unpin(cx) { - Poll::Pending => {} - Poll::Ready(()) => { - self.trigger_next_identify.reset(self.interval); - let ev = ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(Either::Left(Identify), ()), - }; - return Poll::Ready(ev); - } + if let Poll::Ready(()) = self.trigger_next_identify.poll_unpin(cx) { + self.trigger_next_identify.reset(self.interval); + let ev = ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(Either::Left(Identify), ()), + }; + return Poll::Ready(ev); } if let Some(Poll::Ready(res)) = self From b41aeb83348eeeaeee0b34f4352c400e5a2fb3b9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 18:01:58 +0200 Subject: [PATCH 032/105] Rewrite to `if let` for consistency --- protocols/identify/src/handler.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 77633abf059..966339cc612 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -323,17 +323,17 @@ impl ConnectionHandler for Handler { } // Check for pending replies to send. - match self.pending_replies.poll_next_unpin(cx) { - Poll::Ready(Some(Ok(peer_id))) => Poll::Ready(ConnectionHandlerEvent::Custom( - Event::Identification(peer_id), - )), - Poll::Ready(Some(Err(err))) => Poll::Ready(ConnectionHandlerEvent::Custom( + if let Poll::Ready(Some(result)) = self.pending_replies.poll_next_unpin(cx) { + let event = result.map(Event::Identification).unwrap_or_else(|err| { Event::IdentificationError(ConnectionHandlerUpgrErr::Upgrade( - libp2p_core::upgrade::UpgradeError::Apply(err), - )), - )), - Poll::Ready(None) | Poll::Pending => Poll::Pending, + libp2p_core::UpgradeError::Apply(err), + )) + }); + + return Poll::Ready(ConnectionHandlerEvent::Custom(event)); } + + Poll::Pending } fn on_connection_event( From bcd872b002fc9ce25a6ca671039db704b6cf3313 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 2 May 2023 18:03:26 +0200 Subject: [PATCH 033/105] Fill in todo in toggle --- swarm/src/behaviour/toggle.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/swarm/src/behaviour/toggle.rs b/swarm/src/behaviour/toggle.rs index b6533411250..4dcbae1609a 100644 --- a/swarm/src/behaviour/toggle.rs +++ b/swarm/src/behaviour/toggle.rs @@ -368,11 +368,15 @@ where ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { self.on_listen_upgrade_error(listen_upgrade_error) } - ConnectionEvent::LocalProtocolsChange(_) => { - todo!() + ConnectionEvent::LocalProtocolsChange(change) => { + if let Some(inner) = self.inner.as_mut() { + inner.on_connection_event(ConnectionEvent::LocalProtocolsChange(change)); + } } - ConnectionEvent::RemoteProtocolsChange(_) => { - todo!() + ConnectionEvent::RemoteProtocolsChange(change) => { + if let Some(inner) = self.inner.as_mut() { + inner.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); + } } } } From 82642b82e42104b050b6ac5259f27ca5c51c7d60 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 10:41:51 +0200 Subject: [PATCH 034/105] Restore `libp2p-kad` --- Cargo.lock | 3 -- protocols/kad/Cargo.toml | 4 -- protocols/kad/src/behaviour.rs | 30 +---------- protocols/kad/src/handler_priv.rs | 53 +++++-------------- protocols/kad/src/lib.rs | 2 +- protocols/kad/tests/client_mode.rs | 84 ------------------------------ 6 files changed, 16 insertions(+), 160 deletions(-) delete mode 100644 protocols/kad/tests/client_mode.rs diff --git a/Cargo.lock b/Cargo.lock index 69edcc13f4b..04ea1bd39e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2580,7 +2580,6 @@ name = "libp2p-kad" version = "0.44.0" dependencies = [ "arrayvec", - "async-std", "asynchronous-codec", "bytes", "either", @@ -2590,11 +2589,9 @@ dependencies = [ "futures-timer", "instant", "libp2p-core", - "libp2p-identify", "libp2p-identity", "libp2p-noise", "libp2p-swarm", - "libp2p-swarm-test", "libp2p-yamux", "log", "quick-protobuf", diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index defeeb53fe1..561d6e4c424 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -34,13 +34,9 @@ serde = { version = "1.0", optional = true, features = ["derive"] } thiserror = "1" [dev-dependencies] -async-std = { version = "1.12.0", features = ["attributes"] } env_logger = "0.10.0" futures-timer = "3.0" -libp2p-identify = { path = "../identify" } libp2p-noise = { workspace = true } -libp2p-swarm = { path = "../../swarm", features = ["macros"] } -libp2p-swarm-test = { path = "../../swarm-test" } libp2p-yamux = { workspace = true } quickcheck = { workspace = true } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 4876cf45a3e..ed161d7a6ce 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -116,7 +116,6 @@ pub struct Kademlia { /// The record storage. store: TStore, - mode: Mode, } /// The configurable strategies for the insertion of peers @@ -183,7 +182,6 @@ pub struct KademliaConfig { connection_idle_timeout: Duration, kbucket_inserts: KademliaBucketInserts, caching: KademliaCaching, - mode: Mode, } impl Default for KademliaConfig { @@ -201,7 +199,6 @@ impl Default for KademliaConfig { connection_idle_timeout: Duration::from_secs(10), kbucket_inserts: KademliaBucketInserts::OnConnected, caching: KademliaCaching::Enabled { max_peers: 1 }, - mode: Mode::Server, } } } @@ -402,14 +399,6 @@ impl KademliaConfig { self.caching = c; self } - - /// Sets the mode. - /// - /// TODO: More docs. - pub fn set_mode(&mut self, m: Mode) -> &mut Self { - self.mode = m; - self - } } impl Kademlia @@ -464,7 +453,6 @@ where connection_idle_timeout: config.connection_idle_timeout, external_addresses: Default::default(), local_peer_id: id, - mode: config.mode, } } @@ -1990,7 +1978,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: self.mode == Mode::Server, + allow_listening: true, idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Listener { @@ -2011,7 +1999,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: self.mode == Mode::Server, + allow_listening: true, idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Dialer { @@ -2076,14 +2064,6 @@ where self.connection_updated(source, address, NodeStatus::Connected); } - KademliaHandlerEvent::ProtocolNotSupported { endpoint } => { - let address = match endpoint { - ConnectedPoint::Dialer { address, .. } => Some(address), - ConnectedPoint::Listener { .. } => None, - }; - self.connection_updated(source, address, NodeStatus::Disconnected); - } - KademliaHandlerEvent::FindNodeReq { key, request_id } => { let closer_peers = self.find_closest(&kbucket_priv::Key::new(key), &source); @@ -3212,9 +3192,3 @@ pub enum RoutingUpdate { /// peer ID). Failed, } - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Mode { - Client, - Server, -} diff --git a/protocols/kad/src/handler_priv.rs b/protocols/kad/src/handler_priv.rs index a3aced0b235..7a8d5e44515 100644 --- a/protocols/kad/src/handler_priv.rs +++ b/protocols/kad/src/handler_priv.rs @@ -31,14 +31,13 @@ use libp2p_core::{upgrade, ConnectedPoint}; use libp2p_identity::PeerId; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, - ProtocolsChange, }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, - NegotiatedSubstream, StreamProtocol, SubstreamProtocol, + NegotiatedSubstream, SubstreamProtocol, }; use log::trace; -use std::collections::{HashSet, VecDeque}; +use std::collections::VecDeque; use std::task::Waker; use std::{ error, fmt, io, marker::PhantomData, pin::Pin, task::Context, task::Poll, time::Duration, @@ -85,8 +84,6 @@ pub struct KademliaHandler { /// The current state of protocol confirmation. protocol_status: ProtocolStatus, - - remote_supported_protocols: HashSet, } /// The states of protocol confirmation that a connection @@ -94,12 +91,10 @@ pub struct KademliaHandler { enum ProtocolStatus { /// It is as yet unknown whether the remote supports the /// configured protocol name. - Unknown, + Unconfirmed, /// The configured protocol name has been confirmed by the remote /// but has not yet been reported to the `Kademlia` behaviour. Confirmed, - /// The configured protocol name(s) are not or no longer supported by the remote. - NotSupported, /// The configured protocol has been confirmed by the remote /// and the confirmation reported to the `Kademlia` behaviour. Reported, @@ -230,11 +225,13 @@ impl InboundSubstreamState { #[derive(Debug)] pub enum KademliaHandlerEvent { /// The configured protocol name has been confirmed by the peer through - /// a successfully negotiated substream or by learning the supported protocols of the remote. + /// a successfully negotiated substream. + /// + /// This event is only emitted once by a handler upon the first + /// successfully negotiated inbound or outbound substream and + /// indicates that the connected peer participates in the Kademlia + /// overlay network identified by the configured protocol name. ProtocolConfirmed { endpoint: ConnectedPoint }, - /// The configured protocol name(s) are not or no longer supported by the peer on the provided - /// connection and it should be removed from the routing table. - ProtocolNotSupported { endpoint: ConnectedPoint }, /// Request for the list of nodes whose IDs are the closest to `key`. The number of nodes /// returned is not specified, but should be around 20. @@ -503,8 +500,7 @@ where num_requested_outbound_streams: 0, pending_messages: Default::default(), keep_alive, - protocol_status: ProtocolStatus::Unknown, - remote_supported_protocols: Default::default(), + protocol_status: ProtocolStatus::Unconfirmed, } } @@ -526,7 +522,7 @@ where self.num_requested_outbound_streams -= 1; - if let ProtocolStatus::Unknown = self.protocol_status { + if let ProtocolStatus::Unconfirmed = self.protocol_status { // Upon the first successfully negotiated substream, we know that the // remote is configured with the same protocol name and we want // the behaviour to add this peer to the routing table, if possible. @@ -548,7 +544,7 @@ where future::Either::Right(p) => void::unreachable(p), }; - if let ProtocolStatus::Unknown = self.protocol_status { + if let ProtocolStatus::Unconfirmed = self.protocol_status { // Upon the first successfully negotiated substream, we know that the // remote is configured with the same protocol name and we want // the behaviour to add this peer to the routing table, if possible. @@ -781,30 +777,7 @@ where ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) - | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::LocalProtocolsChange(_) => {} - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(added)) => { - for p in added { - self.remote_supported_protocols.insert(p.to_owned()); - } - } - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(removed)) => { - for p in removed { - self.remote_supported_protocols.remove(p); - } - } - } - - let remote_supports_our_kademlia_protocols = self - .remote_supported_protocols - .iter() - .any(|p| self.config.protocol_config.protocol_names().contains(p)); - - if remote_supports_our_kademlia_protocols { - self.protocol_status = ProtocolStatus::Confirmed; - } else { - self.protocol_status = ProtocolStatus::NotSupported; + ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::LocalProtocolsChange(_) | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index 085c1f13c68..c3a705900d8 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -89,7 +89,7 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, + GetRecordOk, GetRecordResult, InboundRequest, NoKnownPeers, PeerRecord, PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, QueryResult, QueryStats, RoutingUpdate, }; diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs deleted file mode 100644 index c2ddd9aa555..00000000000 --- a/protocols/kad/tests/client_mode.rs +++ /dev/null @@ -1,84 +0,0 @@ -use libp2p_identify as identify; -use libp2p_identity as identity; -use libp2p_kad::store::MemoryStore; -use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent, Mode}; -use libp2p_swarm::Swarm; -use libp2p_swarm_test::SwarmExt; - -#[async_std::test] -async fn connection_to_node_in_client_mode_does_not_update_routing_table() { - let mut client = Swarm::new_ephemeral(MyBehaviour::client); - let mut server = Swarm::new_ephemeral(MyBehaviour::server); - - server.listen().await; - client.connect(&mut server).await; - - let server_peer_id = *server.local_peer_id(); - - match libp2p_swarm_test::drive(&mut client, &mut server).await { - ( - [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer, .. })], - [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_)], - ) => { - assert_eq!(peer, server_peer_id) - } - other => panic!("Unexpected events: {other:?}"), - } -} - -#[async_std::test] -async fn two_servers_add_each_other_to_routing_table() { - let mut server1 = Swarm::new_ephemeral(MyBehaviour::server); - let mut server2 = Swarm::new_ephemeral(MyBehaviour::server); - - server1.listen().await; - server2.listen().await; - - server1.connect(&mut server2).await; - - let server1_peer_id = *server1.local_peer_id(); - let server2_peer_id = *server2.local_peer_id(); - - match libp2p_swarm_test::drive(&mut server1, &mut server2).await { - ( - [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer: peer1, .. })], - [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::UnroutablePeer { peer: peer2, .. })], // Unroutable because server2 did not dial. - ) => { - assert_eq!(peer1, server2_peer_id); - assert_eq!(peer2, server1_peer_id); - } - other => panic!("Unexpected events: {other:?}"), - } -} - -#[derive(libp2p_swarm::NetworkBehaviour)] -#[behaviour(prelude = "libp2p_swarm::derive_prelude")] -struct MyBehaviour { - identify: identify::Behaviour, - kad: Kademlia, -} - -impl MyBehaviour { - fn client(k: identity::Keypair) -> Self { - let mut config = KademliaConfig::default(); - config.set_mode(Mode::Client); - - Self::with_config(k, config) - } - - fn server(k: identity::Keypair) -> Self { - Self::with_config(k, KademliaConfig::default()) - } - - fn with_config(k: identity::Keypair, config: KademliaConfig) -> MyBehaviour { - let local_peer_id = k.public().to_peer_id(); - - Self { - identify: identify::Behaviour::new(identify::Config::new( - "/test/1.0.0".to_owned(), - k.public(), - )), - kad: Kademlia::with_config(local_peer_id, MemoryStore::new(local_peer_id), config), - } - } -} From eb6648901b76c4c8d73b3fa586f7439a5ba3afc8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 10:44:46 +0200 Subject: [PATCH 035/105] Fix formatting --- protocols/kad/src/handler_priv.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/kad/src/handler_priv.rs b/protocols/kad/src/handler_priv.rs index 7a8d5e44515..d8ec0e36595 100644 --- a/protocols/kad/src/handler_priv.rs +++ b/protocols/kad/src/handler_priv.rs @@ -777,7 +777,10 @@ where ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { self.on_dial_upgrade_error(dial_upgrade_error) } - ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::LocalProtocolsChange(_) | ConnectionEvent::RemoteProtocolsChange(_) => {} + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} } } } From e9e5d24218791824da548e9032b2de5ad57a1190 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 10:45:26 +0200 Subject: [PATCH 036/105] Implement kademlia client-mode --- protocols/kad/Cargo.toml | 4 ++ protocols/kad/src/behaviour.rs | 30 ++++++++++- protocols/kad/src/handler_priv.rs | 52 +++++++++++++----- protocols/kad/src/lib.rs | 2 +- protocols/kad/tests/client_mode.rs | 84 ++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 protocols/kad/tests/client_mode.rs diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 561d6e4c424..defeeb53fe1 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -34,9 +34,13 @@ serde = { version = "1.0", optional = true, features = ["derive"] } thiserror = "1" [dev-dependencies] +async-std = { version = "1.12.0", features = ["attributes"] } env_logger = "0.10.0" futures-timer = "3.0" +libp2p-identify = { path = "../identify" } libp2p-noise = { workspace = true } +libp2p-swarm = { path = "../../swarm", features = ["macros"] } +libp2p-swarm-test = { path = "../../swarm-test" } libp2p-yamux = { workspace = true } quickcheck = { workspace = true } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index ed161d7a6ce..4876cf45a3e 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -116,6 +116,7 @@ pub struct Kademlia { /// The record storage. store: TStore, + mode: Mode, } /// The configurable strategies for the insertion of peers @@ -182,6 +183,7 @@ pub struct KademliaConfig { connection_idle_timeout: Duration, kbucket_inserts: KademliaBucketInserts, caching: KademliaCaching, + mode: Mode, } impl Default for KademliaConfig { @@ -199,6 +201,7 @@ impl Default for KademliaConfig { connection_idle_timeout: Duration::from_secs(10), kbucket_inserts: KademliaBucketInserts::OnConnected, caching: KademliaCaching::Enabled { max_peers: 1 }, + mode: Mode::Server, } } } @@ -399,6 +402,14 @@ impl KademliaConfig { self.caching = c; self } + + /// Sets the mode. + /// + /// TODO: More docs. + pub fn set_mode(&mut self, m: Mode) -> &mut Self { + self.mode = m; + self + } } impl Kademlia @@ -453,6 +464,7 @@ where connection_idle_timeout: config.connection_idle_timeout, external_addresses: Default::default(), local_peer_id: id, + mode: config.mode, } } @@ -1978,7 +1990,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: true, + allow_listening: self.mode == Mode::Server, idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Listener { @@ -1999,7 +2011,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: true, + allow_listening: self.mode == Mode::Server, idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Dialer { @@ -2064,6 +2076,14 @@ where self.connection_updated(source, address, NodeStatus::Connected); } + KademliaHandlerEvent::ProtocolNotSupported { endpoint } => { + let address = match endpoint { + ConnectedPoint::Dialer { address, .. } => Some(address), + ConnectedPoint::Listener { .. } => None, + }; + self.connection_updated(source, address, NodeStatus::Disconnected); + } + KademliaHandlerEvent::FindNodeReq { key, request_id } => { let closer_peers = self.find_closest(&kbucket_priv::Key::new(key), &source); @@ -3192,3 +3212,9 @@ pub enum RoutingUpdate { /// peer ID). Failed, } + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Mode { + Client, + Server, +} diff --git a/protocols/kad/src/handler_priv.rs b/protocols/kad/src/handler_priv.rs index d8ec0e36595..a3aced0b235 100644 --- a/protocols/kad/src/handler_priv.rs +++ b/protocols/kad/src/handler_priv.rs @@ -31,13 +31,14 @@ use libp2p_core::{upgrade, ConnectedPoint}; use libp2p_identity::PeerId; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, + ProtocolsChange, }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, - NegotiatedSubstream, SubstreamProtocol, + NegotiatedSubstream, StreamProtocol, SubstreamProtocol, }; use log::trace; -use std::collections::VecDeque; +use std::collections::{HashSet, VecDeque}; use std::task::Waker; use std::{ error, fmt, io, marker::PhantomData, pin::Pin, task::Context, task::Poll, time::Duration, @@ -84,6 +85,8 @@ pub struct KademliaHandler { /// The current state of protocol confirmation. protocol_status: ProtocolStatus, + + remote_supported_protocols: HashSet, } /// The states of protocol confirmation that a connection @@ -91,10 +94,12 @@ pub struct KademliaHandler { enum ProtocolStatus { /// It is as yet unknown whether the remote supports the /// configured protocol name. - Unconfirmed, + Unknown, /// The configured protocol name has been confirmed by the remote /// but has not yet been reported to the `Kademlia` behaviour. Confirmed, + /// The configured protocol name(s) are not or no longer supported by the remote. + NotSupported, /// The configured protocol has been confirmed by the remote /// and the confirmation reported to the `Kademlia` behaviour. Reported, @@ -225,13 +230,11 @@ impl InboundSubstreamState { #[derive(Debug)] pub enum KademliaHandlerEvent { /// The configured protocol name has been confirmed by the peer through - /// a successfully negotiated substream. - /// - /// This event is only emitted once by a handler upon the first - /// successfully negotiated inbound or outbound substream and - /// indicates that the connected peer participates in the Kademlia - /// overlay network identified by the configured protocol name. + /// a successfully negotiated substream or by learning the supported protocols of the remote. ProtocolConfirmed { endpoint: ConnectedPoint }, + /// The configured protocol name(s) are not or no longer supported by the peer on the provided + /// connection and it should be removed from the routing table. + ProtocolNotSupported { endpoint: ConnectedPoint }, /// Request for the list of nodes whose IDs are the closest to `key`. The number of nodes /// returned is not specified, but should be around 20. @@ -500,7 +503,8 @@ where num_requested_outbound_streams: 0, pending_messages: Default::default(), keep_alive, - protocol_status: ProtocolStatus::Unconfirmed, + protocol_status: ProtocolStatus::Unknown, + remote_supported_protocols: Default::default(), } } @@ -522,7 +526,7 @@ where self.num_requested_outbound_streams -= 1; - if let ProtocolStatus::Unconfirmed = self.protocol_status { + if let ProtocolStatus::Unknown = self.protocol_status { // Upon the first successfully negotiated substream, we know that the // remote is configured with the same protocol name and we want // the behaviour to add this peer to the routing table, if possible. @@ -544,7 +548,7 @@ where future::Either::Right(p) => void::unreachable(p), }; - if let ProtocolStatus::Unconfirmed = self.protocol_status { + if let ProtocolStatus::Unknown = self.protocol_status { // Upon the first successfully negotiated substream, we know that the // remote is configured with the same protocol name and we want // the behaviour to add this peer to the routing table, if possible. @@ -779,8 +783,28 @@ where } ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) - | ConnectionEvent::LocalProtocolsChange(_) - | ConnectionEvent::RemoteProtocolsChange(_) => {} + | ConnectionEvent::LocalProtocolsChange(_) => {} + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(added)) => { + for p in added { + self.remote_supported_protocols.insert(p.to_owned()); + } + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(removed)) => { + for p in removed { + self.remote_supported_protocols.remove(p); + } + } + } + + let remote_supports_our_kademlia_protocols = self + .remote_supported_protocols + .iter() + .any(|p| self.config.protocol_config.protocol_names().contains(p)); + + if remote_supports_our_kademlia_protocols { + self.protocol_status = ProtocolStatus::Confirmed; + } else { + self.protocol_status = ProtocolStatus::NotSupported; } } } diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index c3a705900d8..085c1f13c68 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -89,7 +89,7 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, NoKnownPeers, PeerRecord, PutRecordContext, + GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, QueryResult, QueryStats, RoutingUpdate, }; diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs new file mode 100644 index 00000000000..c2ddd9aa555 --- /dev/null +++ b/protocols/kad/tests/client_mode.rs @@ -0,0 +1,84 @@ +use libp2p_identify as identify; +use libp2p_identity as identity; +use libp2p_kad::store::MemoryStore; +use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent, Mode}; +use libp2p_swarm::Swarm; +use libp2p_swarm_test::SwarmExt; + +#[async_std::test] +async fn connection_to_node_in_client_mode_does_not_update_routing_table() { + let mut client = Swarm::new_ephemeral(MyBehaviour::client); + let mut server = Swarm::new_ephemeral(MyBehaviour::server); + + server.listen().await; + client.connect(&mut server).await; + + let server_peer_id = *server.local_peer_id(); + + match libp2p_swarm_test::drive(&mut client, &mut server).await { + ( + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer, .. })], + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_)], + ) => { + assert_eq!(peer, server_peer_id) + } + other => panic!("Unexpected events: {other:?}"), + } +} + +#[async_std::test] +async fn two_servers_add_each_other_to_routing_table() { + let mut server1 = Swarm::new_ephemeral(MyBehaviour::server); + let mut server2 = Swarm::new_ephemeral(MyBehaviour::server); + + server1.listen().await; + server2.listen().await; + + server1.connect(&mut server2).await; + + let server1_peer_id = *server1.local_peer_id(); + let server2_peer_id = *server2.local_peer_id(); + + match libp2p_swarm_test::drive(&mut server1, &mut server2).await { + ( + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer: peer1, .. })], + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::UnroutablePeer { peer: peer2, .. })], // Unroutable because server2 did not dial. + ) => { + assert_eq!(peer1, server2_peer_id); + assert_eq!(peer2, server1_peer_id); + } + other => panic!("Unexpected events: {other:?}"), + } +} + +#[derive(libp2p_swarm::NetworkBehaviour)] +#[behaviour(prelude = "libp2p_swarm::derive_prelude")] +struct MyBehaviour { + identify: identify::Behaviour, + kad: Kademlia, +} + +impl MyBehaviour { + fn client(k: identity::Keypair) -> Self { + let mut config = KademliaConfig::default(); + config.set_mode(Mode::Client); + + Self::with_config(k, config) + } + + fn server(k: identity::Keypair) -> Self { + Self::with_config(k, KademliaConfig::default()) + } + + fn with_config(k: identity::Keypair, config: KademliaConfig) -> MyBehaviour { + let local_peer_id = k.public().to_peer_id(); + + Self { + identify: identify::Behaviour::new(identify::Config::new( + "/test/1.0.0".to_owned(), + k.public(), + )), + kad: Kademlia::with_config(local_peer_id, MemoryStore::new(local_peer_id), config), + } + } +} From 6aeef703cb9e0d146a3f7c74e57f4719c8004813 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 10:50:21 +0200 Subject: [PATCH 037/105] Update cargo lock --- Cargo.lock | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 04ea1bd39e8..69edcc13f4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2580,6 +2580,7 @@ name = "libp2p-kad" version = "0.44.0" dependencies = [ "arrayvec", + "async-std", "asynchronous-codec", "bytes", "either", @@ -2589,9 +2590,11 @@ dependencies = [ "futures-timer", "instant", "libp2p-core", + "libp2p-identify", "libp2p-identity", "libp2p-noise", "libp2p-swarm", + "libp2p-swarm-test", "libp2p-yamux", "log", "quick-protobuf", From 8c47bd60cc5ef624b61391092e1458b69fe7fdcf Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:03:48 +0200 Subject: [PATCH 038/105] Fix unit tests --- swarm/src/connection/supported_protocols.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swarm/src/connection/supported_protocols.rs b/swarm/src/connection/supported_protocols.rs index 58edb93626d..0575046bb44 100644 --- a/swarm/src/connection/supported_protocols.rs +++ b/swarm/src/connection/supported_protocols.rs @@ -82,7 +82,7 @@ mod tests { } static FOO_PROTOCOLS: Lazy> = - Lazy::new(|| HashSet::from([StreamProtocol::new("foo")])); + Lazy::new(|| HashSet::from([StreamProtocol::new("/foo")])); static FOO_BAR_PROTOCOLS: Lazy> = - Lazy::new(|| HashSet::from([StreamProtocol::new("foo"), StreamProtocol::new("bar")])); + Lazy::new(|| HashSet::from([StreamProtocol::new("/foo"), StreamProtocol::new("/bar")])); } From bf9421edadfaab37a189713ae4dcd60657ce77be Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:26:58 +0200 Subject: [PATCH 039/105] Change test to assert actual events received --- swarm/src/connection.rs | 53 ++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 1f370f11b59..2c070aee900 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -689,7 +689,6 @@ enum Shutdown { #[cfg(test)] mod tests { use super::*; - use crate::connection::supported_protocols::SupportedProtocols; use crate::keep_alive; use futures::future; use futures::AsyncRead; @@ -768,14 +767,10 @@ mod tests { let _ = Pin::new(&mut connection) .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); assert_eq!( - connection - .handler - .supported_local_protocols - .iter() - .cloned() - .collect::>(), - HashSet::from([StreamProtocol::new("/foo")]) + connection.handler.added, + vec![vec![StreamProtocol::new("/foo")]] ); + assert!(connection.handler.removed.is_empty()); connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/foo"), StreamProtocol::new("/bar")]); @@ -784,14 +779,13 @@ mod tests { .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); assert_eq!( - connection - .handler - .supported_local_protocols - .iter() - .cloned() - .collect::>(), - HashSet::from([StreamProtocol::new("/bar"), StreamProtocol::new("/foo")]) + connection.handler.added, + vec![ + vec![StreamProtocol::new("/foo")], + vec![StreamProtocol::new("/bar")] + ] ); + assert!(connection.handler.removed.is_empty()); connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/bar")]); @@ -799,13 +793,15 @@ mod tests { .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); assert_eq!( - connection - .handler - .supported_local_protocols - .iter() - .cloned() - .collect::>(), - HashSet::from([StreamProtocol::new("/bar")]) + connection.handler.added, + vec![ + vec![StreamProtocol::new("/foo")], + vec![StreamProtocol::new("/bar")] + ] + ); + assert_eq!( + connection.handler.removed, + vec![vec![StreamProtocol::new("/foo")]] ); } @@ -929,7 +925,8 @@ mod tests { #[derive(Default)] struct ConfigurableProtocolConnectionHandler { active_protocols: HashSet, - supported_local_protocols: SupportedProtocols, + added: Vec>, + removed: Vec>, } impl ConnectionHandler for MockConnectionHandler { @@ -1035,8 +1032,14 @@ mod tests { Self::OutboundOpenInfo, >, ) { - if let ConnectionEvent::LocalProtocolsChange(change) = event { - self.supported_local_protocols.on_protocols_change(change); + match event { + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { + self.added.push(added.cloned().collect()) + } + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { + self.removed.push(removed.cloned().collect()) + } + _ => {} } } From a82343a4c5d60948e0ddbe881623b8952055bdbe Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:29:38 +0200 Subject: [PATCH 040/105] Don't report empty set of protocols to handler --- swarm/src/connection.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 2c070aee900..b0d3776e28c 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -184,11 +184,13 @@ where ) -> Self { let initial_protocols = gather_supported_protocols(&handler); - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: initial_protocols.difference(&HashSet::new()).peekable(), - }), - )); + if !initial_protocols.is_empty() { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::Added(ProtocolsAdded { + protocols: initial_protocols.difference(&HashSet::new()).peekable(), + }), + )); + } Connection { muxing: muxer, From 19cd9b90d52952d67033148618fafceabd5fa4a5 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:30:53 +0200 Subject: [PATCH 041/105] Use constructor --- swarm/src/connection.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index b0d3776e28c..7df6483b565 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -186,9 +186,7 @@ where if !initial_protocols.is_empty() { handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: initial_protocols.difference(&HashSet::new()).peekable(), - }), + ProtocolsChange::Added(ProtocolsAdded::from_set(&initial_protocols)), )); } From 6d3e9ee3b20f8758655b79ed4e8a11c5bef65def Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:41:08 +0200 Subject: [PATCH 042/105] Introduce test helper --- swarm/src/connection.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 7df6483b565..03b7d84b254 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -436,6 +436,13 @@ where return Poll::Pending; // Nothing can make progress, return `Pending`. } } + + #[cfg(test)] + fn poll_noop_waker( + &mut self, + ) -> Poll, ConnectionError>> { + Pin::new(self).poll(&mut Context::from_waker(futures::task::noop_waker_ref())) + } } fn gather_supported_protocols(handler: &impl ConnectionHandler) -> HashSet { @@ -715,8 +722,7 @@ mod tests { max_negotiating_inbound_streams, ); - let result = Pin::new(&mut connection) - .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + let result = connection.poll_noop_waker(); assert!(result.is_pending()); assert_eq!( @@ -740,13 +746,11 @@ mod tests { ); connection.handler.open_new_outbound(); - let _ = Pin::new(&mut connection) - .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + let _ = connection.poll_noop_waker(); std::thread::sleep(upgrade_timeout + Duration::from_secs(1)); - let _ = Pin::new(&mut connection) - .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + let _ = connection.poll_noop_waker(); assert!(matches!( connection.handler.error.unwrap(), @@ -764,8 +768,7 @@ mod tests { ); connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/foo")]); - let _ = Pin::new(&mut connection) - .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + let _ = connection.poll_noop_waker(); assert_eq!( connection.handler.added, vec![vec![StreamProtocol::new("/foo")]] @@ -775,8 +778,7 @@ mod tests { connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/foo"), StreamProtocol::new("/bar")]); - let _ = Pin::new(&mut connection) - .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + let _ = connection.poll_noop_waker(); assert_eq!( connection.handler.added, @@ -789,8 +791,7 @@ mod tests { connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/bar")]); - let _ = Pin::new(&mut connection) - .poll(&mut Context::from_waker(futures::task::noop_waker_ref())); + let _ = connection.poll_noop_waker(); assert_eq!( connection.handler.added, From df93a4e5c6a3006d08447e1fa8ad774bb1f4dbab Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:45:42 +0200 Subject: [PATCH 043/105] Make tests less noisy --- swarm/src/connection.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 03b7d84b254..754d43eacb4 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -766,18 +766,17 @@ mod tests { None, 0, ); - connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/foo")]); + connection.handler.listen_on(&["/foo"]); let _ = connection.poll_noop_waker(); + assert_eq!( connection.handler.added, vec![vec![StreamProtocol::new("/foo")]] ); assert!(connection.handler.removed.is_empty()); - connection.handler.active_protocols = - HashSet::from([StreamProtocol::new("/foo"), StreamProtocol::new("/bar")]); - + connection.handler.listen_on(&["/foo", "/bar"]); let _ = connection.poll_noop_waker(); assert_eq!( @@ -789,8 +788,7 @@ mod tests { ); assert!(connection.handler.removed.is_empty()); - connection.handler.active_protocols = HashSet::from([StreamProtocol::new("/bar")]); - + connection.handler.listen_on(&["/bar"]); let _ = connection.poll_noop_waker(); assert_eq!( @@ -930,6 +928,12 @@ mod tests { removed: Vec>, } + impl ConfigurableProtocolConnectionHandler { + fn listen_on(&mut self, protocols: &[&'static str]) { + self.active_protocols = protocols.iter().copied().map(StreamProtocol::new).collect(); + } + } + impl ConnectionHandler for MockConnectionHandler { type InEvent = Void; type OutEvent = Void; From c50bcfd6bbac1655e80b036fd77aefa82b4c0ba7 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 18:50:43 +0200 Subject: [PATCH 044/105] Further simplify test and add comments --- swarm/src/connection.rs | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 754d43eacb4..b6cb755201a 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -767,41 +767,33 @@ mod tests { 0, ); + // First, start listening on a single protocol. connection.handler.listen_on(&["/foo"]); let _ = connection.poll_noop_waker(); - assert_eq!( - connection.handler.added, - vec![vec![StreamProtocol::new("/foo")]] - ); - assert!(connection.handler.removed.is_empty()); + assert_eq!(connection.handler.local_added, vec![vec!["/foo"]]); + assert!(connection.handler.local_removed.is_empty()); + // Second, listen on two protocols. connection.handler.listen_on(&["/foo", "/bar"]); let _ = connection.poll_noop_waker(); assert_eq!( - connection.handler.added, - vec![ - vec![StreamProtocol::new("/foo")], - vec![StreamProtocol::new("/bar")] - ] + connection.handler.local_added, + vec![vec!["/foo"], vec!["/bar"]], + "expect to only receive an event for the newly added protocols" ); - assert!(connection.handler.removed.is_empty()); + assert!(connection.handler.local_removed.is_empty()); + // Third, stop listening on the first protocol. connection.handler.listen_on(&["/bar"]); let _ = connection.poll_noop_waker(); assert_eq!( - connection.handler.added, - vec![ - vec![StreamProtocol::new("/foo")], - vec![StreamProtocol::new("/bar")] - ] - ); - assert_eq!( - connection.handler.removed, - vec![vec![StreamProtocol::new("/foo")]] + connection.handler.local_added, + vec![vec!["/foo"], vec!["/bar"]] ); + assert_eq!(connection.handler.local_removed, vec![vec!["/foo"]]); } struct DummyStreamMuxer { @@ -924,8 +916,8 @@ mod tests { #[derive(Default)] struct ConfigurableProtocolConnectionHandler { active_protocols: HashSet, - added: Vec>, - removed: Vec>, + local_added: Vec>, + local_removed: Vec>, } impl ConfigurableProtocolConnectionHandler { @@ -1039,10 +1031,10 @@ mod tests { ) { match event { ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { - self.added.push(added.cloned().collect()) + self.local_added.push(added.cloned().collect()) } ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { - self.removed.push(removed.cloned().collect()) + self.local_removed.push(removed.cloned().collect()) } _ => {} } From a799798a6812e5bf803d72fbb60c33a5440f7f40 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 19:00:40 +0200 Subject: [PATCH 045/105] Add failing test --- swarm/src/connection.rs | 81 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index b6cb755201a..dfbaabf9773 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -796,6 +796,56 @@ mod tests { assert_eq!(connection.handler.local_removed, vec![vec!["/foo"]]); } + #[test] + fn only_propagtes_actual_changes_to_remote_protocols_to_handler() { + let mut connection = Connection::new( + StreamMuxerBox::new(PendingStreamMuxer), + ConfigurableProtocolConnectionHandler::default(), + None, + 0, + ); + + // First, remote supports a single protocol. + connection.handler.remote_adds_support_for(&["/foo"]); + let _ = connection.poll_noop_waker(); + + assert_eq!(connection.handler.remote_added, vec![vec!["/foo"]]); + assert!(connection.handler.remote_removed.is_empty()); + + // Second, it adds a protocol but also still includes the first one. + connection + .handler + .remote_adds_support_for(&["/foo", "/bar"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.remote_added, + vec![vec!["/foo"], vec!["/bar"]], + "expect to only receive an event for the newly added protocol" + ); + assert!(connection.handler.remote_removed.is_empty()); + + // Third, stop listening on a protocol it never advertised (we can't control what handlers do so this needs to be handled gracefully). + connection.handler.remote_removes_support_for(&["/baz"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.remote_added, + vec![vec!["/foo"], vec!["/bar"]] + ); + assert!(&connection.handler.remote_removed.is_empty()); + + // Fourth, stop listening on a protocol that was previously supported + connection.handler.remote_removes_support_for(&["/bar"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.remote_added, + vec![vec!["/foo"], vec!["/bar"]] + ); + assert_eq!(connection.handler.remote_removed, vec![vec!["/bar"]]); + } + struct DummyStreamMuxer { counter: Arc<()>, } @@ -915,15 +965,36 @@ mod tests { #[derive(Default)] struct ConfigurableProtocolConnectionHandler { + events: Vec>, active_protocols: HashSet, local_added: Vec>, local_removed: Vec>, + remote_added: Vec>, + remote_removed: Vec>, } impl ConfigurableProtocolConnectionHandler { fn listen_on(&mut self, protocols: &[&'static str]) { self.active_protocols = protocols.iter().copied().map(StreamProtocol::new).collect(); } + + fn remote_adds_support_for(&mut self, protocols: &[&'static str]) { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Added( + protocols.iter().copied().map(StreamProtocol::new).collect(), + ), + )); + } + + fn remote_removes_support_for(&mut self, protocols: &[&'static str]) { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Removed( + protocols.iter().copied().map(StreamProtocol::new).collect(), + ), + )); + } } impl ConnectionHandler for MockConnectionHandler { @@ -1036,6 +1107,12 @@ mod tests { ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { self.local_removed.push(removed.cloned().collect()) } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(added)) => { + self.remote_added.push(added.cloned().collect()) + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(removed)) => { + self.remote_removed.push(removed.cloned().collect()) + } _ => {} } } @@ -1059,6 +1136,10 @@ mod tests { Self::Error, >, > { + if let Some(event) = self.events.pop() { + return Poll::Ready(event); + } + Poll::Pending } } From 0260ad17e82a304c7621d4d9306ddeb73ac676fc Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 19:10:39 +0200 Subject: [PATCH 046/105] Make test pass, i.e. only report actual changes back to the handler --- swarm/src/connection.rs | 36 +++++++++++++++++++++++++----------- swarm/src/handler.rs | 11 ++++++++--- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index dfbaabf9773..c7840498ff6 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -38,6 +38,7 @@ use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, Upgra use crate::{ ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, StreamProtocol, SubstreamProtocol, }; +use either::Either; use futures::stream::FuturesUnordered; use futures::FutureExt; use futures::StreamExt; @@ -153,7 +154,7 @@ where >, local_supported_protocols: HashSet, - remote_supported_protocols: SupportedProtocols, + remote_supported_protocols: HashSet, } impl fmt::Debug for Connection @@ -200,7 +201,7 @@ where max_negotiating_inbound_streams, requested_substreams: Default::default(), local_supported_protocols: initial_protocols, - remote_supported_protocols: SupportedProtocols::default(), + remote_supported_protocols: Default::default(), } } @@ -268,11 +269,17 @@ where Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( ProtocolSupport::Added(protocols), )) => { - let change = ProtocolsChange::Added(ProtocolsAdded::from_set(&protocols)); + let mut actually_added_protocols = + protocols.difference(remote_supported_protocols).peekable(); + + if actually_added_protocols.peek().is_some() { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( + ProtocolsChange::Added(ProtocolsAdded { + protocols: actually_added_protocols, + }), + )); - if remote_supported_protocols.on_protocols_change(change.clone()) { - handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); - // TODO: Should we optimise this to be the _actual_ change? + remote_supported_protocols.extend(protocols); } continue; @@ -280,11 +287,18 @@ where Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( ProtocolSupport::Removed(protocols), )) => { - let change = ProtocolsChange::Removed(ProtocolsRemoved::from_set(&protocols)); + let mut actually_removed_protocols = remote_supported_protocols + .intersection(&protocols) + .peekable(); + + if actually_removed_protocols.peek().is_some() { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( + ProtocolsChange::Removed(ProtocolsRemoved { + protocols: Either::Right(actually_removed_protocols), + }), + )); - if remote_supported_protocols.on_protocols_change(change.clone()) { - handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); - // TODO: Should we optimise this to be the _actual_ change? + remote_supported_protocols.retain(|p| !protocols.contains(p)); } continue; @@ -425,7 +439,7 @@ where if removed_protocols.peek().is_some() { handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( ProtocolsChange::Removed(ProtocolsRemoved { - protocols: removed_protocols, + protocols: Either::Left(removed_protocols), }), )); } diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 3092ac750e0..bca19f6a2ae 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -48,12 +48,13 @@ mod select; pub use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, UpgradeInfoSend}; +use ::either::Either; use instant::Instant; use libp2p_core::{upgrade::UpgradeError, ConnectedPoint, Multiaddr}; use libp2p_identity::PeerId; use once_cell::sync::Lazy; use std::collections::hash_map::RandomState; -use std::collections::hash_set::Difference; +use std::collections::hash_set::{Difference, Intersection}; use std::collections::HashSet; use std::iter::Peekable; use std::{cmp::Ordering, error, fmt, task::Context, task::Poll, time::Duration}; @@ -309,13 +310,17 @@ impl<'a> ProtocolsAdded<'a> { /// An [`Iterator`] over all protocols that have been removed. #[derive(Clone)] pub struct ProtocolsRemoved<'a> { - pub(crate) protocols: Peekable>, + pub(crate) protocols: Either< + Peekable>, + Peekable>, + >, } impl<'a> ProtocolsRemoved<'a> { + #[cfg(test)] pub(crate) fn from_set(protocols: &'a HashSet) -> Self { ProtocolsRemoved { - protocols: protocols.difference(&EMPTY_HASHSET).peekable(), + protocols: Either::Left(protocols.difference(&EMPTY_HASHSET).peekable()), } } } From bf99654e3d54aabc22ce8f012511c086ade6df87 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 19:30:31 +0200 Subject: [PATCH 047/105] Extract helpers to make fields crate-private and add docs --- swarm/src/connection.rs | 57 ++++++++++----------------------- swarm/src/handler.rs | 70 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 43 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index c7840498ff6..4efbe33e5bb 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -32,13 +32,11 @@ pub use supported_protocols::SupportedProtocols; use crate::handler::{ AddressChange, ConnectionEvent, ConnectionHandler, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, ListenUpgradeError, ProtocolSupport, ProtocolsAdded, ProtocolsChange, - ProtocolsRemoved, }; use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, UpgradeInfoSend}; use crate::{ ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, StreamProtocol, SubstreamProtocol, }; -use either::Either; use futures::stream::FuturesUnordered; use futures::FutureExt; use futures::StreamExt; @@ -269,16 +267,10 @@ where Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( ProtocolSupport::Added(protocols), )) => { - let mut actually_added_protocols = - protocols.difference(remote_supported_protocols).peekable(); - - if actually_added_protocols.peek().is_some() { - handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: actually_added_protocols, - }), - )); - + if let Some(added) = + ProtocolsChange::add(remote_supported_protocols, &protocols) + { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(added)); remote_supported_protocols.extend(protocols); } @@ -287,17 +279,11 @@ where Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( ProtocolSupport::Removed(protocols), )) => { - let mut actually_removed_protocols = remote_supported_protocols - .intersection(&protocols) - .peekable(); - - if actually_removed_protocols.peek().is_some() { - handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange( - ProtocolsChange::Removed(ProtocolsRemoved { - protocols: Either::Right(actually_removed_protocols), - }), - )); - + if let Some(removed) = + ProtocolsChange::remove(remote_supported_protocols, &protocols) + { + handler + .on_connection_event(ConnectionEvent::RemoteProtocolsChange(removed)); remote_supported_protocols.retain(|p| !protocols.contains(p)); } @@ -423,26 +409,15 @@ where let new_protocols = gather_supported_protocols(handler); - if &new_protocols != supported_protocols { - let mut added_protocols = new_protocols.difference(supported_protocols).peekable(); - let mut removed_protocols = - supported_protocols.difference(&new_protocols).peekable(); + let (added, removed) = + ProtocolsChange::from_full_sets(supported_protocols, &new_protocols); - if added_protocols.peek().is_some() { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Added(ProtocolsAdded { - protocols: added_protocols, - }), - )); - } + if let Some(added) = added { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(added)); + } - if removed_protocols.peek().is_some() { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( - ProtocolsChange::Removed(ProtocolsRemoved { - protocols: Either::Left(removed_protocols), - }), - )); - } + if let Some(removed) = removed { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(removed)); } *supported_protocols = new_protocols; diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index bca19f6a2ae..e31558e301c 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -293,10 +293,76 @@ pub enum ProtocolsChange<'a> { Removed(ProtocolsRemoved<'a>), } +impl<'a> ProtocolsChange<'a> { + /// Compute the [`ProtocolsChange`] that results from adding `to_add` to `existing_protocols`. + /// + /// Returns `None` if the change is a no-op, i.e. `to_add` is a subset of `existing_protocols`. + pub(crate) fn add( + existing_protocols: &'a HashSet, + to_add: &'a HashSet, + ) -> Option { + let mut actually_added_protocols = to_add.difference(existing_protocols).peekable(); + + if actually_added_protocols.peek().is_none() { + return None; + } + + Some(ProtocolsChange::Added(ProtocolsAdded { + protocols: actually_added_protocols, + })) + } + + /// Compute the [`ProtocolsChange`] that results from removing `to_remove` from `existing_protocols`. + /// + /// Returns `None` if the change is a no-op, i.e. none of the protocols in `to_remove` are in `existing_protocols`. + pub(crate) fn remove( + existing_protocols: &'a HashSet, + to_remove: &'a HashSet, + ) -> Option { + let mut actually_removed_protocols = existing_protocols.intersection(&to_remove).peekable(); + + if actually_removed_protocols.peek().is_none() { + return None; + } + + Some(ProtocolsChange::Removed(ProtocolsRemoved { + protocols: Either::Right(actually_removed_protocols), + })) + } + + /// Compute the [`ProtocolsChange`]s required to go from `existing_protocols` to `new_protocols`. + pub(crate) fn from_full_sets( + existing_protocols: &'a HashSet, + new_protocols: &'a HashSet, + ) -> (Option, Option) { + if existing_protocols == new_protocols { + return (None, None); + } + + let mut added_protocols = new_protocols.difference(existing_protocols).peekable(); + let mut removed_protocols = existing_protocols.difference(&new_protocols).peekable(); + + let added = added_protocols + .peek() + .is_some() + .then_some(ProtocolsChange::Added(ProtocolsAdded { + protocols: added_protocols, + })); + let removed = removed_protocols + .peek() + .is_some() + .then_some(ProtocolsChange::Removed(ProtocolsRemoved { + protocols: Either::Left(removed_protocols), + })); + + (added, removed) + } +} + /// An [`Iterator`] over all protocols that have been added. #[derive(Clone)] pub struct ProtocolsAdded<'a> { - pub(crate) protocols: Peekable>, + protocols: Peekable>, } impl<'a> ProtocolsAdded<'a> { @@ -310,7 +376,7 @@ impl<'a> ProtocolsAdded<'a> { /// An [`Iterator`] over all protocols that have been removed. #[derive(Clone)] pub struct ProtocolsRemoved<'a> { - pub(crate) protocols: Either< + protocols: Either< Peekable>, Peekable>, >, From 46f4e9679b17388177cd5bed2b9940ecef71ce4a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 19:39:23 +0200 Subject: [PATCH 048/105] Return `SmallVec` from `from_full_sets` which allows iteration --- swarm/src/connection.rs | 11 ++--------- swarm/src/handler.rs | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 4efbe33e5bb..d247e8bae00 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -409,15 +409,8 @@ where let new_protocols = gather_supported_protocols(handler); - let (added, removed) = - ProtocolsChange::from_full_sets(supported_protocols, &new_protocols); - - if let Some(added) = added { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(added)); - } - - if let Some(removed) = removed { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(removed)); + for change in ProtocolsChange::from_full_sets(supported_protocols, &new_protocols) { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(change)); } *supported_protocols = new_protocols; diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index e31558e301c..566d1582e6b 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -53,6 +53,7 @@ use instant::Instant; use libp2p_core::{upgrade::UpgradeError, ConnectedPoint, Multiaddr}; use libp2p_identity::PeerId; use once_cell::sync::Lazy; +use smallvec::SmallVec; use std::collections::hash_map::RandomState; use std::collections::hash_set::{Difference, Intersection}; use std::collections::HashSet; @@ -334,28 +335,29 @@ impl<'a> ProtocolsChange<'a> { pub(crate) fn from_full_sets( existing_protocols: &'a HashSet, new_protocols: &'a HashSet, - ) -> (Option, Option) { + ) -> SmallVec<[Self; 2]> { if existing_protocols == new_protocols { - return (None, None); + return SmallVec::new(); } + let mut changes = SmallVec::new(); + let mut added_protocols = new_protocols.difference(existing_protocols).peekable(); let mut removed_protocols = existing_protocols.difference(&new_protocols).peekable(); - let added = added_protocols - .peek() - .is_some() - .then_some(ProtocolsChange::Added(ProtocolsAdded { + if added_protocols.peek().is_some() { + changes.push(ProtocolsChange::Added(ProtocolsAdded { protocols: added_protocols, })); - let removed = removed_protocols - .peek() - .is_some() - .then_some(ProtocolsChange::Removed(ProtocolsRemoved { + } + + if removed_protocols.peek().is_some() { + changes.push(ProtocolsChange::Removed(ProtocolsRemoved { protocols: Either::Left(removed_protocols), })); + } - (added, removed) + changes } } From ae7fc939da0be63a6cad9ffdcb38d96db0427cdf Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 19:57:44 +0200 Subject: [PATCH 049/105] Fix clippy warnings --- swarm/src/handler.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index 566d1582e6b..fbf90edc67d 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -304,9 +304,7 @@ impl<'a> ProtocolsChange<'a> { ) -> Option { let mut actually_added_protocols = to_add.difference(existing_protocols).peekable(); - if actually_added_protocols.peek().is_none() { - return None; - } + actually_added_protocols.peek()?; Some(ProtocolsChange::Added(ProtocolsAdded { protocols: actually_added_protocols, @@ -320,11 +318,9 @@ impl<'a> ProtocolsChange<'a> { existing_protocols: &'a HashSet, to_remove: &'a HashSet, ) -> Option { - let mut actually_removed_protocols = existing_protocols.intersection(&to_remove).peekable(); + let mut actually_removed_protocols = existing_protocols.intersection(to_remove).peekable(); - if actually_removed_protocols.peek().is_none() { - return None; - } + actually_removed_protocols.peek()?; Some(ProtocolsChange::Removed(ProtocolsRemoved { protocols: Either::Right(actually_removed_protocols), @@ -343,7 +339,7 @@ impl<'a> ProtocolsChange<'a> { let mut changes = SmallVec::new(); let mut added_protocols = new_protocols.difference(existing_protocols).peekable(); - let mut removed_protocols = existing_protocols.difference(&new_protocols).peekable(); + let mut removed_protocols = existing_protocols.difference(new_protocols).peekable(); if added_protocols.peek().is_some() { changes.push(ProtocolsChange::Added(ProtocolsAdded { From fe9a6e306ea497cdde054154ea7831894215c70b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 4 May 2023 19:58:57 +0200 Subject: [PATCH 050/105] Fix rustdoc --- swarm/src/handler.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/swarm/src/handler.rs b/swarm/src/handler.rs index fbf90edc67d..023c3a60586 100644 --- a/swarm/src/handler.rs +++ b/swarm/src/handler.rs @@ -759,6 +759,5 @@ impl Ord for KeepAlive { } /// A statically declared, empty [`HashSet`] allows us to work around borrow-checker rules for -/// [`ProtocolsAdded::from_set`] and [`ProtocolsRemoved::from_set`]. Those have lifetime-constraints -/// which don't work unless we have a [`HashSet`] with a `'static' lifetime. +/// [`ProtocolsAdded::from_set`]. The lifetimes don't work unless we have a [`HashSet`] with a `'static' lifetime. static EMPTY_HASHSET: Lazy> = Lazy::new(HashSet::new); From 1adb1fece7cb6ae3bc574c06a0f092f9262e583b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Sun, 7 May 2023 23:59:02 +0200 Subject: [PATCH 051/105] Only update protocol support on change message --- protocols/kad/src/handler_priv.rs | 40 +++++++++++++------------------ 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/protocols/kad/src/handler_priv.rs b/protocols/kad/src/handler_priv.rs index a3aced0b235..bfa2ed64111 100644 --- a/protocols/kad/src/handler_priv.rs +++ b/protocols/kad/src/handler_priv.rs @@ -31,14 +31,13 @@ use libp2p_core::{upgrade, ConnectedPoint}; use libp2p_identity::PeerId; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, - ProtocolsChange, }; use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerUpgrErr, KeepAlive, - NegotiatedSubstream, StreamProtocol, SubstreamProtocol, + NegotiatedSubstream, SubstreamProtocol, SupportedProtocols, }; use log::trace; -use std::collections::{HashSet, VecDeque}; +use std::collections::VecDeque; use std::task::Waker; use std::{ error, fmt, io, marker::PhantomData, pin::Pin, task::Context, task::Poll, time::Duration, @@ -86,7 +85,7 @@ pub struct KademliaHandler { /// The current state of protocol confirmation. protocol_status: ProtocolStatus, - remote_supported_protocols: HashSet, + remote_supported_protocols: SupportedProtocols, } /// The states of protocol confirmation that a connection @@ -784,28 +783,23 @@ where ConnectionEvent::AddressChange(_) | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::LocalProtocolsChange(_) => {} - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(added)) => { - for p in added { - self.remote_supported_protocols.insert(p.to_owned()); - } - } - ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(removed)) => { - for p in removed { - self.remote_supported_protocols.remove(p); + ConnectionEvent::RemoteProtocolsChange(change) => { + let dirty = self.remote_supported_protocols.on_protocols_change(change); + + if dirty { + let remote_supports_our_kademlia_protocols = self + .remote_supported_protocols + .iter() + .any(|p| self.config.protocol_config.protocol_names().contains(p)); + + if remote_supports_our_kademlia_protocols { + self.protocol_status = ProtocolStatus::Confirmed; + } else { + self.protocol_status = ProtocolStatus::NotSupported; + } } } } - - let remote_supports_our_kademlia_protocols = self - .remote_supported_protocols - .iter() - .any(|p| self.config.protocol_config.protocol_names().contains(p)); - - if remote_supports_our_kademlia_protocols { - self.protocol_status = ProtocolStatus::Confirmed; - } else { - self.protocol_status = ProtocolStatus::NotSupported; - } } } From 0f4d7a91de405599159080302c3a48e72e4f1c2c Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 8 May 2023 00:03:40 +0200 Subject: [PATCH 052/105] Update docs --- protocols/kad/src/behaviour.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 4876cf45a3e..f257c049df3 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -405,7 +405,7 @@ impl KademliaConfig { /// Sets the mode. /// - /// TODO: More docs. + /// The default is [`Mode::Server`]. pub fn set_mode(&mut self, m: Mode) -> &mut Self { self.mode = m; self @@ -3213,6 +3213,10 @@ pub enum RoutingUpdate { Failed, } +/// The kademlia mode. +/// +/// In server mode, a node accepts inbound kademlia messages and is therefore available to the wider network. +/// In client mode, a node merely issues requests to the network. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Mode { Client, From 16f26569b700182e6ee36b00c40bc2f93787674d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 8 May 2023 16:45:22 +0200 Subject: [PATCH 053/105] Minimize diff --- swarm/src/connection.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index 4ba4bf30668..32a24161393 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -32,8 +32,9 @@ pub use supported_protocols::SupportedProtocols; use crate::handler::{ AddressChange, ConnectionEvent, ConnectionHandler, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, ListenUpgradeError, ProtocolSupport, ProtocolsAdded, ProtocolsChange, + UpgradeInfoSend, }; -use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, UpgradeInfoSend}; +use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper}; use crate::{ ConnectionHandlerEvent, KeepAlive, StreamProtocol, StreamUpgradeError, SubstreamProtocol, }; From 3b0e3a720bfd90d9e95d0b1d038169159632af13 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 8 May 2023 17:21:39 +0200 Subject: [PATCH 054/105] More resilient pattern match --- protocols/kad/tests/client_mode.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index c2ddd9aa555..6e08862d007 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -39,10 +39,14 @@ async fn two_servers_add_each_other_to_routing_table() { let server1_peer_id = *server1.local_peer_id(); let server2_peer_id = *server2.local_peer_id(); + use KademliaEvent::*; + use MyBehaviourEvent::*; + match libp2p_swarm_test::drive(&mut server1, &mut server2).await { ( - [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer: peer1, .. })], - [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::UnroutablePeer { peer: peer2, .. })], // Unroutable because server2 did not dial. + [Identify(_), Identify(_), Kad(RoutingUpdated { peer: peer1, .. })], + [Identify(_), Identify(_), Kad(UnroutablePeer { peer: peer2, .. })] + | [Identify(_), Kad(UnroutablePeer { peer: peer2, .. }), Identify(_)], // Unroutable because server2 did not dial. ) => { assert_eq!(peer1, server2_peer_id); assert_eq!(peer2, server1_peer_id); From 73b48b5321df23895e4872df9d5841f85b86ac41 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 May 2023 13:40:51 +0200 Subject: [PATCH 055/105] Initial refactoring to new events --- misc/allow-block-list/src/lib.rs | 5 +- misc/connection-limits/src/lib.rs | 5 +- protocols/autonat/src/behaviour.rs | 11 ++- protocols/autonat/src/behaviour/as_client.rs | 20 +--- protocols/dcutr/src/behaviour_impl.rs | 5 +- protocols/floodsub/src/layer.rs | 5 +- protocols/gossipsub/src/behaviour.rs | 5 +- protocols/identify/src/behaviour.rs | 15 ++- protocols/kad/src/behaviour.rs | 5 +- protocols/mdns/src/behaviour.rs | 5 +- protocols/perf/src/client/behaviour.rs | 5 +- protocols/perf/src/server/behaviour.rs | 5 +- protocols/ping/src/lib.rs | 5 +- protocols/relay/src/behaviour.rs | 5 +- protocols/relay/src/priv_client.rs | 5 +- protocols/rendezvous/src/client.rs | 5 +- protocols/rendezvous/src/server.rs | 5 +- protocols/request-response/src/lib.rs | 5 +- swarm-derive/src/lib.rs | 65 +++++++++---- swarm/src/behaviour.rs | 72 +++++++------- swarm/src/behaviour/external_addresses.rs | 10 +- swarm/src/dummy.rs | 5 +- swarm/src/keep_alive.rs | 5 +- swarm/src/lib.rs | 98 ++++++++++++-------- swarm/src/test.rs | 19 ++-- swarm/tests/swarm_derive.rs | 5 +- 26 files changed, 232 insertions(+), 168 deletions(-) diff --git a/misc/allow-block-list/src/lib.rs b/misc/allow-block-list/src/lib.rs index 521aa0026cc..eba747462fe 100644 --- a/misc/allow-block-list/src/lib.rs +++ b/misc/allow-block-list/src/lib.rs @@ -243,8 +243,9 @@ where FromSwarm::ExpiredListenAddr(_) => {} FromSwarm::ListenerError(_) => {} FromSwarm::ListenerClosed(_) => {} - FromSwarm::NewExternalAddr(_) => {} - FromSwarm::ExpiredExternalAddr(_) => {} + FromSwarm::NewExternalAddrCandidate(_) => {} + FromSwarm::ExternalAddrExpired(_) => {} + FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/misc/connection-limits/src/lib.rs b/misc/connection-limits/src/lib.rs index 1a568cb7ab9..52d0aa62c39 100644 --- a/misc/connection-limits/src/lib.rs +++ b/misc/connection-limits/src/lib.rs @@ -337,8 +337,9 @@ impl NetworkBehaviour for Behaviour { FromSwarm::ExpiredListenAddr(_) => {} FromSwarm::ListenerError(_) => {} FromSwarm::ListenerClosed(_) => {} - FromSwarm::NewExternalAddr(_) => {} - FromSwarm::ExpiredExternalAddr(_) => {} + FromSwarm::NewExternalAddrCandidate(_) => {} + FromSwarm::ExternalAddrExpired(_) => {} + FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index 17681341489..30e70e48768 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -36,8 +36,8 @@ use libp2p_request_response::{ }; use libp2p_swarm::{ behaviour::{ - AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, ExpiredExternalAddr, - ExpiredListenAddr, FromSwarm, + AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, ExpiredListenAddr, + ExternalAddrExpired, FromSwarm, }, ConnectionDenied, ConnectionId, ExternalAddresses, ListenAddresses, NetworkBehaviour, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, @@ -561,12 +561,12 @@ impl NetworkBehaviour for Behaviour { })); self.as_client().on_expired_address(addr); } - FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr }) => { + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr }) => { self.inner - .on_swarm_event(FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr })); + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); self.as_client().on_expired_address(addr); } - external_addr @ FromSwarm::NewExternalAddr(_) => { + external_addr @ FromSwarm::NewExternalAddrCandidate(_) => { self.inner.on_swarm_event(external_addr); self.as_client().on_new_address(); } @@ -580,6 +580,7 @@ impl NetworkBehaviour for Behaviour { listener_closed @ FromSwarm::ListenerClosed(_) => { self.inner.on_swarm_event(listener_closed) } + confirmed @ FromSwarm::ExternalAddrConfirmed(_) => self.inner.on_swarm_event(confirmed), } } diff --git a/protocols/autonat/src/behaviour/as_client.rs b/protocols/autonat/src/behaviour/as_client.rs index e0c0b2e9e0a..6149a6b35c7 100644 --- a/protocols/autonat/src/behaviour/as_client.rs +++ b/protocols/autonat/src/behaviour/as_client.rs @@ -30,9 +30,7 @@ use instant::Instant; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use libp2p_request_response::{self as request_response, OutboundFailure, RequestId}; -use libp2p_swarm::{ - AddressScore, ConnectionId, ExternalAddresses, ListenAddresses, PollParameters, ToSwarm, -}; +use libp2p_swarm::{ConnectionId, ExternalAddresses, ListenAddresses, PollParameters, ToSwarm}; use rand::{seq::SliceRandom, thread_rng}; use std::{ collections::{HashMap, HashSet, VecDeque}, @@ -103,7 +101,7 @@ pub(crate) struct AsClient<'a> { impl<'a> HandleInnerEvent for AsClient<'a> { fn handle_event( &mut self, - params: &mut impl PollParameters, + _: &mut impl PollParameters, event: request_response::Event, ) -> VecDeque { match event { @@ -147,19 +145,7 @@ impl<'a> HandleInnerEvent for AsClient<'a> { } if let Ok(address) = response.result { - // Update observed address score if it is finite. - #[allow(deprecated)] - // TODO: Fix once we report `AddressScore` through `FromSwarm` event. - let score = params - .external_addresses() - .find_map(|r| (r.addr == address).then_some(r.score)) - .unwrap_or(AddressScore::Finite(0)); - if let AddressScore::Finite(finite_score) = score { - actions.push_back(ToSwarm::ReportObservedAddr { - address, - score: AddressScore::Finite(finite_score + 1), - }); - } + actions.push_back(ToSwarm::ExternalAddrConfirmed(address)); } actions diff --git a/protocols/dcutr/src/behaviour_impl.rs b/protocols/dcutr/src/behaviour_impl.rs index 5b89f2ef2c2..d47e94e9117 100644 --- a/protocols/dcutr/src/behaviour_impl.rs +++ b/protocols/dcutr/src/behaviour_impl.rs @@ -441,8 +441,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/floodsub/src/layer.rs b/protocols/floodsub/src/layer.rs index f8a498912d6..29fe8ba250f 100644 --- a/protocols/floodsub/src/layer.rs +++ b/protocols/floodsub/src/layer.rs @@ -494,8 +494,9 @@ impl NetworkBehaviour for Floodsub { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/gossipsub/src/behaviour.rs b/protocols/gossipsub/src/behaviour.rs index ba0594a9801..f4af1175586 100644 --- a/protocols/gossipsub/src/behaviour.rs +++ b/protocols/gossipsub/src/behaviour.rs @@ -3500,8 +3500,9 @@ where | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index 532b86dafa8..ac08a142a24 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -25,8 +25,8 @@ use libp2p_identity::PeerId; use libp2p_identity::PublicKey; use libp2p_swarm::behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}; use libp2p_swarm::{ - AddressScore, ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, - NetworkBehaviour, NotifyHandler, PollParameters, StreamUpgradeError, THandlerInEvent, ToSwarm, + ConnectionDenied, DialError, ExternalAddresses, ListenAddresses, NetworkBehaviour, + NotifyHandler, PollParameters, StreamUpgradeError, THandlerInEvent, ToSwarm, }; use libp2p_swarm::{ConnectionId, THandler, THandlerOutEvent}; use lru::LruCache; @@ -295,10 +295,8 @@ impl NetworkBehaviour for Behaviour { let observed = info.observed_addr.clone(); self.events .push_back(ToSwarm::GenerateEvent(Event::Received { peer_id, info })); - self.events.push_back(ToSwarm::ReportObservedAddr { - address: observed, - score: AddressScore::Finite(1), - }); + self.events + .push_back(ToSwarm::NewExternalAddrCandidate(observed)); } handler::Event::Identification => { self.events @@ -405,8 +403,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::NewListener(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) => {} + FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 856a994056a..19bcb49c7bc 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2431,13 +2431,14 @@ where FromSwarm::DialFailure(dial_failure) => self.on_dial_failure(dial_failure), FromSwarm::AddressChange(address_change) => self.on_address_change(address_change), FromSwarm::ExpiredListenAddr(_) - | FromSwarm::NewExternalAddr(_) + | FromSwarm::NewExternalAddrCandidate(_) | FromSwarm::NewListenAddr(_) | FromSwarm::ListenFailure(_) | FromSwarm::NewListener(_) | FromSwarm::ListenerClosed(_) | FromSwarm::ListenerError(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/mdns/src/behaviour.rs b/protocols/mdns/src/behaviour.rs index 8f01439403f..bc102f832df 100644 --- a/protocols/mdns/src/behaviour.rs +++ b/protocols/mdns/src/behaviour.rs @@ -243,8 +243,9 @@ where | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/protocols/perf/src/client/behaviour.rs b/protocols/perf/src/client/behaviour.rs index 3dd932b0c77..912f6d5bb9e 100644 --- a/protocols/perf/src/client/behaviour.rs +++ b/protocols/perf/src/client/behaviour.rs @@ -128,8 +128,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/protocols/perf/src/server/behaviour.rs b/protocols/perf/src/server/behaviour.rs index 6f0047913b6..b15cb70110d 100644 --- a/protocols/perf/src/server/behaviour.rs +++ b/protocols/perf/src/server/behaviour.rs @@ -89,8 +89,9 @@ impl NetworkBehaviour for Behaviour { FromSwarm::ExpiredListenAddr(_) => {} FromSwarm::ListenerError(_) => {} FromSwarm::ListenerClosed(_) => {} - FromSwarm::NewExternalAddr(_) => {} - FromSwarm::ExpiredExternalAddr(_) => {} + FromSwarm::NewExternalAddrCandidate(_) => {} + FromSwarm::ExternalAddrExpired(_) => {} + FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/protocols/ping/src/lib.rs b/protocols/ping/src/lib.rs index 85be866dbf5..22cd340ac11 100644 --- a/protocols/ping/src/lib.rs +++ b/protocols/ping/src/lib.rs @@ -164,8 +164,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/relay/src/behaviour.rs b/protocols/relay/src/behaviour.rs index 133a5fa7d50..7349c67ad7a 100644 --- a/protocols/relay/src/behaviour.rs +++ b/protocols/relay/src/behaviour.rs @@ -317,8 +317,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/protocols/relay/src/priv_client.rs b/protocols/relay/src/priv_client.rs index 7a264bacb03..c3c80c5b504 100644 --- a/protocols/relay/src/priv_client.rs +++ b/protocols/relay/src/priv_client.rs @@ -229,8 +229,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index a8527ba5dfe..d410cce8d89 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -307,8 +307,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/rendezvous/src/server.rs b/protocols/rendezvous/src/server.rs index 8568076202a..6d64938ca3d 100644 --- a/protocols/rendezvous/src/server.rs +++ b/protocols/rendezvous/src/server.rs @@ -191,8 +191,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/protocols/request-response/src/lib.rs b/protocols/request-response/src/lib.rs index 10c78703e45..1c3f4cf725f 100644 --- a/protocols/request-response/src/lib.rs +++ b/protocols/request-response/src/lib.rs @@ -756,8 +756,9 @@ where FromSwarm::ExpiredListenAddr(_) => {} FromSwarm::ListenerError(_) => {} FromSwarm::ListenerClosed(_) => {} - FromSwarm::NewExternalAddr(_) => {} - FromSwarm::ExpiredExternalAddr(_) => {} + FromSwarm::NewExternalAddrCandidate(_) => {} + FromSwarm::ExternalAddrExpired(_) => {} + FromSwarm::ExternalAddrConfirmed(_) => {} } } diff --git a/swarm-derive/src/lib.rs b/swarm-derive/src/lib.rs index 8bcf90e8798..dafd1076218 100644 --- a/swarm-derive/src/lib.rs +++ b/swarm-derive/src/lib.rs @@ -82,8 +82,9 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> syn::Result syn::Result quote! { - self.#i.on_swarm_event(#from_swarm::NewExternalAddr(#new_external_addr { + self.#i.on_swarm_event(#from_swarm::NewExternalAddrCandidate(#new_external_addr_candidate { addr, })); }, None => quote! { - self.#field_n.on_swarm_event(#from_swarm::NewExternalAddr(#new_external_addr { + self.#field_n.on_swarm_event(#from_swarm::NewExternalAddrCandidate(#new_external_addr_candidate { addr, })); }, @@ -463,20 +464,41 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> syn::Result quote! { - self.#i.on_swarm_event(#from_swarm::ExpiredExternalAddr(#expired_external_addr { + self.#i.on_swarm_event(#from_swarm::ExternalAddrExpired(#external_addr_expired { addr, })); }, None => quote! { - self.#field_n.on_swarm_event(#from_swarm::ExpiredExternalAddr(#expired_external_addr { + self.#field_n.on_swarm_event(#from_swarm::ExternalAddrExpired(#external_addr_expired { + addr, + })); + }, + }) + }; + + // Build the list of statements to put in the body of `on_swarm_event()` + // for the `FromSwarm::ExternalAddrConfirmed` variant. + let on_external_addr_confirmed_stmts = { + data_struct + .fields + .iter() + .enumerate() + .map(|(field_n, field)| match field.ident { + Some(ref i) => quote! { + self.#i.on_swarm_event(#from_swarm::ExternalAddrConfirmed(#external_addr_confirmed { + addr, + })); + }, + None => quote! { + self.#field_n.on_swarm_event(#from_swarm::ExternalAddrConfirmed(#external_addr_confirmed { addr, })); }, @@ -717,8 +739,14 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> syn::Result { - return std::task::Poll::Ready(#network_behaviour_action::ReportObservedAddr { address, score }); + std::task::Poll::Ready(#network_behaviour_action::NewExternalAddrCandidate(addr)) => { + return std::task::Poll::Ready(#network_behaviour_action::NewExternalAddrCandidate(addr)); + } + std::task::Poll::Ready(#network_behaviour_action::ExternalAddrConfirmed(addr)) => { + return std::task::Poll::Ready(#network_behaviour_action::ExternalAddrConfirmed(addr)); + } + std::task::Poll::Ready(#network_behaviour_action::ExternalAddrExpired(addr)) => { + return std::task::Poll::Ready(#network_behaviour_action::ExternalAddrExpired(addr)); } std::task::Poll::Ready(#network_behaviour_action::CloseConnection { peer_id, connection }) => { return std::task::Poll::Ready(#network_behaviour_action::CloseConnection { peer_id, connection }); @@ -834,12 +862,15 @@ fn build_struct(ast: &DeriveInput, data_struct: &DataStruct) -> syn::Result { #(#on_expired_listen_addr_stmts)* } - #from_swarm::NewExternalAddr( - #new_external_addr { addr }) - => { #(#on_new_external_addr_stmts)* } - #from_swarm::ExpiredExternalAddr( - #expired_external_addr { addr }) - => { #(#on_expired_external_addr_stmts)* } + #from_swarm::NewExternalAddrCandidate( + #new_external_addr_candidate { addr }) + => { #(#on_new_external_addr_candidate_stmts)* } + #from_swarm::ExternalAddrExpired( + #external_addr_expired { addr }) + => { #(#on_external_addr_expired_stmts)* } + #from_swarm::ExternalAddrConfirmed( + #external_addr_confirmed { addr }) + => { #(#on_external_addr_confirmed_stmts)* } #from_swarm::ListenerError( #listener_error { listener_id, err }) => { #(#on_listener_error_stmts)* } diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index c5e5b7f25c3..dde96082a0b 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -29,8 +29,8 @@ pub use listen_addresses::ListenAddresses; use crate::connection::ConnectionId; use crate::dial_opts::DialOpts; use crate::{ - AddressRecord, AddressScore, ConnectionDenied, ConnectionHandler, DialError, ListenError, - THandler, THandlerInEvent, THandlerOutEvent, + AddressRecord, ConnectionDenied, ConnectionHandler, DialError, ListenError, THandler, + THandlerInEvent, THandlerOutEvent, }; use libp2p_core::{transport::ListenerId, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -299,21 +299,20 @@ pub enum ToSwarm { event: TInEvent, }, - /// Informs the `Swarm` about an address observed by a remote for - /// the local node by which the local node is supposedly publicly - /// reachable. + /// Reports a new candidate for an external address to the [`Swarm`]. /// - /// It is advisable to issue `ReportObservedAddr` actions at a fixed frequency - /// per node. This way address information will be more accurate over time - /// and individual outliers carry less weight. - ReportObservedAddr { - /// The observed address of the local node. - address: Multiaddr, - /// The score to associate with this observation, i.e. - /// an indicator for the trusworthiness of this address - /// relative to other observed addresses. - score: AddressScore, - }, + /// This address could come from a variety of sources: + /// - A protocol such as identify obtained it from a remote. + /// - The user provided it based on configuration. + /// - We made an educated guess based on one of our listen addresses. + /// - We established a new relay connection. + NewExternalAddrCandidate(Multiaddr), + + /// Indicates to the [`Swarm`] that the provided address is confirmed to be externally reachable. + ExternalAddrConfirmed(Multiaddr), + + /// Indicates to the [`Swarm`] that we are no longer externally reachable under the provided address. + ExternalAddrExpired(Multiaddr), /// Instructs the `Swarm` to initiate a graceful close of one or all connections /// with the given peer. @@ -351,9 +350,6 @@ impl ToSwarm { handler, event: f(event), }, - ToSwarm::ReportObservedAddr { address, score } => { - ToSwarm::ReportObservedAddr { address, score } - } ToSwarm::CloseConnection { peer_id, connection, @@ -361,6 +357,9 @@ impl ToSwarm { peer_id, connection, }, + ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), + ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), + ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), } } } @@ -380,9 +379,9 @@ impl ToSwarm { handler, event, }, - ToSwarm::ReportObservedAddr { address, score } => { - ToSwarm::ReportObservedAddr { address, score } - } + ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), + ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), + ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), ToSwarm::CloseConnection { peer_id, connection, @@ -449,10 +448,12 @@ pub enum FromSwarm<'a, Handler> { ListenerError(ListenerError<'a>), /// Informs the behaviour that a listener closed. ListenerClosed(ListenerClosed<'a>), - /// Informs the behaviour that we have discovered a new external address for us. - NewExternalAddr(NewExternalAddr<'a>), + /// Informs the behaviour that we have discovered a new candidate for an external address for us. + NewExternalAddrCandidate(NewExternalAddrCandidate<'a>), + /// Informs the behaviour that an external address was removed. + ExternalAddrConfirmed(ExternalAddrConfirmed<'a>), /// Informs the behaviour that an external address was removed. - ExpiredExternalAddr(ExpiredExternalAddr<'a>), + ExternalAddrExpired(ExternalAddrExpired<'a>), } /// [`FromSwarm`] variant that informs the behaviour about a newly established connection to a peer. @@ -549,15 +550,21 @@ pub struct ListenerClosed<'a> { } /// [`FromSwarm`] variant that informs the behaviour -/// that we have discovered a new external address for us. +/// that we have discovered a new candidate for an external address for us. #[derive(Clone, Copy)] -pub struct NewExternalAddr<'a> { +pub struct NewExternalAddrCandidate<'a> { pub addr: &'a Multiaddr, } /// [`FromSwarm`] variant that informs the behaviour that an external address was removed. #[derive(Clone, Copy)] -pub struct ExpiredExternalAddr<'a> { +pub struct ExternalAddrConfirmed<'a> { + pub addr: &'a Multiaddr, +} + +/// [`FromSwarm`] variant that informs the behaviour that an external address was removed. +#[derive(Clone, Copy)] +pub struct ExternalAddrExpired<'a> { pub addr: &'a Multiaddr, } @@ -657,12 +664,9 @@ impl<'a, Handler> FromSwarm<'a, Handler> { listener_id, reason, })), - FromSwarm::NewExternalAddr(NewExternalAddr { addr }) => { - Some(FromSwarm::NewExternalAddr(NewExternalAddr { addr })) - } - FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr }) => { - Some(FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr })) - } + FromSwarm::NewExternalAddrCandidate(e) => Some(FromSwarm::NewExternalAddrCandidate(e)), + FromSwarm::ExternalAddrExpired(e) => Some(FromSwarm::ExternalAddrExpired(e)), + FromSwarm::ExternalAddrConfirmed(e) => Some(FromSwarm::ExternalAddrConfirmed(e)), } } } diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 49f540f8dfc..88cb2a88d63 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -1,4 +1,4 @@ -use crate::behaviour::{ExpiredExternalAddr, FromSwarm, NewExternalAddr}; +use crate::behaviour::{ExternalAddrExpired, FromSwarm, NewExternalAddrCandidate}; use libp2p_core::Multiaddr; use std::collections::HashSet; @@ -34,12 +34,12 @@ impl ExternalAddresses { /// Returns whether the event changed our set of external addresses. pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { match event { - FromSwarm::NewExternalAddr(NewExternalAddr { addr, .. }) => { + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr, .. }) => { if self.addresses.len() < self.limit { return self.addresses.insert((*addr).clone()); } } - FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr, .. }) => { + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr, .. }) => { return self.addresses.remove(addr) } _ => {} @@ -80,11 +80,11 @@ mod tests { } fn new_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::NewExternalAddr(NewExternalAddr { addr: &MEMORY_ADDR }) + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr: &MEMORY_ADDR }) } fn expired_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr: &MEMORY_ADDR }) + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: &MEMORY_ADDR }) } static MEMORY_ADDR: Lazy = diff --git a/swarm/src/dummy.rs b/swarm/src/dummy.rs index 563d86a667e..6810abec591 100644 --- a/swarm/src/dummy.rs +++ b/swarm/src/dummy.rs @@ -70,8 +70,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/swarm/src/keep_alive.rs b/swarm/src/keep_alive.rs index f91f80990a6..05cbcdf7b8c 100644 --- a/swarm/src/keep_alive.rs +++ b/swarm/src/keep_alive.rs @@ -73,8 +73,9 @@ impl NetworkBehaviour for Behaviour { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index f0f5f07cb97..6fe3d8a36c1 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -77,13 +77,14 @@ pub mod derive_prelude { pub use crate::behaviour::ConnectionClosed; pub use crate::behaviour::ConnectionEstablished; pub use crate::behaviour::DialFailure; - pub use crate::behaviour::ExpiredExternalAddr; pub use crate::behaviour::ExpiredListenAddr; + pub use crate::behaviour::ExternalAddrConfirmed; + pub use crate::behaviour::ExternalAddrExpired; pub use crate::behaviour::FromSwarm; pub use crate::behaviour::ListenFailure; pub use crate::behaviour::ListenerClosed; pub use crate::behaviour::ListenerError; - pub use crate::behaviour::NewExternalAddr; + pub use crate::behaviour::NewExternalAddrCandidate; pub use crate::behaviour::NewListenAddr; pub use crate::behaviour::NewListener; pub use crate::connection::ConnectionId; @@ -107,10 +108,10 @@ pub mod derive_prelude { } pub use behaviour::{ - AddressChange, CloseConnection, ConnectionClosed, DialFailure, ExpiredExternalAddr, - ExpiredListenAddr, ExternalAddresses, FromSwarm, ListenAddresses, ListenFailure, - ListenerClosed, ListenerError, NetworkBehaviour, NewExternalAddr, NewListenAddr, NotifyHandler, - PollParameters, ToSwarm, + AddressChange, CloseConnection, ConnectionClosed, DialFailure, ExpiredListenAddr, + ExternalAddrExpired, ExternalAddresses, FromSwarm, ListenAddresses, ListenFailure, + ListenerClosed, ListenerError, NetworkBehaviour, NewExternalAddrCandidate, NewListenAddr, + NotifyHandler, PollParameters, ToSwarm, }; pub use connection::pool::ConnectionCounters; pub use connection::{ConnectionError, ConnectionId, SupportedProtocols}; @@ -125,6 +126,7 @@ pub use registry::{AddAddressResult, AddressRecord, AddressScore}; pub use stream::Stream; pub use stream_protocol::{InvalidProtocol, StreamProtocol}; +use crate::behaviour::ExternalAddrConfirmed; use crate::handler::UpgradeInfoSend; use connection::pool::{EstablishedConnection, Pool, PoolConfig, PoolEvent}; use connection::IncomingInfo; @@ -664,14 +666,16 @@ where let expired = match &result { AddAddressResult::Inserted { expired } => { self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddr(NewExternalAddr { addr: &a })); + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &a }, + )); expired } AddAddressResult::Updated { expired } => expired, }; for a in expired { self.behaviour - .on_swarm_event(FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: &a.addr, })); } @@ -687,7 +691,7 @@ where pub fn remove_external_address(&mut self, addr: &Multiaddr) -> bool { if self.external_addrs.remove(addr) { self.behaviour - .on_swarm_event(FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr })); + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); true } else { false @@ -1136,35 +1140,53 @@ where self.pending_event = Some((peer_id, handler, event)); } - ToSwarm::ReportObservedAddr { address, score } => { - // Maps the given `observed_addr`, representing an address of the local - // node observed by a remote peer, onto the locally known listen addresses - // to yield one or more addresses of the local node that may be publicly - // reachable. - // - // I.e. self method incorporates the view of other peers into the listen - // addresses seen by the local node to account for possible IP and port - // mappings performed by intermediate network devices in an effort to - // obtain addresses for the local peer that are also reachable for peers - // other than the peer who reported the `observed_addr`. - // - // The translation is transport-specific. See [`Transport::address_translation`]. - let translated_addresses = { - let mut addrs: Vec<_> = self - .listened_addrs - .values() - .flatten() - .filter_map(|server| self.transport.address_translation(server, &address)) - .collect(); - - // remove duplicates - addrs.sort_unstable(); - addrs.dedup(); - addrs - }; - for addr in translated_addresses { - self.add_external_address(addr, score); - } + // ToSwarm::ReportObservedAddr { address, score } => { + // // Maps the given `observed_addr`, representing an address of the local + // // node observed by a remote peer, onto the locally known listen addresses + // // to yield one or more addresses of the local node that may be publicly + // // reachable. + // // + // // I.e. self method incorporates the view of other peers into the listen + // // addresses seen by the local node to account for possible IP and port + // // mappings performed by intermediate network devices in an effort to + // // obtain addresses for the local peer that are also reachable for peers + // // other than the peer who reported the `observed_addr`. + // // + // // The translation is transport-specific. See [`Transport::address_translation`]. + // let translated_addresses = { + // let mut addrs: Vec<_> = self + // .listened_addrs + // .values() + // .flatten() + // .filter_map(|server| self.transport.address_translation(server, &address)) + // .collect(); + // + // // remove duplicates + // addrs.sort_unstable(); + // addrs.dedup(); + // addrs + // }; + // for addr in translated_addresses { + // self.add_external_address(addr, score); + // } + // } + ToSwarm::NewExternalAddrCandidate(addr) => { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + } + ToSwarm::ExternalAddrConfirmed(addr) => { + self.behaviour + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &addr, + })); + } + ToSwarm::ExternalAddrExpired(addr) => { + self.behaviour + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { + addr: &addr, + })); } ToSwarm::CloseConnection { peer_id, diff --git a/swarm/src/test.rs b/swarm/src/test.rs index 70d4e790956..6f39d56da91 100644 --- a/swarm/src/test.rs +++ b/swarm/src/test.rs @@ -19,8 +19,8 @@ // DEALINGS IN THE SOFTWARE. use crate::behaviour::{ - ConnectionClosed, ConnectionEstablished, DialFailure, ExpiredExternalAddr, ExpiredListenAddr, - FromSwarm, ListenerClosed, ListenerError, NewExternalAddr, NewListenAddr, NewListener, + ConnectionClosed, ConnectionEstablished, DialFailure, ExpiredListenAddr, ExternalAddrExpired, + FromSwarm, ListenerClosed, ListenerError, NewExternalAddrCandidate, NewListenAddr, NewListener, }; use crate::{ ConnectionDenied, ConnectionHandler, ConnectionId, NetworkBehaviour, PollParameters, THandler, @@ -130,8 +130,9 @@ where | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } @@ -500,15 +501,17 @@ where addr, })); } - FromSwarm::NewExternalAddr(NewExternalAddr { addr }) => { + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { self.on_new_external_addr.push(addr.clone()); self.inner - .on_swarm_event(FromSwarm::NewExternalAddr(NewExternalAddr { addr })); + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr }, + )); } - FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr }) => { + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr }) => { self.on_expired_external_addr.push(addr.clone()); self.inner - .on_swarm_event(FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { addr })); + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); } FromSwarm::ListenerError(ListenerError { listener_id, err }) => { self.on_listener_error.push(listener_id); diff --git a/swarm/tests/swarm_derive.rs b/swarm/tests/swarm_derive.rs index ab1efaca396..fa3f6c69dd0 100644 --- a/swarm/tests/swarm_derive.rs +++ b/swarm/tests/swarm_derive.rs @@ -518,8 +518,9 @@ fn custom_out_event_no_type_parameters() { | FromSwarm::ExpiredListenAddr(_) | FromSwarm::ListenerError(_) | FromSwarm::ListenerClosed(_) - | FromSwarm::NewExternalAddr(_) - | FromSwarm::ExpiredExternalAddr(_) => {} + | FromSwarm::NewExternalAddrCandidate(_) + | FromSwarm::ExternalAddrExpired(_) + | FromSwarm::ExternalAddrConfirmed(_) => {} } } } From d5f45b5e7fba58f2fec9b106d1c4cdec2d307c14 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 May 2023 14:06:59 +0200 Subject: [PATCH 056/105] Generate candidates based on address translation --- swarm/src/lib.rs | 53 +++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 6fe3d8a36c1..ba55d784cfa 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -1140,41 +1140,34 @@ where self.pending_event = Some((peer_id, handler, event)); } - // ToSwarm::ReportObservedAddr { address, score } => { - // // Maps the given `observed_addr`, representing an address of the local - // // node observed by a remote peer, onto the locally known listen addresses - // // to yield one or more addresses of the local node that may be publicly - // // reachable. - // // - // // I.e. self method incorporates the view of other peers into the listen - // // addresses seen by the local node to account for possible IP and port - // // mappings performed by intermediate network devices in an effort to - // // obtain addresses for the local peer that are also reachable for peers - // // other than the peer who reported the `observed_addr`. - // // - // // The translation is transport-specific. See [`Transport::address_translation`]. - // let translated_addresses = { - // let mut addrs: Vec<_> = self - // .listened_addrs - // .values() - // .flatten() - // .filter_map(|server| self.transport.address_translation(server, &address)) - // .collect(); - // - // // remove duplicates - // addrs.sort_unstable(); - // addrs.dedup(); - // addrs - // }; - // for addr in translated_addresses { - // self.add_external_address(addr, score); - // } - // } ToSwarm::NewExternalAddrCandidate(addr) => { self.behaviour .on_swarm_event(FromSwarm::NewExternalAddrCandidate( NewExternalAddrCandidate { addr: &addr }, )); + + // Generate more candidates based on address translation. + // For TCP without port-reuse, the observed address contains an ephemeral port which needs to be replaced by the port of a listen address. + + let translated_addresses = { + let mut addrs: Vec<_> = self + .listened_addrs + .values() + .flatten() + .filter_map(|server| self.transport.address_translation(server, &addr)) + .collect(); + + // remove duplicates + addrs.sort_unstable(); + addrs.dedup(); + addrs + }; + for addr in translated_addresses { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + } } ToSwarm::ExternalAddrConfirmed(addr) => { self.behaviour From ab12bb7e8fb4d46b9d8e674b4fbf089bda7cc00d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 May 2023 14:14:24 +0200 Subject: [PATCH 057/105] Use candidates in AutoNAT --- protocols/autonat/src/behaviour.rs | 17 ++++++++++------- protocols/autonat/src/behaviour/as_client.rs | 6 +++--- swarm/src/behaviour/external_addresses.rs | 6 +++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index 30e70e48768..b5381d62af6 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -39,7 +39,7 @@ use libp2p_swarm::{ AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, ExpiredListenAddr, ExternalAddrExpired, FromSwarm, }, - ConnectionDenied, ConnectionId, ExternalAddresses, ListenAddresses, NetworkBehaviour, + ConnectionDenied, ConnectionId, ListenAddresses, NetworkBehaviour, NewExternalAddrCandidate, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, }; use std::{ @@ -214,7 +214,7 @@ pub struct Behaviour { probe_id: ProbeId, listen_addresses: ListenAddresses, - external_addresses: ExternalAddresses, + other_candidates: HashSet, } impl Behaviour { @@ -240,7 +240,7 @@ impl Behaviour { pending_actions: VecDeque::new(), probe_id: ProbeId(0), listen_addresses: Default::default(), - external_addresses: Default::default(), + other_candidates: Default::default(), } } @@ -294,7 +294,7 @@ impl Behaviour { last_probe: &mut self.last_probe, schedule_probe: &mut self.schedule_probe, listen_addresses: &self.listen_addresses, - external_addresses: &self.external_addresses, + other_candidates: &self.other_candidates, } } @@ -532,7 +532,6 @@ impl NetworkBehaviour for Behaviour { fn on_swarm_event(&mut self, event: FromSwarm) { self.listen_addresses.on_swarm_event(&event); - self.external_addresses.on_swarm_event(&event); match event { FromSwarm::ConnectionEstablished(connection_established) => { @@ -566,8 +565,12 @@ impl NetworkBehaviour for Behaviour { .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); self.as_client().on_expired_address(addr); } - external_addr @ FromSwarm::NewExternalAddrCandidate(_) => { - self.inner.on_swarm_event(external_addr); + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { + self.inner + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr }, + )); + self.other_candidates.insert(addr.to_owned()); self.as_client().on_new_address(); } listen_failure @ FromSwarm::ListenFailure(_) => { diff --git a/protocols/autonat/src/behaviour/as_client.rs b/protocols/autonat/src/behaviour/as_client.rs index 6149a6b35c7..e57523afaf8 100644 --- a/protocols/autonat/src/behaviour/as_client.rs +++ b/protocols/autonat/src/behaviour/as_client.rs @@ -30,7 +30,7 @@ use instant::Instant; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; use libp2p_request_response::{self as request_response, OutboundFailure, RequestId}; -use libp2p_swarm::{ConnectionId, ExternalAddresses, ListenAddresses, PollParameters, ToSwarm}; +use libp2p_swarm::{ConnectionId, ListenAddresses, PollParameters, ToSwarm}; use rand::{seq::SliceRandom, thread_rng}; use std::{ collections::{HashMap, HashSet, VecDeque}, @@ -95,7 +95,7 @@ pub(crate) struct AsClient<'a> { pub(crate) last_probe: &'a mut Option, pub(crate) schedule_probe: &'a mut Delay, pub(crate) listen_addresses: &'a ListenAddresses, - pub(crate) external_addresses: &'a ExternalAddresses, + pub(crate) other_candidates: &'a HashSet, } impl<'a> HandleInnerEvent for AsClient<'a> { @@ -187,7 +187,7 @@ impl<'a> AsClient<'a> { self.schedule_probe.reset(self.config.retry_interval); let addresses = self - .external_addresses + .other_candidates .iter() .chain(self.listen_addresses.iter()) .cloned() diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 88cb2a88d63..89e06e26608 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -1,4 +1,4 @@ -use crate::behaviour::{ExternalAddrExpired, FromSwarm, NewExternalAddrCandidate}; +use crate::behaviour::{ExternalAddrConfirmed, ExternalAddrExpired, FromSwarm}; use libp2p_core::Multiaddr; use std::collections::HashSet; @@ -34,7 +34,7 @@ impl ExternalAddresses { /// Returns whether the event changed our set of external addresses. pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { match event { - FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr, .. }) => { + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { if self.addresses.len() < self.limit { return self.addresses.insert((*addr).clone()); } @@ -80,7 +80,7 @@ mod tests { } fn new_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr: &MEMORY_ADDR }) + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR }) } fn expired_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { From 43058e1e6d31d71a8fc352527c854ad2723248a9 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 May 2023 14:28:43 +0200 Subject: [PATCH 058/105] Only keep minimal external address state in swarm --- examples/rendezvous/src/bin/rzv-register.rs | 4 +- misc/allow-block-list/src/lib.rs | 15 +- protocols/autonat/tests/test_client.rs | 6 +- protocols/autonat/tests/test_server.rs | 12 +- protocols/relay/tests/lib.rs | 10 +- swarm-test/src/lib.rs | 11 +- swarm/src/behaviour.rs | 13 +- swarm/src/lib.rs | 83 +--- swarm/src/registry.rs | 504 -------------------- 9 files changed, 38 insertions(+), 620 deletions(-) delete mode 100644 swarm/src/registry.rs diff --git a/examples/rendezvous/src/bin/rzv-register.rs b/examples/rendezvous/src/bin/rzv-register.rs index 40053aa96b9..03aa56978a3 100644 --- a/examples/rendezvous/src/bin/rzv-register.rs +++ b/examples/rendezvous/src/bin/rzv-register.rs @@ -22,7 +22,7 @@ use futures::StreamExt; use libp2p::{ core::transport::upgrade::Version, identity, noise, ping, rendezvous, - swarm::{keep_alive, AddressScore, NetworkBehaviour, SwarmBuilder, SwarmEvent}, + swarm::{keep_alive, NetworkBehaviour, SwarmBuilder, SwarmEvent}, tcp, yamux, Multiaddr, PeerId, Transport, }; use std::time::Duration; @@ -55,7 +55,7 @@ async fn main() { // In production the external address should be the publicly facing IP address of the rendezvous point. // This address is recorded in the registration entry by the rendezvous point. let external_address = "/ip4/127.0.0.1/tcp/0".parse::().unwrap(); - swarm.add_external_address(external_address, AddressScore::Infinite); + swarm.add_external_address(external_address); log::info!("Local peer id: {}", swarm.local_peer_id()); diff --git a/misc/allow-block-list/src/lib.rs b/misc/allow-block-list/src/lib.rs index eba747462fe..7aa0dd87822 100644 --- a/misc/allow-block-list/src/lib.rs +++ b/misc/allow-block-list/src/lib.rs @@ -411,13 +411,7 @@ mod tests { dialer .dial( DialOpts::unknown_peer_id() - .address( - listener - .external_addresses() - .map(|a| a.addr.clone()) - .next() - .unwrap(), - ) + .address(listener.external_addresses().next().cloned().unwrap()) .build(), ) .unwrap(); @@ -471,12 +465,7 @@ mod tests { { dialer.dial( DialOpts::peer_id(*listener.local_peer_id()) - .addresses( - listener - .external_addresses() - .map(|a| a.addr.clone()) - .collect(), - ) + .addresses(listener.external_addresses().cloned().collect()) .build(), ) } diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 221833c9377..d4708f542f9 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -24,7 +24,7 @@ use libp2p_autonat::{ }; use libp2p_core::Multiaddr; use libp2p_identity::PeerId; -use libp2p_swarm::{AddressScore, Swarm, SwarmEvent}; +use libp2p_swarm::{Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt as _; use std::time::Duration; @@ -74,7 +74,7 @@ async fn test_auto_probe() { // Artificially add a faulty address. let unreachable_addr: Multiaddr = "/ip4/127.0.0.1/tcp/42".parse().unwrap(); - client.add_external_address(unreachable_addr.clone(), AddressScore::Infinite); + client.add_external_address(unreachable_addr.clone()); let id = match client.next_behaviour_event().await { Event::OutboundProbe(OutboundProbeEvent::Request { probe_id, peer }) => { @@ -198,7 +198,7 @@ async fn test_confidence() { client.listen().await; } else { let unreachable_addr = "/ip4/127.0.0.1/tcp/42".parse().unwrap(); - client.add_external_address(unreachable_addr, AddressScore::Infinite); + client.add_external_address(unreachable_addr); } for i in 0..MAX_CONFIDENCE + 1 { diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index 319fd84865d..fa0ec5367c8 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -24,7 +24,7 @@ use libp2p_autonat::{ use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::DialError; -use libp2p_swarm::{AddressScore, Swarm, SwarmEvent}; +use libp2p_swarm::{Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt as _; use std::{num::NonZeroU32, time::Duration}; @@ -127,10 +127,7 @@ async fn test_dial_back() { async fn test_dial_error() { let (mut server, server_id, server_addr) = new_server_swarm(None).await; let (mut client, client_id) = new_client_swarm(server_id, server_addr).await; - client.add_external_address( - "/ip4/127.0.0.1/tcp/12345".parse().unwrap(), - AddressScore::Infinite, - ); + client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); async_std::task::spawn(client.loop_on_next()); let request_probe_id = match server.next_behaviour_event().await { @@ -267,10 +264,7 @@ async fn test_dial_multiple_addr() { let (mut client, client_id) = new_client_swarm(server_id, server_addr.clone()).await; client.listen().await; - client.add_external_address( - "/ip4/127.0.0.1/tcp/12345".parse().unwrap(), - AddressScore::Infinite, - ); + client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); async_std::task::spawn(client.loop_on_next()); let dial_addresses = match server.next_behaviour_event().await { diff --git a/protocols/relay/tests/lib.rs b/protocols/relay/tests/lib.rs index 2103893ba12..98fe2873f09 100644 --- a/protocols/relay/tests/lib.rs +++ b/protocols/relay/tests/lib.rs @@ -34,7 +34,7 @@ use libp2p_identity::PublicKey; use libp2p_ping as ping; use libp2p_plaintext::PlainText2Config; use libp2p_relay as relay; -use libp2p_swarm::{AddressScore, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; +use libp2p_swarm::{NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent}; use std::time::Duration; #[test] @@ -47,7 +47,7 @@ fn reservation() { let relay_peer_id = *relay.local_peer_id(); relay.listen_on(relay_addr.clone()).unwrap(); - relay.add_external_address(relay_addr.clone(), AddressScore::Infinite); + relay.add_external_address(relay_addr.clone()); spawn_swarm_on_pool(&pool, relay); let client_addr = relay_addr @@ -90,7 +90,7 @@ fn new_reservation_to_same_relay_replaces_old() { let relay_peer_id = *relay.local_peer_id(); relay.listen_on(relay_addr.clone()).unwrap(); - relay.add_external_address(relay_addr.clone(), AddressScore::Infinite); + relay.add_external_address(relay_addr.clone()); spawn_swarm_on_pool(&pool, relay); let mut client = build_client(); @@ -183,7 +183,7 @@ fn connect() { let relay_peer_id = *relay.local_peer_id(); relay.listen_on(relay_addr.clone()).unwrap(); - relay.add_external_address(relay_addr.clone(), AddressScore::Infinite); + relay.add_external_address(relay_addr.clone()); spawn_swarm_on_pool(&pool, relay); let mut dst = build_client(); @@ -278,7 +278,7 @@ fn reuse_connection() { let relay_peer_id = *relay.local_peer_id(); relay.listen_on(relay_addr.clone()).unwrap(); - relay.add_external_address(relay_addr.clone(), AddressScore::Infinite); + relay.add_external_address(relay_addr.clone()); spawn_swarm_on_pool(&pool, relay); let client_addr = relay_addr diff --git a/swarm-test/src/lib.rs b/swarm-test/src/lib.rs index 72a9821cced..e70c64bbfd1 100644 --- a/swarm-test/src/lib.rs +++ b/swarm-test/src/lib.rs @@ -29,8 +29,7 @@ use libp2p_identity::PeerId; use libp2p_plaintext::PlainText2Config; use libp2p_swarm::dial_opts::PeerCondition; use libp2p_swarm::{ - dial_opts::DialOpts, AddressScore, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent, - THandlerErr, + dial_opts::DialOpts, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent, THandlerErr, }; use libp2p_yamux as yamux; use std::fmt::Debug; @@ -228,11 +227,7 @@ where T: NetworkBehaviour + Send, ::ToSwarm: Debug, { - let external_addresses = other - .external_addresses() - .cloned() - .map(|r| r.addr) - .collect(); + let external_addresses = other.external_addresses().cloned().collect(); let dial_opts = DialOpts::peer_id(*other.local_peer_id()) .addresses(external_addresses) @@ -315,7 +310,7 @@ where .await; // Memory addresses are externally reachable because they all share the same memory-space. - self.add_external_address(memory_multiaddr.clone(), AddressScore::Infinite); + self.add_external_address(memory_multiaddr.clone()); let tcp_addr_listener_id = self .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index dde96082a0b..8e1707b0300 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -29,8 +29,8 @@ pub use listen_addresses::ListenAddresses; use crate::connection::ConnectionId; use crate::dial_opts::DialOpts; use crate::{ - AddressRecord, ConnectionDenied, ConnectionHandler, DialError, ListenError, THandler, - THandlerInEvent, THandlerOutEvent, + ConnectionDenied, ConnectionHandler, DialError, ListenError, THandler, THandlerInEvent, + THandlerOutEvent, }; use libp2p_core::{transport::ListenerId, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; @@ -221,8 +221,6 @@ pub trait PollParameters { type SupportedProtocolsIter: ExactSizeIterator>; /// Iterator returned by [`listened_addresses`](PollParameters::listened_addresses). type ListenedAddressesIter: ExactSizeIterator; - /// Iterator returned by [`external_addresses`](PollParameters::external_addresses). - type ExternalAddressesIter: ExactSizeIterator; /// Returns the list of protocol the behaviour supports when a remote negotiates a protocol on /// an inbound substream. @@ -242,13 +240,6 @@ pub trait PollParameters { )] fn listened_addresses(&self) -> Self::ListenedAddressesIter; - /// Returns the list of the addresses nodes can use to reach us. - #[deprecated( - since = "0.42.0", - note = "Use `libp2p_swarm::ExternalAddresses` instead." - )] - fn external_addresses(&self) -> Self::ExternalAddressesIter; - /// Returns the peer id of the local node. #[deprecated( since = "0.42.0", diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index ba55d784cfa..8e80a65ac65 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -57,7 +57,6 @@ mod connection; mod executor; -mod registry; mod stream; mod stream_protocol; #[cfg(test)] @@ -122,7 +121,6 @@ pub use handler::{ }; #[cfg(feature = "macros")] pub use libp2p_swarm_derive::NetworkBehaviour; -pub use registry::{AddAddressResult, AddressRecord, AddressScore}; pub use stream::Stream; pub use stream_protocol::{InvalidProtocol, StreamProtocol}; @@ -144,7 +142,6 @@ use libp2p_core::{ Endpoint, Multiaddr, Transport, }; use libp2p_identity::PeerId; -use registry::{AddressIntoIter, Addresses}; use smallvec::SmallVec; use std::collections::{HashMap, HashSet}; use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; @@ -326,13 +323,11 @@ where /// List of protocols that the behaviour says it supports. supported_protocols: SmallVec<[Vec; 16]>, + confirmed_external_addr: HashSet, + /// Multiaddresses that our listeners are listening on, listened_addrs: HashMap>, - /// List of multiaddresses we're listening on, after account for external IP addresses and - /// similar mechanisms. - external_addrs: Addresses, - /// Pending event to be delivered to connection handlers /// (or dropped if the peer disconnected) before the `behaviour` /// can be polled again. @@ -640,62 +635,25 @@ where &self.local_peer_id } - /// Returns an iterator for [`AddressRecord`]s of external addresses - /// of the local node, in decreasing order of their current - /// [score](AddressScore). - pub fn external_addresses(&self) -> impl Iterator { - self.external_addrs.iter() + /// TODO + pub fn external_addresses(&self) -> impl Iterator { + self.confirmed_external_addr.iter() } /// Adds an external address record for the local node. /// - /// An external address is an address of the local node known to - /// be (likely) reachable for other nodes, possibly taking into - /// account NAT. The external addresses of the local node may be - /// shared with other nodes by the `NetworkBehaviour`. - /// - /// The associated score determines both the position of the address - /// in the list of external addresses (which can determine the - /// order in which addresses are used to connect to) as well as - /// how long the address is retained in the list, depending on - /// how frequently it is reported by the `NetworkBehaviour` via - /// [`ToSwarm::ReportObservedAddr`] or explicitly - /// through this method. - pub fn add_external_address(&mut self, a: Multiaddr, s: AddressScore) -> AddAddressResult { - let result = self.external_addrs.add(a.clone(), s); - let expired = match &result { - AddAddressResult::Inserted { expired } => { - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &a }, - )); - expired - } - AddAddressResult::Updated { expired } => expired, - }; - for a in expired { - self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { - addr: &a.addr, - })); - } - result + /// TODO + pub fn add_external_address(&mut self, a: Multiaddr) { + self.behaviour + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &a, + })) } - /// Removes an external address of the local node, regardless of - /// its current score. See [`Swarm::add_external_address`] - /// for details. - /// - /// Returns `true` if the address existed and was removed, `false` - /// otherwise. - pub fn remove_external_address(&mut self, addr: &Multiaddr) -> bool { - if self.external_addrs.remove(addr) { - self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); - true - } else { - false - } + /// TODO + pub fn remove_external_address(&mut self, addr: &Multiaddr) { + self.behaviour + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })) } /// Disconnects a peer by its peer ID, closing all connections to said peer. @@ -1174,12 +1132,14 @@ where .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &addr, })); + self.confirmed_external_addr.insert(addr); } ToSwarm::ExternalAddrExpired(addr) => { self.behaviour .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: &addr, })); + self.confirmed_external_addr.remove(&addr); } ToSwarm::CloseConnection { peer_id, @@ -1252,7 +1212,6 @@ where local_peer_id: &this.local_peer_id, supported_protocols: &this.supported_protocols, listened_addrs: this.listened_addrs.values().flatten().collect(), - external_addrs: &this.external_addrs, }; this.behaviour.poll(cx, &mut parameters) }; @@ -1420,13 +1379,11 @@ pub struct SwarmPollParameters<'a> { local_peer_id: &'a PeerId, supported_protocols: &'a [Vec], listened_addrs: Vec<&'a Multiaddr>, - external_addrs: &'a Addresses, } impl<'a> PollParameters for SwarmPollParameters<'a> { type SupportedProtocolsIter = std::iter::Cloned>>; type ListenedAddressesIter = std::iter::Cloned>; - type ExternalAddressesIter = AddressIntoIter; fn supported_protocols(&self) -> Self::SupportedProtocolsIter { self.supported_protocols.iter().cloned() @@ -1436,10 +1393,6 @@ impl<'a> PollParameters for SwarmPollParameters<'a> { self.listened_addrs.clone().into_iter().cloned() } - fn external_addresses(&self) -> Self::ExternalAddressesIter { - self.external_addrs.clone().into_iter() - } - fn local_peer_id(&self) -> &PeerId { self.local_peer_id } @@ -1628,8 +1581,8 @@ where pool: Pool::new(self.local_peer_id, self.pool_config), behaviour: self.behaviour, supported_protocols: Default::default(), + confirmed_external_addr: Default::default(), listened_addrs: HashMap::new(), - external_addrs: Addresses::default(), pending_event: None, } } diff --git a/swarm/src/registry.rs b/swarm/src/registry.rs deleted file mode 100644 index 7f8225a6a25..00000000000 --- a/swarm/src/registry.rs +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -use libp2p_core::Multiaddr; -use smallvec::SmallVec; -use std::ops::{Add, Sub}; -use std::{cmp::Ordering, collections::VecDeque, num::NonZeroUsize}; - -/// A ranked collection of [`Multiaddr`] values. -/// -/// Every address has an associated [score](`AddressScore`) and iterating -/// over the addresses will return them in order from highest to lowest score. -/// -/// In addition to the currently held addresses and their score, the collection -/// keeps track of a limited history of the most-recently added addresses. -/// This history determines how address scores are reduced over time as old -/// scores expire in the context of new addresses being added: -/// -/// * An address's score is increased by a given amount whenever it is -/// [(re-)added](Addresses::add) to the collection. -/// * An address's score is decreased by the same amount used when it -/// was added when the least-recently seen addition is (as per the -/// limited history) for this address in the context of [`Addresses::add`]. -/// * If an address's score reaches 0 in the context of [`Addresses::add`], -/// it is removed from the collection. -/// -#[derive(Debug, Clone)] -pub(crate) struct Addresses { - /// The ranked sequence of addresses, from highest to lowest score. - /// - /// By design, the number of finitely scored addresses stored here is - /// never larger (but may be smaller) than the number of historic `reports` - /// at any time. - registry: SmallVec<[AddressRecord; 8]>, - /// The configured limit of the `reports` history of added addresses, - /// and thus also of the size of the `registry` w.r.t. finitely scored - /// addresses. - limit: NonZeroUsize, - /// The limited history of added addresses. If the queue reaches the `limit`, - /// the first record, i.e. the least-recently added, is removed in the - /// context of [`Addresses::add`] and the corresponding record in the - /// `registry` has its score reduced accordingly. - reports: VecDeque, -} - -/// An record in a prioritised list of addresses. -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub struct AddressRecord { - pub addr: Multiaddr, - pub score: AddressScore, -} - -/// A report tracked for a finitely scored address. -#[derive(Debug, Clone)] -struct Report { - addr: Multiaddr, - score: u32, -} - -impl AddressRecord { - fn new(addr: Multiaddr, score: AddressScore) -> Self { - AddressRecord { addr, score } - } -} - -/// The "score" of an address w.r.t. an ordered collection of addresses. -/// -/// A score is a measure of the trusworthyness of a particular -/// observation of an address. The same address may be repeatedly -/// reported with the same or differing scores. -#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)] -pub enum AddressScore { - /// The score is "infinite", i.e. an address with this score is never - /// purged from the associated address records and remains sorted at - /// the beginning (possibly with other `Infinite`ly scored addresses). - Infinite, - /// The score is finite, i.e. an address with this score has - /// its score increased and decreased as per the frequency of - /// reports (i.e. additions) of the same address relative to - /// the reports of other addresses. - Finite(u32), -} - -impl AddressScore { - fn is_zero(&self) -> bool { - &AddressScore::Finite(0) == self - } -} - -impl PartialOrd for AddressScore { - fn partial_cmp(&self, other: &AddressScore) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for AddressScore { - fn cmp(&self, other: &AddressScore) -> Ordering { - // Semantics of cardinal numbers with a single infinite cardinal. - match (self, other) { - (AddressScore::Infinite, AddressScore::Infinite) => Ordering::Equal, - (AddressScore::Infinite, AddressScore::Finite(_)) => Ordering::Greater, - (AddressScore::Finite(_), AddressScore::Infinite) => Ordering::Less, - (AddressScore::Finite(a), AddressScore::Finite(b)) => a.cmp(b), - } - } -} - -impl Add for AddressScore { - type Output = AddressScore; - - fn add(self, rhs: AddressScore) -> Self::Output { - // Semantics of cardinal numbers with a single infinite cardinal. - match (self, rhs) { - (AddressScore::Infinite, AddressScore::Infinite) => AddressScore::Infinite, - (AddressScore::Infinite, AddressScore::Finite(_)) => AddressScore::Infinite, - (AddressScore::Finite(_), AddressScore::Infinite) => AddressScore::Infinite, - (AddressScore::Finite(a), AddressScore::Finite(b)) => { - AddressScore::Finite(a.saturating_add(b)) - } - } - } -} - -impl Sub for AddressScore { - type Output = AddressScore; - - fn sub(self, rhs: u32) -> Self::Output { - // Semantics of cardinal numbers with a single infinite cardinal. - match self { - AddressScore::Infinite => AddressScore::Infinite, - AddressScore::Finite(score) => AddressScore::Finite(score.saturating_sub(rhs)), - } - } -} - -impl Default for Addresses { - fn default() -> Self { - Addresses::new(NonZeroUsize::new(200).expect("200 > 0")) - } -} - -/// The result of adding an address to an ordered list of -/// addresses with associated scores. -pub enum AddAddressResult { - Inserted { - expired: SmallVec<[AddressRecord; 8]>, - }, - Updated { - expired: SmallVec<[AddressRecord; 8]>, - }, -} - -impl Addresses { - /// Create a new ranked address collection with the given size limit - /// for [finitely scored](AddressScore::Finite) addresses. - pub(crate) fn new(limit: NonZeroUsize) -> Self { - Addresses { - registry: SmallVec::new(), - limit, - reports: VecDeque::with_capacity(limit.get()), - } - } - - /// Add a [`Multiaddr`] to the collection. - /// - /// If the given address already exists in the collection, - /// the given score is added to the current score of the address. - /// - /// If the collection has already observed the configured - /// number of address additions, the least-recently added address - /// as per this limited history has its score reduced by the amount - /// used in this prior report, with removal from the collection - /// occurring when the score drops to 0. - pub(crate) fn add(&mut self, addr: Multiaddr, score: AddressScore) -> AddAddressResult { - // If enough reports (i.e. address additions) occurred, reduce - // the score of the least-recently added address. - if self.reports.len() == self.limit.get() { - let old_report = self.reports.pop_front().expect("len = limit > 0"); - // If the address is still in the collection, decrease its score. - if let Some(record) = self.registry.iter_mut().find(|r| r.addr == old_report.addr) { - record.score = record.score - old_report.score; - isort(&mut self.registry); - } - } - - // Remove addresses that have a score of 0. - let mut expired = SmallVec::new(); - while self - .registry - .last() - .map(|e| e.score.is_zero()) - .unwrap_or(false) - { - if let Some(addr) = self.registry.pop() { - expired.push(addr); - } - } - - // If the address score is finite, remember this report. - if let AddressScore::Finite(score) = score { - self.reports.push_back(Report { - addr: addr.clone(), - score, - }); - } - - // If the address is already in the collection, increase its score. - for r in &mut self.registry { - if r.addr == addr { - r.score = r.score + score; - isort(&mut self.registry); - return AddAddressResult::Updated { expired }; - } - } - - // It is a new record. - self.registry.push(AddressRecord::new(addr, score)); - AddAddressResult::Inserted { expired } - } - - /// Explicitly remove an address from the collection. - /// - /// Returns `true` if the address existed in the collection - /// and was thus removed, false otherwise. - pub(crate) fn remove(&mut self, addr: &Multiaddr) -> bool { - if let Some(pos) = self.registry.iter().position(|r| &r.addr == addr) { - self.registry.remove(pos); - true - } else { - false - } - } - - /// Return an iterator over all [`Multiaddr`] values. - /// - /// The iteration is ordered by descending score. - pub(crate) fn iter(&self) -> AddressIter<'_> { - AddressIter { - items: &self.registry, - offset: 0, - } - } - - /// Return an iterator over all [`Multiaddr`] values. - /// - /// The iteration is ordered by descending score. - pub(crate) fn into_iter(self) -> AddressIntoIter { - AddressIntoIter { - items: self.registry, - } - } -} - -/// An iterator over [`Multiaddr`] values. -#[derive(Clone)] -pub(crate) struct AddressIter<'a> { - items: &'a [AddressRecord], - offset: usize, -} - -impl<'a> Iterator for AddressIter<'a> { - type Item = &'a AddressRecord; - - fn next(&mut self) -> Option { - if self.offset == self.items.len() { - return None; - } - let item = &self.items[self.offset]; - self.offset += 1; - Some(item) - } - - fn size_hint(&self) -> (usize, Option) { - let n = self.items.len() - self.offset; - (n, Some(n)) - } -} - -impl<'a> ExactSizeIterator for AddressIter<'a> {} - -/// An iterator over [`Multiaddr`] values. -#[derive(Clone)] -pub struct AddressIntoIter { - items: SmallVec<[AddressRecord; 8]>, -} - -impl Iterator for AddressIntoIter { - type Item = AddressRecord; - - fn next(&mut self) -> Option { - if !self.items.is_empty() { - Some(self.items.remove(0)) - } else { - None - } - } - - fn size_hint(&self) -> (usize, Option) { - let n = self.items.len(); - (n, Some(n)) - } -} - -impl ExactSizeIterator for AddressIntoIter {} - -// Reverse insertion sort. -fn isort(xs: &mut [AddressRecord]) { - for i in 1..xs.len() { - for j in (1..=i).rev() { - if xs[j].score <= xs[j - 1].score { - break; - } - xs.swap(j, j - 1) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use libp2p_core::multiaddr::{Multiaddr, Protocol}; - use quickcheck::*; - use std::num::{NonZeroU8, NonZeroUsize}; - - impl Arbitrary for AddressScore { - fn arbitrary(g: &mut Gen) -> AddressScore { - if g.gen_range(0..10u8) == 0 { - // ~10% "Infinitely" scored addresses - AddressScore::Infinite - } else { - AddressScore::Finite(Arbitrary::arbitrary(g)) - } - } - } - - impl Arbitrary for AddressRecord { - fn arbitrary(g: &mut Gen) -> Self { - let addr = Protocol::Tcp(g.gen_range(0..256)).into(); - let score = AddressScore::arbitrary(g); - AddressRecord::new(addr, score) - } - } - - #[test] - fn isort_sorts() { - fn property(xs: Vec) { - let mut xs = xs - .into_iter() - .map(|score| AddressRecord::new(Multiaddr::empty(), score)) - .collect::>(); - - isort(&mut xs); - - for i in 1..xs.len() { - assert!(xs[i - 1].score >= xs[i].score) - } - } - - quickcheck(property as fn(_)); - } - - #[test] - fn score_retention() { - fn prop(first: AddressRecord, other: AddressRecord) -> TestResult { - if first.addr == other.addr || first.score.is_zero() { - return TestResult::discard(); - } - - let mut addresses = Addresses::default(); - - // Add the first address. - addresses.add(first.addr.clone(), first.score); - assert!(addresses.iter().any(|a| a.addr == first.addr)); - - // Add another address so often that the initial report of - // the first address may be purged and, since it was the - // only report, the address removed. - for _ in 0..addresses.limit.get() + 1 { - addresses.add(other.addr.clone(), other.score); - } - - let exists = addresses.iter().any(|a| a.addr == first.addr); - - match (first.score, other.score) { - // Only finite scores push out other finite scores. - (AddressScore::Finite(_), AddressScore::Finite(_)) => assert!(!exists), - _ => assert!(exists), - } - - TestResult::passed() - } - - quickcheck(prop as fn(_, _) -> _); - } - - #[test] - fn score_retention_finite_0() { - let first = { - let addr = Protocol::Tcp(42).into(); - let score = AddressScore::Finite(0); - AddressRecord::new(addr, score) - }; - let other = { - let addr = Protocol::Udp(42).into(); - let score = AddressScore::Finite(42); - AddressRecord::new(addr, score) - }; - - let mut addresses = Addresses::default(); - - // Add the first address. - addresses.add(first.addr.clone(), first.score); - assert!(addresses.iter().any(|a| a.addr == first.addr)); - - // Add another address so the first will address be purged, - // because its score is finite(0) - addresses.add(other.addr.clone(), other.score); - - assert!(addresses.iter().any(|a| a.addr == other.addr)); - assert!(!addresses.iter().any(|a| a.addr == first.addr)); - } - - #[test] - fn finitely_scored_address_limit() { - fn prop(reports: Vec, limit: NonZeroU8) { - let mut addresses = Addresses::new(limit.into()); - - // Add all reports. - for r in reports { - addresses.add(r.addr, r.score); - } - - // Count the finitely scored addresses. - let num_finite = addresses - .iter() - .filter(|r| { - matches!( - r, - AddressRecord { - score: AddressScore::Finite(_), - .. - } - ) - }) - .count(); - - // Check against the limit. - assert!(num_finite <= limit.get() as usize); - } - - quickcheck(prop as fn(_, _)); - } - - #[test] - fn record_score_sum() { - fn prop(records: Vec) -> bool { - // Make sure the address collection can hold all reports. - let n = std::cmp::max(records.len(), 1); - let mut addresses = Addresses::new(NonZeroUsize::new(n).unwrap()); - - // Add all address reports to the collection. - for r in records.iter() { - addresses.add(r.addr.clone(), r.score); - } - - // Check that each address in the registry has the expected score. - for r in &addresses.registry { - let expected_score = records.iter().fold(None::, |sum, rec| { - if rec.addr == r.addr { - sum.map_or(Some(rec.score), |s| Some(s + rec.score)) - } else { - sum - } - }); - - if Some(r.score) != expected_score { - return false; - } - } - - true - } - - quickcheck(prop as fn(_) -> _) - } -} From 2ae6f34a52cc8dc09aa954dda0c89fd0e1190e9a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 May 2023 14:57:37 +0200 Subject: [PATCH 059/105] Always enable server mode for inbound connections --- protocols/kad/src/behaviour.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 91cceb3030a..84741b2bb07 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1984,7 +1984,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: self.mode == Mode::Server, + allow_listening: true, // If somebody dialed us, they can definitely include us in their routing table. idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Listener { From 58eb192804d9491ffba539dd56c7e1e404b9cfe3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 16 May 2023 15:33:14 +0200 Subject: [PATCH 060/105] Determine mode based in inbound/outbound connection --- protocols/kad/src/behaviour.rs | 24 +------------- protocols/kad/src/lib.rs | 2 +- protocols/kad/tests/client_mode.rs | 50 ++++++++++++++++-------------- 3 files changed, 29 insertions(+), 47 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 84741b2bb07..e4c8d0408d0 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -116,7 +116,6 @@ pub struct Kademlia { /// The record storage. store: TStore, - mode: Mode, } /// The configurable strategies for the insertion of peers @@ -183,7 +182,6 @@ pub struct KademliaConfig { connection_idle_timeout: Duration, kbucket_inserts: KademliaBucketInserts, caching: KademliaCaching, - mode: Mode, } impl Default for KademliaConfig { @@ -201,7 +199,6 @@ impl Default for KademliaConfig { connection_idle_timeout: Duration::from_secs(10), kbucket_inserts: KademliaBucketInserts::OnConnected, caching: KademliaCaching::Enabled { max_peers: 1 }, - mode: Mode::Server, } } } @@ -402,14 +399,6 @@ impl KademliaConfig { self.caching = c; self } - - /// Sets the mode. - /// - /// The default is [`Mode::Server`]. - pub fn set_mode(&mut self, m: Mode) -> &mut Self { - self.mode = m; - self - } } impl Kademlia @@ -464,7 +453,6 @@ where connection_idle_timeout: config.connection_idle_timeout, external_addresses: Default::default(), local_peer_id: id, - mode: config.mode, } } @@ -2005,7 +1993,7 @@ where Ok(KademliaHandler::new( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), - allow_listening: self.mode == Mode::Server, + allow_listening: false, // We dialed the remote, which might happen on ephemeral port. Disable listening because we cannot guarantee that the remote will be able to connect back to us on the observed address. idle_timeout: self.connection_idle_timeout, }, ConnectedPoint::Dialer { @@ -3206,13 +3194,3 @@ pub enum RoutingUpdate { /// peer ID). Failed, } - -/// The kademlia mode. -/// -/// In server mode, a node accepts inbound kademlia messages and is therefore available to the wider network. -/// In client mode, a node merely issues requests to the network. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Mode { - Client, - Server, -} diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index 77768941331..7e51f8b9908 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -68,7 +68,7 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, + GetRecordOk, GetRecordResult, InboundRequest, NoKnownPeers, PeerRecord, PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, QueryResult, QueryStats, RoutingUpdate, }; diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index 6e08862d007..c0485ce7099 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -1,14 +1,14 @@ use libp2p_identify as identify; use libp2p_identity as identity; use libp2p_kad::store::MemoryStore; -use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent, Mode}; +use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent}; use libp2p_swarm::Swarm; use libp2p_swarm_test::SwarmExt; #[async_std::test] -async fn connection_to_node_in_client_mode_does_not_update_routing_table() { - let mut client = Swarm::new_ephemeral(MyBehaviour::client); - let mut server = Swarm::new_ephemeral(MyBehaviour::server); +async fn connection_from_client_to_server_does_not_update_routing_table() { + let mut client = Swarm::new_ephemeral(MyBehaviour::new); + let mut server = Swarm::new_ephemeral(MyBehaviour::new); server.listen().await; client.connect(&mut server).await; @@ -28,12 +28,12 @@ async fn connection_to_node_in_client_mode_does_not_update_routing_table() { #[async_std::test] async fn two_servers_add_each_other_to_routing_table() { - let mut server1 = Swarm::new_ephemeral(MyBehaviour::server); - let mut server2 = Swarm::new_ephemeral(MyBehaviour::server); + let _ = env_logger::try_init(); - server1.listen().await; - server2.listen().await; + let mut server1 = Swarm::new_ephemeral(MyBehaviour::new); + let mut server2 = Swarm::new_ephemeral(MyBehaviour::new); + server2.listen().await; server1.connect(&mut server2).await; let server1_peer_id = *server1.local_peer_id(); @@ -45,10 +45,21 @@ async fn two_servers_add_each_other_to_routing_table() { match libp2p_swarm_test::drive(&mut server1, &mut server2).await { ( [Identify(_), Identify(_), Kad(RoutingUpdated { peer: peer1, .. })], - [Identify(_), Identify(_), Kad(UnroutablePeer { peer: peer2, .. })] - | [Identify(_), Kad(UnroutablePeer { peer: peer2, .. }), Identify(_)], // Unroutable because server2 did not dial. + [Identify(_), Identify(_)], ) => { assert_eq!(peer1, server2_peer_id); + } + other => panic!("Unexpected events: {other:?}"), + } + + server1.listen().await; + server2.connect(&mut server1).await; + + match libp2p_swarm_test::drive(&mut server2, &mut server1).await { + ( + [Identify(_), Kad(RoutingUpdated { peer: peer2, .. }), Identify(_)], + [Identify(_), Identify(_)], + ) => { assert_eq!(peer2, server1_peer_id); } other => panic!("Unexpected events: {other:?}"), @@ -63,18 +74,7 @@ struct MyBehaviour { } impl MyBehaviour { - fn client(k: identity::Keypair) -> Self { - let mut config = KademliaConfig::default(); - config.set_mode(Mode::Client); - - Self::with_config(k, config) - } - - fn server(k: identity::Keypair) -> Self { - Self::with_config(k, KademliaConfig::default()) - } - - fn with_config(k: identity::Keypair, config: KademliaConfig) -> MyBehaviour { + fn new(k: identity::Keypair) -> Self { let local_peer_id = k.public().to_peer_id(); Self { @@ -82,7 +82,11 @@ impl MyBehaviour { "/test/1.0.0".to_owned(), k.public(), )), - kad: Kademlia::with_config(local_peer_id, MemoryStore::new(local_peer_id), config), + kad: Kademlia::with_config( + local_peer_id, + MemoryStore::new(local_peer_id), + KademliaConfig::default(), + ), } } } From 5a945a8794cbc7c4bb6b9f2f2afbb5b42744d667 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 17 May 2023 14:14:47 +0200 Subject: [PATCH 061/105] Update identify docs --- protocols/identify/src/behaviour.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocols/identify/src/behaviour.rs b/protocols/identify/src/behaviour.rs index ac08a142a24..8081924fffd 100644 --- a/protocols/identify/src/behaviour.rs +++ b/protocols/identify/src/behaviour.rs @@ -43,8 +43,7 @@ use std::{ /// about them, and answers identify queries from other nodes. /// /// All external addresses of the local node supposedly observed by remotes -/// are reported via [`ToSwarm::ReportObservedAddr`] with a -/// [score](AddressScore) of `1`. +/// are reported via [`ToSwarm::NewExternalAddrCandidate`]. pub struct Behaviour { config: Config, /// For each peer we're connected to, the observed address to send back to it. From 4bcb4c115660cef1791220298228f54236a04649 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 17 May 2023 14:16:39 +0200 Subject: [PATCH 062/105] Insert and remove external addresses --- swarm/src/lib.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 8e80a65ac65..3acac58b5a2 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -647,13 +647,15 @@ where self.behaviour .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &a, - })) + })); + self.confirmed_external_addr.insert(a); } /// TODO pub fn remove_external_address(&mut self, addr: &Multiaddr) { self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })) + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); + self.confirmed_external_addr.remove(addr); } /// Disconnects a peer by its peer ID, closing all connections to said peer. @@ -1128,18 +1130,10 @@ where } } ToSwarm::ExternalAddrConfirmed(addr) => { - self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { - addr: &addr, - })); - self.confirmed_external_addr.insert(addr); + self.add_external_address(addr); } ToSwarm::ExternalAddrExpired(addr) => { - self.behaviour - .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { - addr: &addr, - })); - self.confirmed_external_addr.remove(&addr); + self.remove_external_address(&addr); } ToSwarm::CloseConnection { peer_id, From 7fef679292f216d758c3cdebcfba17c2d05bbcf8 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 17 May 2023 14:22:22 +0200 Subject: [PATCH 063/105] Update docs --- swarm/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 3acac58b5a2..fad4a5a7f94 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -640,9 +640,10 @@ where self.confirmed_external_addr.iter() } - /// Adds an external address record for the local node. + /// Add a **confirmed** external address for the local node. /// - /// TODO + /// This function should only be called with addresses that are guaranteed to be reachable. + /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. pub fn add_external_address(&mut self, a: Multiaddr) { self.behaviour .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { @@ -651,7 +652,9 @@ where self.confirmed_external_addr.insert(a); } - /// TODO + /// Remove an external address for the local node. + /// + /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrExpired`]. pub fn remove_external_address(&mut self, addr: &Multiaddr) { self.behaviour .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); From 5f0c01dd61c8a2162ee50499cefb4ec6b8eebc29 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 18 May 2023 23:51:23 +1000 Subject: [PATCH 064/105] Apply suggestions from code review Co-authored-by: Max Inden --- swarm/src/behaviour.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 8e1707b0300..b6026109459 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -441,9 +441,9 @@ pub enum FromSwarm<'a, Handler> { ListenerClosed(ListenerClosed<'a>), /// Informs the behaviour that we have discovered a new candidate for an external address for us. NewExternalAddrCandidate(NewExternalAddrCandidate<'a>), - /// Informs the behaviour that an external address was removed. + /// Informs the behaviour that an external address of the local node was removed. ExternalAddrConfirmed(ExternalAddrConfirmed<'a>), - /// Informs the behaviour that an external address was removed. + /// Informs the behaviour that an external address of the local node expired, i.e. is no-longer confirmed. ExternalAddrExpired(ExternalAddrExpired<'a>), } From 0256f8a0cfe57813d7e47fd462d53f092fbe4cea Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 10:21:14 +0200 Subject: [PATCH 065/105] Fix doc link --- protocols/rendezvous/src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index d410cce8d89..d1a514f1820 100644 --- a/protocols/rendezvous/src/client.rs +++ b/protocols/rendezvous/src/client.rs @@ -74,7 +74,7 @@ impl Behaviour { /// Register our external addresses in the given namespace with the given rendezvous peer. /// /// External addresses are either manually added via [`libp2p_swarm::Swarm::add_external_address`] or reported - /// by other [`NetworkBehaviour`]s via [`ToSwarm::ReportObservedAddr`]. + /// by other [`NetworkBehaviour`]s via [`ToSwarm::ExternalAddrConfirmed`]. pub fn register(&mut self, namespace: Namespace, rendezvous_node: PeerId, ttl: Option) { self.pending_register_requests .push((namespace, rendezvous_node, ttl)); From 3c38b3f5d6e1bf2a5f0a87acb4be90d1bd6959d6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 10:25:37 +0200 Subject: [PATCH 066/105] Remove private NAT status test We no longer test external addresses. --- protocols/autonat/tests/test_client.rs | 46 ++------------------------ 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index d4708f542f9..e8c923c0842 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -70,48 +70,6 @@ async fn test_auto_probe() { assert!(client.behaviour().public_address().is_none()); assert_eq!(client.behaviour().confidence(), 0); - // Test Private NAT Status - - // Artificially add a faulty address. - let unreachable_addr: Multiaddr = "/ip4/127.0.0.1/tcp/42".parse().unwrap(); - client.add_external_address(unreachable_addr.clone()); - - let id = match client.next_behaviour_event().await { - Event::OutboundProbe(OutboundProbeEvent::Request { probe_id, peer }) => { - assert_eq!(peer, server_id); - probe_id - } - other => panic!("Unexpected behaviour event: {other:?}."), - }; - - match client.next_behaviour_event().await { - Event::OutboundProbe(OutboundProbeEvent::Error { - probe_id, - peer, - error, - }) => { - assert_eq!(peer.unwrap(), server_id); - assert_eq!(probe_id, id); - assert_eq!( - error, - OutboundProbeError::Response(ResponseError::DialError) - ); - } - other => panic!("Unexpected behaviour event: {other:?}."), - } - - match client.next_behaviour_event().await { - Event::StatusChanged { old, new } => { - assert_eq!(old, NatStatus::Unknown); - assert_eq!(new, NatStatus::Private); - } - other => panic!("Unexpected behaviour event: {other:?}."), - } - - assert_eq!(client.behaviour().confidence(), 0); - assert_eq!(client.behaviour().nat_status(), NatStatus::Private); - assert!(client.behaviour().public_address().is_none()); - // Test new public listening address client.listen().await; @@ -142,12 +100,14 @@ async fn test_auto_probe() { } SwarmEvent::Behaviour(Event::StatusChanged { old, new }) => { // Expect to flip status to public - assert_eq!(old, NatStatus::Private); + assert_eq!(old, NatStatus::Unknown); assert!(matches!(new, NatStatus::Public(_))); assert!(new.is_public()); break; } SwarmEvent::IncomingConnection { .. } + | SwarmEvent::ConnectionEstablished { .. } + | SwarmEvent::Dialing(..) | SwarmEvent::NewListenAddr { .. } | SwarmEvent::ExpiredListenAddr { .. } => {} other => panic!("Unexpected swarm event: {other:?}."), From b1edd30ac0bdf290156fb0dc627e8b33a3382b1b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 10:31:33 +0200 Subject: [PATCH 067/105] Fix more doc links --- swarm/src/behaviour.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index b6026109459..98d1d00c1a8 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -290,7 +290,7 @@ pub enum ToSwarm { event: TInEvent, }, - /// Reports a new candidate for an external address to the [`Swarm`]. + /// Reports a new candidate for an external address to the [`Swarm`](crate::Swarm). /// /// This address could come from a variety of sources: /// - A protocol such as identify obtained it from a remote. @@ -299,10 +299,10 @@ pub enum ToSwarm { /// - We established a new relay connection. NewExternalAddrCandidate(Multiaddr), - /// Indicates to the [`Swarm`] that the provided address is confirmed to be externally reachable. + /// Indicates to the [`Swarm`](crate::Swarm) that the provided address is confirmed to be externally reachable. ExternalAddrConfirmed(Multiaddr), - /// Indicates to the [`Swarm`] that we are no longer externally reachable under the provided address. + /// Indicates to the [`Swarm`](crate::Swarm) that we are no longer externally reachable under the provided address. ExternalAddrExpired(Multiaddr), /// Instructs the `Swarm` to initiate a graceful close of one or all connections From fc58b54e8e1aae0759d9adb9ab62a9d398942017 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 10:32:49 +0200 Subject: [PATCH 068/105] Fix compile error with latest master --- protocols/autonat/tests/test_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index e8c923c0842..6f89cc2e17c 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -107,7 +107,7 @@ async fn test_auto_probe() { } SwarmEvent::IncomingConnection { .. } | SwarmEvent::ConnectionEstablished { .. } - | SwarmEvent::Dialing(..) + | SwarmEvent::Dialing { .. } | SwarmEvent::NewListenAddr { .. } | SwarmEvent::ExpiredListenAddr { .. } => {} other => panic!("Unexpected swarm event: {other:?}."), From 62ec4828ca9ea7a293b57cec0b11d697f39eed72 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 20:20:32 +0200 Subject: [PATCH 069/105] Delete tests --- protocols/autonat/tests/test_server.rs | 105 ------------------------- 1 file changed, 105 deletions(-) diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index 7d3ead0c0c4..f8bcd375c36 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -23,7 +23,6 @@ use libp2p_autonat::{ }; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; -use libp2p_swarm::DialError; use libp2p_swarm::{Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt as _; use std::{num::NonZeroU32, time::Duration}; @@ -127,51 +126,6 @@ async fn test_dial_back() { } } -#[async_std::test] -async fn test_dial_error() { - let (mut server, server_id, server_addr) = new_server_swarm(None).await; - let (mut client, client_id) = new_client_swarm(server_id, server_addr).await; - client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); - async_std::task::spawn(client.loop_on_next()); - - let request_probe_id = match server.next_behaviour_event().await { - Event::InboundProbe(InboundProbeEvent::Request { peer, probe_id, .. }) => { - assert_eq!(peer, client_id); - probe_id - } - other => panic!("Unexpected behaviour event: {other:?}."), - }; - - loop { - match server.next_swarm_event().await { - SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => { - assert_eq!(peer_id.unwrap(), client_id); - assert!(matches!(error, DialError::Transport(_))); - break; - } - SwarmEvent::Dialing { - peer_id: Some(peer), - .. - } => assert_eq!(peer, client_id), - SwarmEvent::NewListenAddr { .. } | SwarmEvent::ExpiredListenAddr { .. } => {} - other => panic!("Unexpected swarm event: {other:?}."), - } - } - - match server.next_behaviour_event().await { - Event::InboundProbe(InboundProbeEvent::Error { - probe_id, - peer, - error, - }) => { - assert_eq!(probe_id, request_probe_id); - assert_eq!(peer, client_id); - assert_eq!(error, InboundProbeError::Response(ResponseError::DialError)); - } - other => panic!("Unexpected behaviour event: {other:?}."), - } -} - #[async_std::test] async fn test_throttle_global_max() { let (mut server, server_id, server_addr) = new_server_swarm(Some(Config { @@ -259,65 +213,6 @@ async fn test_throttle_peer_max() { }; } -#[async_std::test] -async fn test_dial_multiple_addr() { - let (mut server, server_id, server_addr) = new_server_swarm(Some(Config { - throttle_clients_peer_max: 1, - throttle_clients_period: Duration::from_secs(60), - only_global_ips: false, - ..Default::default() - })) - .await; - - let (mut client, client_id) = new_client_swarm(server_id, server_addr.clone()).await; - client.listen().await; - client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); - async_std::task::spawn(client.loop_on_next()); - - let dial_addresses = match server.next_behaviour_event().await { - Event::InboundProbe(InboundProbeEvent::Request { - peer, addresses, .. - }) => { - assert_eq!(addresses.len(), 2); - assert_eq!(client_id, peer); - addresses - } - other => panic!("Unexpected behaviour event: {other:?}."), - }; - - loop { - match server.next_swarm_event().await { - SwarmEvent::ConnectionEstablished { - peer_id, - endpoint: - ConnectedPoint::Dialer { - address, - role_override: Endpoint::Dialer, - }, - concurrent_dial_errors, - .. - } => { - assert_eq!(peer_id, client_id); - let dial_errors = concurrent_dial_errors.unwrap(); - - // The concurrent dial might not be fast enough to produce a dial error. - if let Some((addr, _)) = dial_errors.get(0) { - assert_eq!(addr, &dial_addresses[0]); - } - - assert_eq!(address, dial_addresses[1]); - break; - } - SwarmEvent::Dialing { - peer_id: Some(peer), - .. - } => assert_eq!(peer, client_id), - SwarmEvent::NewListenAddr { .. } | SwarmEvent::ExpiredListenAddr { .. } => {} - other => panic!("Unexpected swarm event: {other:?}."), - } - } -} - #[async_std::test] async fn test_global_ips_config() { let (mut server, server_id, server_addr) = new_server_swarm(Some(Config { From 1c5d243fe54618a9220609bc55e51a34198fb410 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 22 May 2023 20:30:44 +0200 Subject: [PATCH 070/105] Update docs --- swarm/src/behaviour.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 98d1d00c1a8..a6560190e48 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -292,6 +292,8 @@ pub enum ToSwarm { /// Reports a new candidate for an external address to the [`Swarm`](crate::Swarm). /// + /// This address will be shared with all [`NetworkBehaviour`]s via [`FromSwarm::NewExternalAddrCandidate`]. + /// /// This address could come from a variety of sources: /// - A protocol such as identify obtained it from a remote. /// - The user provided it based on configuration. @@ -300,9 +302,15 @@ pub enum ToSwarm { NewExternalAddrCandidate(Multiaddr), /// Indicates to the [`Swarm`](crate::Swarm) that the provided address is confirmed to be externally reachable. + /// + /// This is intended to be issued in response to a [`FromSwarm::NewExternalAddrCandidate`] if we are indeed externally reachable on this address. + /// This address will be shared with all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. ExternalAddrConfirmed(Multiaddr), /// Indicates to the [`Swarm`](crate::Swarm) that we are no longer externally reachable under the provided address. + /// + /// This expires an address that was earlier confirmed via [`ToSwarm::ExternalAddrConfirmed`]. + /// This address will be shared with all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrExpired`]. ExternalAddrExpired(Multiaddr), /// Instructs the `Swarm` to initiate a graceful close of one or all connections From ccc9bf302836811df0c439ee6520e16ef37b3936 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 12:28:10 +0200 Subject: [PATCH 071/105] Revert "Delete tests" This reverts commit 62ec4828ca9ea7a293b57cec0b11d697f39eed72. --- protocols/autonat/tests/test_server.rs | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index f8bcd375c36..7d3ead0c0c4 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -23,6 +23,7 @@ use libp2p_autonat::{ }; use libp2p_core::{multiaddr::Protocol, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; +use libp2p_swarm::DialError; use libp2p_swarm::{Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt as _; use std::{num::NonZeroU32, time::Duration}; @@ -126,6 +127,51 @@ async fn test_dial_back() { } } +#[async_std::test] +async fn test_dial_error() { + let (mut server, server_id, server_addr) = new_server_swarm(None).await; + let (mut client, client_id) = new_client_swarm(server_id, server_addr).await; + client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); + async_std::task::spawn(client.loop_on_next()); + + let request_probe_id = match server.next_behaviour_event().await { + Event::InboundProbe(InboundProbeEvent::Request { peer, probe_id, .. }) => { + assert_eq!(peer, client_id); + probe_id + } + other => panic!("Unexpected behaviour event: {other:?}."), + }; + + loop { + match server.next_swarm_event().await { + SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => { + assert_eq!(peer_id.unwrap(), client_id); + assert!(matches!(error, DialError::Transport(_))); + break; + } + SwarmEvent::Dialing { + peer_id: Some(peer), + .. + } => assert_eq!(peer, client_id), + SwarmEvent::NewListenAddr { .. } | SwarmEvent::ExpiredListenAddr { .. } => {} + other => panic!("Unexpected swarm event: {other:?}."), + } + } + + match server.next_behaviour_event().await { + Event::InboundProbe(InboundProbeEvent::Error { + probe_id, + peer, + error, + }) => { + assert_eq!(probe_id, request_probe_id); + assert_eq!(peer, client_id); + assert_eq!(error, InboundProbeError::Response(ResponseError::DialError)); + } + other => panic!("Unexpected behaviour event: {other:?}."), + } +} + #[async_std::test] async fn test_throttle_global_max() { let (mut server, server_id, server_addr) = new_server_swarm(Some(Config { @@ -213,6 +259,65 @@ async fn test_throttle_peer_max() { }; } +#[async_std::test] +async fn test_dial_multiple_addr() { + let (mut server, server_id, server_addr) = new_server_swarm(Some(Config { + throttle_clients_peer_max: 1, + throttle_clients_period: Duration::from_secs(60), + only_global_ips: false, + ..Default::default() + })) + .await; + + let (mut client, client_id) = new_client_swarm(server_id, server_addr.clone()).await; + client.listen().await; + client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); + async_std::task::spawn(client.loop_on_next()); + + let dial_addresses = match server.next_behaviour_event().await { + Event::InboundProbe(InboundProbeEvent::Request { + peer, addresses, .. + }) => { + assert_eq!(addresses.len(), 2); + assert_eq!(client_id, peer); + addresses + } + other => panic!("Unexpected behaviour event: {other:?}."), + }; + + loop { + match server.next_swarm_event().await { + SwarmEvent::ConnectionEstablished { + peer_id, + endpoint: + ConnectedPoint::Dialer { + address, + role_override: Endpoint::Dialer, + }, + concurrent_dial_errors, + .. + } => { + assert_eq!(peer_id, client_id); + let dial_errors = concurrent_dial_errors.unwrap(); + + // The concurrent dial might not be fast enough to produce a dial error. + if let Some((addr, _)) = dial_errors.get(0) { + assert_eq!(addr, &dial_addresses[0]); + } + + assert_eq!(address, dial_addresses[1]); + break; + } + SwarmEvent::Dialing { + peer_id: Some(peer), + .. + } => assert_eq!(peer, client_id), + SwarmEvent::NewListenAddr { .. } | SwarmEvent::ExpiredListenAddr { .. } => {} + other => panic!("Unexpected swarm event: {other:?}."), + } + } +} + #[async_std::test] async fn test_global_ips_config() { let (mut server, server_id, server_addr) = new_server_swarm(Some(Config { From a4886f554d1b101acc95d4ebdc8e403fced26b1a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 12:33:51 +0200 Subject: [PATCH 072/105] Fix tests by adding a new API to AutoNAT --- protocols/autonat/src/behaviour.rs | 11 +++++++++-- protocols/autonat/tests/test_client.rs | 4 +++- protocols/autonat/tests/test_server.rs | 8 ++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index b5381d62af6..0437e0849eb 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -279,6 +279,14 @@ impl Behaviour { self.servers.retain(|p| p != peer); } + /// Add a new candidate for an external address. + /// + /// This will trigger a new probe for the given address. + pub fn add_external_address_candidate(&mut self, candidate: Multiaddr) { + self.other_candidates.insert(candidate); + self.as_client().on_new_address(); + } + fn as_client(&mut self) -> AsClient { AsClient { inner: &mut self.inner, @@ -570,8 +578,7 @@ impl NetworkBehaviour for Behaviour { .on_swarm_event(FromSwarm::NewExternalAddrCandidate( NewExternalAddrCandidate { addr }, )); - self.other_candidates.insert(addr.to_owned()); - self.as_client().on_new_address(); + self.add_external_address_candidate(addr.to_owned()); } listen_failure @ FromSwarm::ListenFailure(_) => { self.inner.on_swarm_event(listen_failure) diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 6f89cc2e17c..baa65300c2a 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -158,7 +158,9 @@ async fn test_confidence() { client.listen().await; } else { let unreachable_addr = "/ip4/127.0.0.1/tcp/42".parse().unwrap(); - client.add_external_address(unreachable_addr); + client + .behaviour_mut() + .add_external_address_candidate(unreachable_addr); } for i in 0..MAX_CONFIDENCE + 1 { diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index 7d3ead0c0c4..8753003d6de 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -131,7 +131,9 @@ async fn test_dial_back() { async fn test_dial_error() { let (mut server, server_id, server_addr) = new_server_swarm(None).await; let (mut client, client_id) = new_client_swarm(server_id, server_addr).await; - client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); + client + .behaviour_mut() + .add_external_address_candidate("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); async_std::task::spawn(client.loop_on_next()); let request_probe_id = match server.next_behaviour_event().await { @@ -271,7 +273,9 @@ async fn test_dial_multiple_addr() { let (mut client, client_id) = new_client_swarm(server_id, server_addr.clone()).await; client.listen().await; - client.add_external_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); + client + .behaviour_mut() + .add_external_address_candidate("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); async_std::task::spawn(client.loop_on_next()); let dial_addresses = match server.next_behaviour_event().await { From d7da7567a08a609d0a664284ed3517608be0f7cf Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 12:35:13 +0200 Subject: [PATCH 073/105] Rename to `probe_address` --- protocols/autonat/src/behaviour.rs | 8 +++----- protocols/autonat/tests/test_client.rs | 4 +--- protocols/autonat/tests/test_server.rs | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/protocols/autonat/src/behaviour.rs b/protocols/autonat/src/behaviour.rs index 0437e0849eb..926445c9e49 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -279,10 +279,8 @@ impl Behaviour { self.servers.retain(|p| p != peer); } - /// Add a new candidate for an external address. - /// - /// This will trigger a new probe for the given address. - pub fn add_external_address_candidate(&mut self, candidate: Multiaddr) { + /// Explicitly probe the provided address for external reachability. + pub fn probe_address(&mut self, candidate: Multiaddr) { self.other_candidates.insert(candidate); self.as_client().on_new_address(); } @@ -578,7 +576,7 @@ impl NetworkBehaviour for Behaviour { .on_swarm_event(FromSwarm::NewExternalAddrCandidate( NewExternalAddrCandidate { addr }, )); - self.add_external_address_candidate(addr.to_owned()); + self.probe_address(addr.to_owned()); } listen_failure @ FromSwarm::ListenFailure(_) => { self.inner.on_swarm_event(listen_failure) diff --git a/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index baa65300c2a..1911d1a6b2d 100644 --- a/protocols/autonat/tests/test_client.rs +++ b/protocols/autonat/tests/test_client.rs @@ -158,9 +158,7 @@ async fn test_confidence() { client.listen().await; } else { let unreachable_addr = "/ip4/127.0.0.1/tcp/42".parse().unwrap(); - client - .behaviour_mut() - .add_external_address_candidate(unreachable_addr); + client.behaviour_mut().probe_address(unreachable_addr); } for i in 0..MAX_CONFIDENCE + 1 { diff --git a/protocols/autonat/tests/test_server.rs b/protocols/autonat/tests/test_server.rs index 8753003d6de..0f14c6edb27 100644 --- a/protocols/autonat/tests/test_server.rs +++ b/protocols/autonat/tests/test_server.rs @@ -133,7 +133,7 @@ async fn test_dial_error() { let (mut client, client_id) = new_client_swarm(server_id, server_addr).await; client .behaviour_mut() - .add_external_address_candidate("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); + .probe_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); async_std::task::spawn(client.loop_on_next()); let request_probe_id = match server.next_behaviour_event().await { @@ -275,7 +275,7 @@ async fn test_dial_multiple_addr() { client.listen().await; client .behaviour_mut() - .add_external_address_candidate("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); + .probe_address("/ip4/127.0.0.1/tcp/12345".parse().unwrap()); async_std::task::spawn(client.loop_on_next()); let dial_addresses = match server.next_behaviour_event().await { From fbea5de3c77676de44b7eedd3a709b80e7a2aa90 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 13:10:18 +0200 Subject: [PATCH 074/105] Split handler ctor into inbound and outbound --- protocols/kad/src/behaviour.rs | 16 ++++++-------- protocols/kad/src/handler.rs | 39 +++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index c1a3e33b15a..ef0a2c4e163 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1969,16 +1969,14 @@ where local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { - Ok(KademliaHandler::new( + Ok(KademliaHandler::new_inbound( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), allow_listening: true, // If somebody dialed us, they can definitely include us in their routing table. idle_timeout: self.connection_idle_timeout, }, - ConnectedPoint::Listener { - local_addr: local_addr.clone(), - send_back_addr: remote_addr.clone(), - }, + local_addr.clone(), + remote_addr.clone(), peer, )) } @@ -1990,16 +1988,14 @@ where addr: &Multiaddr, role_override: Endpoint, ) -> Result, ConnectionDenied> { - Ok(KademliaHandler::new( + Ok(KademliaHandler::new_outbound( KademliaHandlerConfig { protocol_config: self.protocol_config.clone(), allow_listening: false, // We dialed the remote, which might happen on ephemeral port. Disable listening because we cannot guarantee that the remote will be able to connect back to us on the observed address. idle_timeout: self.connection_idle_timeout, }, - ConnectedPoint::Dialer { - address: addr.clone(), - role_override, - }, + addr.clone(), + role_override, peer, )) } diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index cd61632c82b..7ad9c0aea8c 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -28,7 +28,7 @@ use either::Either; use futures::prelude::*; use futures::stream::SelectAll; use instant::Instant; -use libp2p_core::{upgrade, ConnectedPoint}; +use libp2p_core::{upgrade, ConnectedPoint, Endpoint, Multiaddr}; use libp2p_identity::PeerId; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, @@ -470,8 +470,41 @@ pub struct KademliaRequestId { struct UniqueConnecId(u64); impl KademliaHandler { - /// Create a [`KademliaHandler`] using the given configuration. - pub fn new( + /// Create a [`KademliaHandler`] for a new inbound connection. + pub fn new_inbound( + config: KademliaHandlerConfig, + local_addr: Multiaddr, + remote_addr: Multiaddr, + remote_peer_id: PeerId, + ) -> Self { + Self::new( + config, + ConnectedPoint::Listener { + local_addr, + send_back_addr: remote_addr, + }, + remote_peer_id, + ) + } + + /// Create a [`KademliaHandler`] for a new outbound connection. + pub fn new_outbound( + config: KademliaHandlerConfig, + local_addr: Multiaddr, + role_override: Endpoint, + peer: PeerId, + ) -> Self { + Self::new( + config, + ConnectedPoint::Dialer { + address: local_addr, + role_override, + }, + peer, + ) + } + + fn new( config: KademliaHandlerConfig, endpoint: ConnectedPoint, remote_peer_id: PeerId, From 78dbdd3ff54face3255d0d208d5742e3103c8445 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 14:41:18 +0200 Subject: [PATCH 075/105] Fix starved connection handler on local-protocols change event --- swarm/src/connection.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/swarm/src/connection.rs b/swarm/src/connection.rs index eab521593ac..b8f3b50338b 100644 --- a/swarm/src/connection.rs +++ b/swarm/src/connection.rs @@ -425,12 +425,17 @@ where } let new_protocols = gather_supported_protocols(handler); + let changes = ProtocolsChange::from_full_sets(supported_protocols, &new_protocols); - for change in ProtocolsChange::from_full_sets(supported_protocols, &new_protocols) { - handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(change)); - } + if !changes.is_empty() { + for change in changes { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(change)); + } - *supported_protocols = new_protocols; + *supported_protocols = new_protocols; + + continue; // Go back to the top, handler can potentially make progress again. + } return Poll::Pending; // Nothing can make progress, return `Pending`. } From 5d20d54436ba2d6eb60926b83922ea0406457106 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 14:04:00 +0200 Subject: [PATCH 076/105] Push updated protocols to peer --- protocols/identify/src/handler.rs | 39 +++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/protocols/identify/src/handler.rs b/protocols/identify/src/handler.rs index 4c7ae58631e..748f4249fde 100644 --- a/protocols/identify/src/handler.rs +++ b/protocols/identify/src/handler.rs @@ -36,7 +36,7 @@ use libp2p_swarm::{ ConnectionHandler, ConnectionHandlerEvent, KeepAlive, StreamProtocol, StreamUpgradeError, SubstreamProtocol, SupportedProtocols, }; -use log::warn; +use log::{warn, Level}; use smallvec::SmallVec; use std::collections::HashSet; use std::{io, task::Context, task::Poll, time::Duration}; @@ -60,6 +60,9 @@ pub struct Handler { /// Future that fires when we need to identify the node again. trigger_next_identify: Delay, + /// Whether we have exchanged at least one periodic identify. + exchanged_one_periodic_identify: bool, + /// The interval of `trigger_next_identify`, i.e. the recurrent delay. interval: Duration, @@ -122,6 +125,7 @@ impl Handler { events: SmallVec::new(), pending_replies: FuturesUnordered::new(), trigger_next_identify: Delay::new(initial_delay), + exchanged_one_periodic_identify: false, interval, public_key, protocol_version, @@ -238,6 +242,14 @@ impl Handler { self.remote_supported_protocols = new_remote_protocols; } + + fn local_protocols_to_string(&mut self) -> String { + self.local_supported_protocols + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", ") + } } impl ConnectionHandler for Handler { @@ -319,6 +331,7 @@ impl ConnectionHandler for Handler { let event = result .map(|()| Event::Identification) .unwrap_or_else(|err| Event::IdentificationError(StreamUpgradeError::Apply(err))); + self.exchanged_one_periodic_identify = true; return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event)); } @@ -349,7 +362,29 @@ impl ConnectionHandler for Handler { | ConnectionEvent::ListenUpgradeError(_) | ConnectionEvent::RemoteProtocolsChange(_) => {} ConnectionEvent::LocalProtocolsChange(change) => { - self.local_supported_protocols.on_protocols_change(change); + let before = + log::log_enabled!(Level::Debug).then(|| self.local_protocols_to_string()); + let protocols_changed = self.local_supported_protocols.on_protocols_change(change); + let after = + log::log_enabled!(Level::Debug).then(|| self.local_protocols_to_string()); + + if protocols_changed && self.exchanged_one_periodic_identify { + log::debug!( + "Supported listen protocols changed from [{}] to [{}], pushing to {}", + before.unwrap(), + after.unwrap(), + self.remote_peer_id + ); + + let info = self.build_info(); + self.events + .push(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new( + Either::Right(Push::outbound(info)), + (), + ), + }); + } } } } From 2cf984afd3dc7e6080ac8f45ad7c35fb30bd9fb4 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 14:42:42 +0200 Subject: [PATCH 077/105] Add test for automatic switch to server mode on existing connections --- protocols/kad/tests/client_mode.rs | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index c0485ce7099..9fb219bf7d1 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -66,6 +66,48 @@ async fn two_servers_add_each_other_to_routing_table() { } } +#[async_std::test] +async fn adding_an_external_addresses_activates_server_mode_on_existing_connections() { + let _ = env_logger::try_init(); + + let mut client = Swarm::new_ephemeral(MyBehaviour::new); + let mut server = Swarm::new_ephemeral(MyBehaviour::new); + let server_peer_id = *server.local_peer_id(); + + let (memory_addr, _) = server.listen().await; + + // Remove memory address to simulate a server that doesn't know its external address. + server.remove_external_address(&memory_addr); + client.dial(memory_addr.clone()).unwrap(); + + use MyBehaviourEvent::*; + + // Do the usual identify send/receive dance. + match libp2p_swarm_test::drive(&mut client, &mut server).await { + ( + [Identify(_), Identify(_)], + [Identify(_), Identify(_)], + ) => {} + other => panic!("Unexpected events: {other:?}"), + } + + use KademliaEvent::*; + + // Server learns its external address (this could be through AutoNAT or some other mechanism). + server.add_external_address(memory_addr); + + // The server reconfigured its connection to the client to be in server mode, pushes that information to client which as a result updates its routing table. + match libp2p_swarm_test::drive(&mut client, &mut server).await { + ( + [Identify(identify::Event::Received { .. }), Kad(RoutingUpdated { peer: peer1, .. })], + [Identify(identify::Event::Pushed { .. })], + ) => { + assert_eq!(peer1, server_peer_id); + } + other => panic!("Unexpected events: {other:?}"), + } +} + #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct MyBehaviour { From 1233784ae95b5b7ea02faa4abf7101f69914aa6d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 13:13:28 +0200 Subject: [PATCH 078/105] Remove handler config --- protocols/kad/src/behaviour.rs | 21 ++++------ protocols/kad/src/handler.rs | 72 ++++++++++++++++------------------ 2 files changed, 40 insertions(+), 53 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index ef0a2c4e163..743c534fbce 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -23,10 +23,7 @@ mod test; use crate::addresses::Addresses; -use crate::handler::{ - KademliaHandler, KademliaHandlerConfig, KademliaHandlerEvent, KademliaHandlerIn, - KademliaRequestId, -}; +use crate::handler::{KademliaHandler, KademliaHandlerEvent, KademliaHandlerIn, KademliaRequestId}; use crate::jobs::*; use crate::kbucket::{self, Distance, KBucketsTable, NodeStatus}; use crate::protocol::{KadConnectionType, KadPeer, KademliaProtocolConfig}; @@ -1970,11 +1967,9 @@ where remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { Ok(KademliaHandler::new_inbound( - KademliaHandlerConfig { - protocol_config: self.protocol_config.clone(), - allow_listening: true, // If somebody dialed us, they can definitely include us in their routing table. - idle_timeout: self.connection_idle_timeout, - }, + self.protocol_config.clone(), + true, // If somebody dialed us, they can definitely include us in their routing table. + self.connection_idle_timeout, local_addr.clone(), remote_addr.clone(), peer, @@ -1989,11 +1984,9 @@ where role_override: Endpoint, ) -> Result, ConnectionDenied> { Ok(KademliaHandler::new_outbound( - KademliaHandlerConfig { - protocol_config: self.protocol_config.clone(), - allow_listening: false, // We dialed the remote, which might happen on ephemeral port. Disable listening because we cannot guarantee that the remote will be able to connect back to us on the observed address. - idle_timeout: self.connection_idle_timeout, - }, + self.protocol_config.clone(), + false, // We dialed the remote, which might happen on ephemeral port. Disable listening because we cannot guarantee that the remote will be able to connect back to us on the observed address. + self.connection_idle_timeout, addr.clone(), role_override, peer, diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 7ad9c0aea8c..a441989d2f3 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -54,8 +54,14 @@ const MAX_NUM_SUBSTREAMS: usize = 32; /// /// It also handles requests made by the remote. pub struct KademliaHandler { - /// Configuration for the Kademlia protocol. - config: KademliaHandlerConfig, + /// Configuration of the wire protocol. + protocol_config: KademliaProtocolConfig, + + /// If false, we deny incoming requests. + allow_listening: bool, + + /// Time after which we close an idle connection. + idle_timeout: Duration, /// Next unique ID of a connection. next_connec_unique_id: UniqueConnecId, @@ -105,19 +111,6 @@ enum ProtocolStatus { Reported, } -/// Configuration of a [`KademliaHandler`]. -#[derive(Debug, Clone)] -pub struct KademliaHandlerConfig { - /// Configuration of the wire protocol. - pub protocol_config: KademliaProtocolConfig, - - /// If false, we deny incoming requests. - pub allow_listening: bool, - - /// Time after which we close an idle connection. - pub idle_timeout: Duration, -} - /// State of an active outbound substream. enum OutboundSubstreamState { /// Waiting to send a message to the remote. @@ -472,13 +465,17 @@ struct UniqueConnecId(u64); impl KademliaHandler { /// Create a [`KademliaHandler`] for a new inbound connection. pub fn new_inbound( - config: KademliaHandlerConfig, + protocol_config: KademliaProtocolConfig, + allow_listening: bool, + idle_timeout: Duration, local_addr: Multiaddr, remote_addr: Multiaddr, remote_peer_id: PeerId, ) -> Self { Self::new( - config, + protocol_config, + allow_listening, + idle_timeout, ConnectedPoint::Listener { local_addr, send_back_addr: remote_addr, @@ -489,13 +486,17 @@ impl KademliaHandler { /// Create a [`KademliaHandler`] for a new outbound connection. pub fn new_outbound( - config: KademliaHandlerConfig, + protocol_config: KademliaProtocolConfig, + allow_listening: bool, + idle_timeout: Duration, local_addr: Multiaddr, role_override: Endpoint, peer: PeerId, ) -> Self { Self::new( - config, + protocol_config, + allow_listening, + idle_timeout, ConnectedPoint::Dialer { address: local_addr, role_override, @@ -505,14 +506,18 @@ impl KademliaHandler { } fn new( - config: KademliaHandlerConfig, + protocol_config: KademliaProtocolConfig, + allow_listening: bool, + idle_timeout: Duration, endpoint: ConnectedPoint, remote_peer_id: PeerId, ) -> Self { - let keep_alive = KeepAlive::Until(Instant::now() + config.idle_timeout); + let keep_alive = KeepAlive::Until(Instant::now() + idle_timeout); KademliaHandler { - config, + protocol_config, + allow_listening, + idle_timeout, endpoint, remote_peer_id, next_connec_unique_id: UniqueConnecId(0), @@ -595,7 +600,7 @@ impl KademliaHandler { } } - debug_assert!(self.config.allow_listening); + debug_assert!(self.allow_listening); let connec_unique_id = self.next_connec_unique_id; self.next_connec_unique_id.0 += 1; self.inbound_substreams @@ -637,9 +642,8 @@ impl ConnectionHandler for KademliaHandler { type InboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - if self.config.allow_listening { - SubstreamProtocol::new(self.config.protocol_config.clone(), ()) - .map_upgrade(Either::Left) + if self.allow_listening { + SubstreamProtocol::new(self.protocol_config.clone(), ()).map_upgrade(Either::Left) } else { SubstreamProtocol::new(Either::Right(upgrade::DeniedUpgrade), ()) } @@ -758,7 +762,7 @@ impl ConnectionHandler for KademliaHandler { { self.num_requested_outbound_streams += 1; return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(self.config.protocol_config.clone(), ()), + protocol: SubstreamProtocol::new(self.protocol_config.clone(), ()), }); } @@ -767,7 +771,7 @@ impl ConnectionHandler for KademliaHandler { // No open streams. Preserve the existing idle timeout. (true, k @ KeepAlive::Until(_)) => k, // No open streams. Set idle timeout. - (true, _) => KeepAlive::Until(Instant::now() + self.config.idle_timeout), + (true, _) => KeepAlive::Until(Instant::now() + self.idle_timeout), // Keep alive for open streams. (false, _) => KeepAlive::Yes, }; @@ -804,7 +808,7 @@ impl ConnectionHandler for KademliaHandler { let remote_supports_our_kademlia_protocols = self .remote_supported_protocols .iter() - .any(|p| self.config.protocol_config.protocol_names().contains(p)); + .any(|p| self.protocol_config.protocol_names().contains(p)); if remote_supports_our_kademlia_protocols { self.protocol_status = ProtocolStatus::Confirmed; @@ -832,16 +836,6 @@ impl KademliaHandler { } } -impl Default for KademliaHandlerConfig { - fn default() -> Self { - KademliaHandlerConfig { - protocol_config: Default::default(), - allow_listening: true, - idle_timeout: Duration::from_secs(10), - } - } -} - impl futures::Stream for OutboundSubstreamState { type Item = ConnectionHandlerEvent; From f56597978598d935471c3690d36d7ba17c821840 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 13:14:05 +0200 Subject: [PATCH 079/105] Outbound connections are never in server mode --- protocols/kad/src/behaviour.rs | 1 - protocols/kad/src/handler.rs | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 743c534fbce..3ef3689a694 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1985,7 +1985,6 @@ where ) -> Result, ConnectionDenied> { Ok(KademliaHandler::new_outbound( self.protocol_config.clone(), - false, // We dialed the remote, which might happen on ephemeral port. Disable listening because we cannot guarantee that the remote will be able to connect back to us on the observed address. self.connection_idle_timeout, addr.clone(), role_override, diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index a441989d2f3..4ff23c29d8c 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -487,7 +487,6 @@ impl KademliaHandler { /// Create a [`KademliaHandler`] for a new outbound connection. pub fn new_outbound( protocol_config: KademliaProtocolConfig, - allow_listening: bool, idle_timeout: Duration, local_addr: Multiaddr, role_override: Endpoint, @@ -495,7 +494,7 @@ impl KademliaHandler { ) -> Self { Self::new( protocol_config, - allow_listening, + false, idle_timeout, ConnectedPoint::Dialer { address: local_addr, From b70346840df700ac7de0f33d0cf84a10c7feda09 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 13:16:17 +0200 Subject: [PATCH 080/105] Add log for outbound client mode --- protocols/kad/src/handler.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 4ff23c29d8c..03793a8c62d 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -488,16 +488,19 @@ impl KademliaHandler { pub fn new_outbound( protocol_config: KademliaProtocolConfig, idle_timeout: Duration, - local_addr: Multiaddr, + addr: Multiaddr, role_override: Endpoint, peer: PeerId, ) -> Self { + // Outbound connections are always in client-mode because we may be using an ephemeral port for dialing and thus the observed address may not be reachable. + log::debug!("Operating in client-mode on new outbound connection to peer {peer} at {addr}"); + Self::new( protocol_config, false, idle_timeout, ConnectedPoint::Dialer { - address: local_addr, + address: addr, role_override, }, peer, From c47cc34158f12de8550aad867810db86350556e0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 13:25:10 +0200 Subject: [PATCH 081/105] Activate server mode for inbound connections based on our external addresses --- protocols/kad/src/behaviour.rs | 2 +- protocols/kad/src/handler.rs | 29 +++++++++++++++++++++++++++-- protocols/kad/tests/client_mode.rs | 4 +++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 3ef3689a694..442c956d72a 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1968,11 +1968,11 @@ where ) -> Result, ConnectionDenied> { Ok(KademliaHandler::new_inbound( self.protocol_config.clone(), - true, // If somebody dialed us, they can definitely include us in their routing table. self.connection_idle_timeout, local_addr.clone(), remote_addr.clone(), peer, + self.external_addresses.iter().collect(), )) } diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 03793a8c62d..bb0b0cf48bd 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -466,15 +466,40 @@ impl KademliaHandler { /// Create a [`KademliaHandler`] for a new inbound connection. pub fn new_inbound( protocol_config: KademliaProtocolConfig, - allow_listening: bool, idle_timeout: Duration, local_addr: Multiaddr, remote_addr: Multiaddr, remote_peer_id: PeerId, + external_addresses: Vec<&Multiaddr>, ) -> Self { + let server_mode = match external_addresses.as_slice() { + [] => { + log::debug!("Operating in client-mode on new inbound connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); + + false + } + [confirmed_external_addr] => { + // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. + log::debug!("Operating in server-mode on new inbound connection from peer {remote_peer_id} under the assumption that {confirmed_external_addr} routes to {local_addr}"); + + true + } + confirmed_external_addresses => { + let confirmed_external_addresses = confirmed_external_addresses + .iter() + .map(|addr| addr.to_string()) + .collect::>() + .join(", "); + + log::debug!("Operating in server-mode on new inbound connection from peer {remote_peer_id} under the assumption that one of [{confirmed_external_addresses}] routes to {local_addr}"); + + true + } + }; + Self::new( protocol_config, - allow_listening, + server_mode, idle_timeout, ConnectedPoint::Listener { local_addr, diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index 9fb219bf7d1..63aa4099753 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -6,7 +6,9 @@ use libp2p_swarm::Swarm; use libp2p_swarm_test::SwarmExt; #[async_std::test] -async fn connection_from_client_to_server_does_not_update_routing_table() { +async fn server_gets_added_to_routing_table_by_client() { + let _ = env_logger::try_init(); + let mut client = Swarm::new_ephemeral(MyBehaviour::new); let mut server = Swarm::new_ephemeral(MyBehaviour::new); From a0622ca1fcfb59d074ece1f7c5a835e26bb23f75 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 14:43:02 +0200 Subject: [PATCH 082/105] Reconfigure existing connections to server mode --- protocols/kad/src/behaviour.rs | 24 ++++++++-- protocols/kad/src/handler.rs | 80 +++++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 442c956d72a..403988f5636 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -49,7 +49,7 @@ use libp2p_swarm::{ }; use log::{debug, info, warn}; use smallvec::SmallVec; -use std::collections::{BTreeMap, HashSet, VecDeque}; +use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::fmt; use std::num::NonZeroUsize; use std::task::{Context, Poll}; @@ -106,6 +106,8 @@ pub struct Kademlia { external_addresses: ExternalAddresses, + inbound_connections: HashMap, + /// See [`KademliaConfig::caching`]. caching: KademliaCaching, @@ -450,6 +452,7 @@ where connection_idle_timeout: config.connection_idle_timeout, external_addresses: Default::default(), local_peer_id: id, + inbound_connections: Default::default(), } } @@ -1934,9 +1937,12 @@ where ConnectionClosed { peer_id, remaining_established, + connection_id, .. }: ConnectionClosed<::ConnectionHandler>, ) { + self.inbound_connections.remove(&connection_id); + if remaining_established == 0 { for query in self.queries.iter_mut() { query.on_failure(&peer_id); @@ -1961,11 +1967,13 @@ where fn handle_established_inbound_connection( &mut self, - _connection_id: ConnectionId, + connection_id: ConnectionId, peer: PeerId, local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { + self.inbound_connections.insert(connection_id, peer); + Ok(KademliaHandler::new_inbound( self.protocol_config.clone(), self.connection_idle_timeout, @@ -2415,7 +2423,17 @@ where fn on_swarm_event(&mut self, event: FromSwarm) { self.listen_addresses.on_swarm_event(&event); - self.external_addresses.on_swarm_event(&event); + let external_addresses_changed = self.external_addresses.on_swarm_event(&event); + + if external_addresses_changed { + self.queued_events.extend(self.inbound_connections.iter().map(|(conn_id, peer_id)| ToSwarm::NotifyHandler { + peer_id: *peer_id, + handler: NotifyHandler::One(*conn_id), + event: KademliaHandlerIn::ReconfigureMode { + external_addresses: self.external_addresses.iter().cloned().collect(), + } + })); + } match event { FromSwarm::ConnectionEstablished(connection_established) => { diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index bb0b0cf48bd..510e3836a2c 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -57,8 +57,8 @@ pub struct KademliaHandler { /// Configuration of the wire protocol. protocol_config: KademliaProtocolConfig, - /// If false, we deny incoming requests. - allow_listening: bool, + /// In client mode, we don't accept inbound substreams. + mode: Mode, /// Time after which we close an idle connection. idle_timeout: Duration, @@ -111,6 +111,12 @@ enum ProtocolStatus { Reported, } +#[derive(PartialEq, Copy, Clone, Debug)] +enum Mode { + Client, + Server, +} + /// State of an active outbound substream. enum OutboundSubstreamState { /// Waiting to send a message to the remote. @@ -363,6 +369,12 @@ pub enum KademliaHandlerIn { /// for the query on the remote. Reset(KademliaRequestId), + /// Re-evaluate whether the current connection should operate in client or server mode. + /// + /// When the external addresses of a node change, we may be able to switch into server mode or + /// might have switch from server to client mode on existing connections. + ReconfigureMode { external_addresses: Vec }, + /// Request for the list of nodes whose IDs are the closest to `key`. The number of nodes /// returned is not specified, but should be around 20. FindNodeReq { @@ -472,17 +484,17 @@ impl KademliaHandler { remote_peer_id: PeerId, external_addresses: Vec<&Multiaddr>, ) -> Self { - let server_mode = match external_addresses.as_slice() { + let mode = match external_addresses.as_slice() { [] => { log::debug!("Operating in client-mode on new inbound connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); - false + Mode::Client } [confirmed_external_addr] => { // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. log::debug!("Operating in server-mode on new inbound connection from peer {remote_peer_id} under the assumption that {confirmed_external_addr} routes to {local_addr}"); - true + Mode::Server } confirmed_external_addresses => { let confirmed_external_addresses = confirmed_external_addresses @@ -493,13 +505,13 @@ impl KademliaHandler { log::debug!("Operating in server-mode on new inbound connection from peer {remote_peer_id} under the assumption that one of [{confirmed_external_addresses}] routes to {local_addr}"); - true + Mode::Server } }; Self::new( protocol_config, - server_mode, + mode, idle_timeout, ConnectedPoint::Listener { local_addr, @@ -522,7 +534,7 @@ impl KademliaHandler { Self::new( protocol_config, - false, + Mode::Client, idle_timeout, ConnectedPoint::Dialer { address: addr, @@ -534,7 +546,7 @@ impl KademliaHandler { fn new( protocol_config: KademliaProtocolConfig, - allow_listening: bool, + mode: Mode, idle_timeout: Duration, endpoint: ConnectedPoint, remote_peer_id: PeerId, @@ -543,7 +555,7 @@ impl KademliaHandler { KademliaHandler { protocol_config, - allow_listening, + mode, idle_timeout, endpoint, remote_peer_id, @@ -627,7 +639,7 @@ impl KademliaHandler { } } - debug_assert!(self.allow_listening); + debug_assert!(self.mode == Mode::Server); let connec_unique_id = self.next_connec_unique_id; self.next_connec_unique_id.0 += 1; self.inbound_substreams @@ -669,10 +681,9 @@ impl ConnectionHandler for KademliaHandler { type InboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { - if self.allow_listening { - SubstreamProtocol::new(self.protocol_config.clone(), ()).map_upgrade(Either::Left) - } else { - SubstreamProtocol::new(Either::Right(upgrade::DeniedUpgrade), ()) + match self.mode { + Mode::Server => SubstreamProtocol::new(Either::Left(self.protocol_config.clone()), ()), + Mode::Client => SubstreamProtocol::new(Either::Right(upgrade::DeniedUpgrade), ()), } } @@ -747,6 +758,45 @@ impl ConnectionHandler for KademliaHandler { } => { self.answer_pending_request(request_id, KadResponseMsg::PutValue { key, value }); } + KademliaHandlerIn::ReconfigureMode { external_addresses } => { + // Outbound connections are always in client mode. + let local_addr = match &self.endpoint { + ConnectedPoint::Dialer { .. } => return, + ConnectedPoint::Listener { local_addr, .. } => local_addr, + }; + let remote_peer_id = self.remote_peer_id; + + match (external_addresses.as_slice(), self.mode) { + ([], Mode::Server) => { + log::debug!("Switching from server to client-mode because we no longer have a confirmed external addresses"); + + self.mode = Mode::Client; + } + ([], Mode::Client) => { + self.mode = Mode::Client; + } + ([confirmed_external_addr], Mode::Client) => { + // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. + log::debug!("Switching from client to server-mode with {remote_peer_id} under the assumption that {confirmed_external_addr} routes to {local_addr}"); + + self.mode = Mode::Server + } + // Now server-mode with multiple addresses, previously client-mode. + (confirmed_external_addresses, Mode::Client) => { + let confirmed_external_addresses = confirmed_external_addresses + .iter() + .map(|addr| addr.to_string()) + .collect::>() + .join(", "); + + log::debug!("Switching from client to server-mode with {remote_peer_id} under the assumption one of [{confirmed_external_addresses}] routes to {local_addr}"); + + self.mode = Mode::Server + } + // Now server-mode with one or more addresses, previously server-mode. + (_, Mode::Server) => self.mode = Mode::Server, + } + } } } From e5b7c3753c179d9bad439dffe8813778445dccef Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 15:06:59 +0200 Subject: [PATCH 083/105] Add more logs for routing table updates --- protocols/kad/src/behaviour.rs | 9 ++++++++- protocols/kad/src/handler.rs | 18 ++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 403988f5636..33e6e1c428d 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2048,9 +2048,14 @@ where // since the remote address on an inbound connection may be specific // to that connection (e.g. typically the TCP port numbers). let address = match endpoint { - ConnectedPoint::Dialer { address, .. } => Some(address), + ConnectedPoint::Dialer { address, .. } => { + log::debug!("Adding address {address} of {source} to routing table"); + + Some(address) + }, ConnectedPoint::Listener { .. } => None, }; + self.connection_updated(source, address, NodeStatus::Connected); } @@ -2426,6 +2431,8 @@ where let external_addresses_changed = self.external_addresses.on_swarm_event(&event); if external_addresses_changed { + log::debug!("External addresses changed, re-configuring established connections"); + self.queued_events.extend(self.inbound_connections.iter().map(|(conn_id, peer_id)| ToSwarm::NotifyHandler { peer_id: *peer_id, handler: NotifyHandler::One(*conn_id), diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 510e3836a2c..7dafb9ec7aa 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -97,6 +97,7 @@ pub struct KademliaHandler { /// The states of protocol confirmation that a connection /// handler transitions through. +#[derive(Copy, Clone)] enum ProtocolStatus { /// It is as yet unknown whether the remote supports the /// configured protocol name. @@ -887,10 +888,19 @@ impl ConnectionHandler for KademliaHandler { .iter() .any(|p| self.protocol_config.protocol_names().contains(p)); - if remote_supports_our_kademlia_protocols { - self.protocol_status = ProtocolStatus::Confirmed; - } else { - self.protocol_status = ProtocolStatus::NotSupported; + match (remote_supports_our_kademlia_protocols, self.protocol_status) { + (true, ProtocolStatus::Confirmed) => {}, + (true, _) => { + log::info!("Remote {} now supports our kademlia protocol", self.remote_peer_id); + + self.protocol_status = ProtocolStatus::Confirmed; + }, + (false, ProtocolStatus::Confirmed | ProtocolStatus::Reported) => { + log::info!("Remote {} no longer supports our kademlia protocol", self.remote_peer_id); + + self.protocol_status = ProtocolStatus::NotSupported; + }, + (false, _) => {}, } } } From 5d7d648b2916f4a14f2a576300aab5e306ab5db0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 15:07:41 +0200 Subject: [PATCH 084/105] Include reported in condition --- protocols/kad/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 7dafb9ec7aa..00559722f65 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -889,7 +889,7 @@ impl ConnectionHandler for KademliaHandler { .any(|p| self.protocol_config.protocol_names().contains(p)); match (remote_supports_our_kademlia_protocols, self.protocol_status) { - (true, ProtocolStatus::Confirmed) => {}, + (true, ProtocolStatus::Confirmed | ProtocolStatus::Reported) => {}, (true, _) => { log::info!("Remote {} now supports our kademlia protocol", self.remote_peer_id); From 2145c7d3560f4cbe0b3095a0f9ad0c5ce6c6e50b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 15:22:12 +0200 Subject: [PATCH 085/105] Fix formatting --- protocols/kad/src/behaviour.rs | 19 +++++++++++-------- protocols/kad/src/handler.rs | 18 ++++++++++++------ protocols/kad/tests/client_mode.rs | 5 +---- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 33e6e1c428d..8ab28cf33ee 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2052,7 +2052,7 @@ where log::debug!("Adding address {address} of {source} to routing table"); Some(address) - }, + } ConnectedPoint::Listener { .. } => None, }; @@ -2433,13 +2433,16 @@ where if external_addresses_changed { log::debug!("External addresses changed, re-configuring established connections"); - self.queued_events.extend(self.inbound_connections.iter().map(|(conn_id, peer_id)| ToSwarm::NotifyHandler { - peer_id: *peer_id, - handler: NotifyHandler::One(*conn_id), - event: KademliaHandlerIn::ReconfigureMode { - external_addresses: self.external_addresses.iter().cloned().collect(), - } - })); + self.queued_events + .extend(self.inbound_connections.iter().map(|(conn_id, peer_id)| { + ToSwarm::NotifyHandler { + peer_id: *peer_id, + handler: NotifyHandler::One(*conn_id), + event: KademliaHandlerIn::ReconfigureMode { + external_addresses: self.external_addresses.iter().cloned().collect(), + }, + } + })); } match event { diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 00559722f65..9088019cf62 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -889,18 +889,24 @@ impl ConnectionHandler for KademliaHandler { .any(|p| self.protocol_config.protocol_names().contains(p)); match (remote_supports_our_kademlia_protocols, self.protocol_status) { - (true, ProtocolStatus::Confirmed | ProtocolStatus::Reported) => {}, + (true, ProtocolStatus::Confirmed | ProtocolStatus::Reported) => {} (true, _) => { - log::info!("Remote {} now supports our kademlia protocol", self.remote_peer_id); + log::info!( + "Remote {} now supports our kademlia protocol", + self.remote_peer_id + ); self.protocol_status = ProtocolStatus::Confirmed; - }, + } (false, ProtocolStatus::Confirmed | ProtocolStatus::Reported) => { - log::info!("Remote {} no longer supports our kademlia protocol", self.remote_peer_id); + log::info!( + "Remote {} no longer supports our kademlia protocol", + self.remote_peer_id + ); self.protocol_status = ProtocolStatus::NotSupported; - }, - (false, _) => {}, + } + (false, _) => {} } } } diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index 63aa4099753..b187b6b9685 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -86,10 +86,7 @@ async fn adding_an_external_addresses_activates_server_mode_on_existing_connecti // Do the usual identify send/receive dance. match libp2p_swarm_test::drive(&mut client, &mut server).await { - ( - [Identify(_), Identify(_)], - [Identify(_), Identify(_)], - ) => {} + ([Identify(_), Identify(_)], [Identify(_), Identify(_)]) => {} other => panic!("Unexpected events: {other:?}"), } From 12b85f6dd59f61545039561e896f5e63b2339c36 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 15:25:35 +0200 Subject: [PATCH 086/105] Remove log that we can't guarantee --- protocols/kad/src/behaviour.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 8ab28cf33ee..3b7612b53a8 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2048,11 +2048,7 @@ where // since the remote address on an inbound connection may be specific // to that connection (e.g. typically the TCP port numbers). let address = match endpoint { - ConnectedPoint::Dialer { address, .. } => { - log::debug!("Adding address {address} of {source} to routing table"); - - Some(address) - } + ConnectedPoint::Dialer { address, .. } => Some(address), ConnectedPoint::Listener { .. } => None, }; From 05b31cba9ad99a036208e9fb2f9ede8dc23b4a8b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 23 May 2023 15:27:27 +0200 Subject: [PATCH 087/105] Only log if we are actually connected --- protocols/kad/src/behaviour.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 3b7612b53a8..ae7784e5da2 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2426,8 +2426,14 @@ where self.listen_addresses.on_swarm_event(&event); let external_addresses_changed = self.external_addresses.on_swarm_event(&event); - if external_addresses_changed { - log::debug!("External addresses changed, re-configuring established connections"); + if external_addresses_changed && !self.inbound_connections.is_empty() { + let num_connections = self.inbound_connections.len(); + + log::debug!( + "External addresses changed, re-configuring {} inbound connection{}", + num_connections, + if num_connections > 1 { "s" } else { "" } + ); self.queued_events .extend(self.inbound_connections.iter().map(|(conn_id, peer_id)| { From 61fd753e81c716ba87c4d94de2421b1d9d6278f4 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 24 May 2023 10:33:59 +0200 Subject: [PATCH 088/105] Set listen address as external address to trigger server mode --- protocols/kad/src/behaviour/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/kad/src/behaviour/test.rs b/protocols/kad/src/behaviour/test.rs index 43145f0c79e..ce7712b9e8e 100644 --- a/protocols/kad/src/behaviour/test.rs +++ b/protocols/kad/src/behaviour/test.rs @@ -71,6 +71,7 @@ fn build_node_with_config(cfg: KademliaConfig) -> (Multiaddr, TestSwarm) { let address: Multiaddr = Protocol::Memory(random::()).into(); swarm.listen_on(address.clone()).unwrap(); + swarm.add_external_address(address.clone()); (address, swarm) } From 82985ff327a0a797b78b61a07d218e84984e9d2e Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 12:54:55 +0100 Subject: [PATCH 089/105] Have a global mode for kademlia --- protocols/kad/CHANGELOG.md | 6 ++ protocols/kad/src/behaviour.rs | 45 ++++++---- protocols/kad/src/handler.rs | 157 ++++++++++++++++++--------------- 3 files changed, 120 insertions(+), 88 deletions(-) diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index d7a35b1b8fb..8381b172bda 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -6,7 +6,13 @@ - Remove deprecated public modules `handler`, `protocol` and `kbucket`. See [PR 3896]. +- Automatically configure client/server mode based on external addresses. + If we have or learn about an external address of our node, we operate in server-mode and thus allow inbound requests. + By default, all connections are in client-mode and only allow outbound requests. + See [PR 3877]. + [PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715 +[PR 3877]: https://github.com/libp2p/rust-libp2p/pull/3877 [PR 3896]: https://github.com/libp2p/rust-libp2p/pull/3896 ## 0.43.3 diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index ae7784e5da2..5a427fa2f98 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -106,7 +106,7 @@ pub struct Kademlia { external_addresses: ExternalAddresses, - inbound_connections: HashMap, + connections: HashMap, /// See [`KademliaConfig::caching`]. caching: KademliaCaching, @@ -452,7 +452,7 @@ where connection_idle_timeout: config.connection_idle_timeout, external_addresses: Default::default(), local_peer_id: id, - inbound_connections: Default::default(), + connections: Default::default(), } } @@ -1941,7 +1941,7 @@ where .. }: ConnectionClosed<::ConnectionHandler>, ) { - self.inbound_connections.remove(&connection_id); + self.connections.remove(&connection_id); if remaining_established == 0 { for query in self.queries.iter_mut() { @@ -1972,7 +1972,7 @@ where local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { - self.inbound_connections.insert(connection_id, peer); + self.connections.insert(connection_id, peer); Ok(KademliaHandler::new_inbound( self.protocol_config.clone(), @@ -1980,23 +1980,26 @@ where local_addr.clone(), remote_addr.clone(), peer, - self.external_addresses.iter().collect(), + self.external_addresses.iter().cloned().collect(), )) } fn handle_established_outbound_connection( &mut self, - _connection_id: ConnectionId, + connection_id: ConnectionId, peer: PeerId, addr: &Multiaddr, role_override: Endpoint, ) -> Result, ConnectionDenied> { + self.connections.insert(connection_id, peer); + Ok(KademliaHandler::new_outbound( self.protocol_config.clone(), self.connection_idle_timeout, addr.clone(), role_override, peer, + self.external_addresses.iter().cloned().collect(), )) } @@ -2426,25 +2429,31 @@ where self.listen_addresses.on_swarm_event(&event); let external_addresses_changed = self.external_addresses.on_swarm_event(&event); - if external_addresses_changed && !self.inbound_connections.is_empty() { - let num_connections = self.inbound_connections.len(); + if external_addresses_changed && !self.connections.is_empty() { + let num_connections = self.connections.len(); log::debug!( - "External addresses changed, re-configuring {} inbound connection{}", + "External addresses changed, re-configuring {} connection{}", num_connections, if num_connections > 1 { "s" } else { "" } ); self.queued_events - .extend(self.inbound_connections.iter().map(|(conn_id, peer_id)| { - ToSwarm::NotifyHandler { - peer_id: *peer_id, - handler: NotifyHandler::One(*conn_id), - event: KademliaHandlerIn::ReconfigureMode { - external_addresses: self.external_addresses.iter().cloned().collect(), - }, - } - })); + .extend( + self.connections + .iter() + .map(|(conn_id, peer_id)| ToSwarm::NotifyHandler { + peer_id: *peer_id, + handler: NotifyHandler::One(*conn_id), + event: KademliaHandlerIn::ReconfigureMode { + external_addresses: self + .external_addresses + .iter() + .cloned() + .collect(), + }, + }), + ); } match event { diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 9088019cf62..18f6b6dc91e 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -483,42 +483,17 @@ impl KademliaHandler { local_addr: Multiaddr, remote_addr: Multiaddr, remote_peer_id: PeerId, - external_addresses: Vec<&Multiaddr>, + external_addresses: Vec, ) -> Self { - let mode = match external_addresses.as_slice() { - [] => { - log::debug!("Operating in client-mode on new inbound connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); - - Mode::Client - } - [confirmed_external_addr] => { - // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. - log::debug!("Operating in server-mode on new inbound connection from peer {remote_peer_id} under the assumption that {confirmed_external_addr} routes to {local_addr}"); - - Mode::Server - } - confirmed_external_addresses => { - let confirmed_external_addresses = confirmed_external_addresses - .iter() - .map(|addr| addr.to_string()) - .collect::>() - .join(", "); - - log::debug!("Operating in server-mode on new inbound connection from peer {remote_peer_id} under the assumption that one of [{confirmed_external_addresses}] routes to {local_addr}"); - - Mode::Server - } - }; - Self::new( protocol_config, - mode, idle_timeout, ConnectedPoint::Listener { local_addr, send_back_addr: remote_addr, }, remote_peer_id, + external_addresses, ) } @@ -529,34 +504,32 @@ impl KademliaHandler { addr: Multiaddr, role_override: Endpoint, peer: PeerId, + external_addresses: Vec, ) -> Self { - // Outbound connections are always in client-mode because we may be using an ephemeral port for dialing and thus the observed address may not be reachable. - log::debug!("Operating in client-mode on new outbound connection to peer {peer} at {addr}"); - Self::new( protocol_config, - Mode::Client, idle_timeout, ConnectedPoint::Dialer { address: addr, role_override, }, peer, + external_addresses, ) } fn new( protocol_config: KademliaProtocolConfig, - mode: Mode, idle_timeout: Duration, endpoint: ConnectedPoint, remote_peer_id: PeerId, + external_addresses: Vec, ) -> Self { let keep_alive = KeepAlive::Until(Instant::now() + idle_timeout); KademliaHandler { protocol_config, - mode, + mode: infer_mode(external_addresses, &endpoint, remote_peer_id, None), idle_timeout, endpoint, remote_peer_id, @@ -760,43 +733,12 @@ impl ConnectionHandler for KademliaHandler { self.answer_pending_request(request_id, KadResponseMsg::PutValue { key, value }); } KademliaHandlerIn::ReconfigureMode { external_addresses } => { - // Outbound connections are always in client mode. - let local_addr = match &self.endpoint { - ConnectedPoint::Dialer { .. } => return, - ConnectedPoint::Listener { local_addr, .. } => local_addr, - }; - let remote_peer_id = self.remote_peer_id; - - match (external_addresses.as_slice(), self.mode) { - ([], Mode::Server) => { - log::debug!("Switching from server to client-mode because we no longer have a confirmed external addresses"); - - self.mode = Mode::Client; - } - ([], Mode::Client) => { - self.mode = Mode::Client; - } - ([confirmed_external_addr], Mode::Client) => { - // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. - log::debug!("Switching from client to server-mode with {remote_peer_id} under the assumption that {confirmed_external_addr} routes to {local_addr}"); - - self.mode = Mode::Server - } - // Now server-mode with multiple addresses, previously client-mode. - (confirmed_external_addresses, Mode::Client) => { - let confirmed_external_addresses = confirmed_external_addresses - .iter() - .map(|addr| addr.to_string()) - .collect::>() - .join(", "); - - log::debug!("Switching from client to server-mode with {remote_peer_id} under the assumption one of [{confirmed_external_addresses}] routes to {local_addr}"); - - self.mode = Mode::Server - } - // Now server-mode with one or more addresses, previously server-mode. - (_, Mode::Server) => self.mode = Mode::Server, - } + self.mode = infer_mode( + external_addresses, + &self.endpoint, + self.remote_peer_id, + None, + ); } } } @@ -1256,3 +1198,78 @@ fn process_kad_response(event: KadResponseMsg, query_id: QueryId) -> KademliaHan }, } } + +fn infer_mode( + external_addresses: Vec, + endpoint: &ConnectedPoint, + remote_peer_id: PeerId, + current_mode: Option, +) -> Mode { + match (external_addresses.as_slice(), &endpoint, current_mode) { + ([], _, None | Some(Mode::Server)) => { + log::debug!("Operating in client-mode on connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); + + Mode::Client + } + ( + [confirmed_external_addr], + ConnectedPoint::Listener { local_addr, .. }, + None | Some(Mode::Client), + ) => { + // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. + log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that {confirmed_external_addr} routes to {local_addr}"); + + Mode::Server + } + ( + confirmed_external_addresses, + ConnectedPoint::Listener { local_addr, .. }, + None | Some(Mode::Client), + ) => { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); + + log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to {local_addr}"); + + Mode::Server + } + ( + confirmed_external_addresses, + ConnectedPoint::Dialer { .. }, + None | Some(Mode::Client), + ) => { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); + + log::debug!("Operating in server-mode on outbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to us"); + + // This assumes that the remote node somehow figures out a dialable address for us. + // For example, our observed address may not be reachable if we don't reuse ports on TCP. + // We leave it up to the remote to figure this out. + // Maybe they categorically don't add inbound connections to their routing table. + // We assume to be publicly reachable on `confirmed_external_addr`, hence we allow inbound requests, i.e. server-mode. + Mode::Server + } + (confirmed_external_addresses, _, Some(Mode::Server)) => { + debug_assert!( + !confirmed_external_addresses.is_empty(), + "Previous match arm handled empty list" + ); + + // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. + + Mode::Server + } + } +} + +fn to_comma_separated_list(confirmed_external_addresses: &[T]) -> String +where + T: ToString, +{ + confirmed_external_addresses + .iter() + .map(|addr| addr.to_string()) + .collect::>() + .join(", ") +} From dcf84ca6e620d7497695676a8ae6732f42463cc3 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 12:56:37 +0100 Subject: [PATCH 090/105] Only allocate in debug mode --- protocols/kad/src/handler.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 18f6b6dc91e..44a86388e08 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -1226,10 +1226,12 @@ fn infer_mode( ConnectedPoint::Listener { local_addr, .. }, None | Some(Mode::Client), ) => { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); - log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to {local_addr}"); + log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to {local_addr}"); + } Mode::Server } @@ -1238,10 +1240,12 @@ fn infer_mode( ConnectedPoint::Dialer { .. }, None | Some(Mode::Client), ) => { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); - log::debug!("Operating in server-mode on outbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to us"); + log::debug!("Operating in server-mode on outbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to us"); + } // This assumes that the remote node somehow figures out a dialable address for us. // For example, our observed address may not be reachable if we don't reuse ports on TCP. From 5779b6b0fa8c25f63b104a8ba9ce1d571e41a246 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:09:17 +0100 Subject: [PATCH 091/105] Inline `limit` field --- swarm/src/behaviour/external_addresses.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 89e06e26608..2ff618bbb41 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -11,14 +11,12 @@ const MAX_LOCAL_EXTERNAL_ADDRS: usize = 20; #[derive(Debug, Clone)] pub struct ExternalAddresses { addresses: HashSet, - limit: usize, } impl Default for ExternalAddresses { fn default() -> Self { Self { addresses: Default::default(), - limit: MAX_LOCAL_EXTERNAL_ADDRS, } } } @@ -35,7 +33,7 @@ impl ExternalAddresses { pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { match event { FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - if self.addresses.len() < self.limit { + if self.addresses.len() < MAX_LOCAL_EXTERNAL_ADDRS { return self.addresses.insert((*addr).clone()); } } From 9d874b541b17dda696ce705aa27ba29abf315684 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:13:20 +0100 Subject: [PATCH 092/105] Rewrite `ExternalAddresses` to be backed by `Vec` This allows us to expose an `as_slice` function. --- swarm/src/behaviour/external_addresses.rs | 39 ++++++++++++++--------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 2ff618bbb41..98e96a28e8e 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -1,6 +1,5 @@ use crate::behaviour::{ExternalAddrConfirmed, ExternalAddrExpired, FromSwarm}; use libp2p_core::Multiaddr; -use std::collections::HashSet; /// The maximum number of local external addresses. When reached any /// further externally reported addresses are ignored. The behaviour always @@ -8,17 +7,9 @@ use std::collections::HashSet; const MAX_LOCAL_EXTERNAL_ADDRS: usize = 20; /// Utility struct for tracking the external addresses of a [`Swarm`](crate::Swarm). -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ExternalAddresses { - addresses: HashSet, -} - -impl Default for ExternalAddresses { - fn default() -> Self { - Self { - addresses: Default::default(), - } - } + addresses: Vec, } impl ExternalAddresses { @@ -27,18 +18,36 @@ impl ExternalAddresses { self.addresses.iter() } + pub fn as_slice(&self) -> &[Multiaddr] { + self.addresses.as_slice() + } + /// Feed a [`FromSwarm`] event to this struct. /// /// Returns whether the event changed our set of external addresses. pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { match event { FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - if self.addresses.len() < MAX_LOCAL_EXTERNAL_ADDRS { - return self.addresses.insert((*addr).clone()); + if self.addresses.contains(addr) { + return false; + } + + if self.addresses.len() >= MAX_LOCAL_EXTERNAL_ADDRS { + return false; } + + self.addresses.insert(0, (*addr).clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. + + return true; } - FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr, .. }) => { - return self.addresses.remove(addr) + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: expired_addr, .. }) => { + let pos = match self.addresses.iter().position(|candidate| candidate == *expired_addr) { + None => return false, + Some(p) => p, + }; + + self.addresses.remove(pos); + return true; } _ => {} } From ca4f67c838fc7c1170aa6b62aa632f4db696021d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:34:51 +0100 Subject: [PATCH 093/105] Move mode variable to `Behaviour` --- protocols/kad/src/behaviour.rs | 153 +++++++++++++++++++++++++--- protocols/kad/src/handler.rs | 178 +++++++-------------------------- 2 files changed, 175 insertions(+), 156 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 5a427fa2f98..feec175c325 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -113,6 +113,8 @@ pub struct Kademlia { local_peer_id: PeerId, + mode: Mode, + /// The record storage. store: TStore, } @@ -453,6 +455,7 @@ where external_addresses: Default::default(), local_peer_id: id, connections: Default::default(), + mode: Mode::Client, } } @@ -1951,6 +1954,74 @@ where self.connected_peers.remove(&peer_id); } } + + fn infer_mode( + &self, + endpoint: &ConnectedPoint, + remote_peer_id: PeerId, + current_mode: Option, + ) -> Mode { + match (self.external_addresses.as_slice(), &endpoint, current_mode) { + ([], _, None | Some(Mode::Server)) => { + log::debug!("Operating in client-mode on connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); + + Mode::Client + } + ( + [confirmed_external_addr], + ConnectedPoint::Listener { local_addr, .. }, + None | Some(Mode::Client), + ) => { + // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. + log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that {confirmed_external_addr} routes to {local_addr}"); + + Mode::Server + } + ( + confirmed_external_addresses, + ConnectedPoint::Listener { local_addr, .. }, + None | Some(Mode::Client), + ) => { + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); + + log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to {local_addr}"); + } + + Mode::Server + } + ( + confirmed_external_addresses, + ConnectedPoint::Dialer { .. }, + None | Some(Mode::Client), + ) => { + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); + + log::debug!("Operating in server-mode on outbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to us"); + } + + // This assumes that the remote node somehow figures out a dialable address for us. + // For example, our observed address may not be reachable if we don't reuse ports on TCP. + // We leave it up to the remote to figure this out. + // Maybe they categorically don't add inbound connections to their routing table. + // We assume to be publicly reachable on `confirmed_external_addr`, hence we allow inbound requests, i.e. server-mode. + Mode::Server + } + (confirmed_external_addresses, _, Some(Mode::Server)) => { + debug_assert!( + !confirmed_external_addresses.is_empty(), + "Previous match arm handled empty list" + ); + + // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. + + Mode::Server + } + } + } } /// Exponentially decrease the given duration (base 2). @@ -1972,15 +2043,18 @@ where local_addr: &Multiaddr, remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { + let connected_point = ConnectedPoint::Listener { + local_addr: local_addr.clone(), + send_back_addr: remote_addr.clone(), + }; self.connections.insert(connection_id, peer); - Ok(KademliaHandler::new_inbound( + Ok(KademliaHandler::new( self.protocol_config.clone(), self.connection_idle_timeout, - local_addr.clone(), - remote_addr.clone(), + connected_point, peer, - self.external_addresses.iter().cloned().collect(), + self.mode, )) } @@ -1991,15 +2065,18 @@ where addr: &Multiaddr, role_override: Endpoint, ) -> Result, ConnectionDenied> { + let connected_point = ConnectedPoint::Dialer { + address: addr.clone(), + role_override, + }; self.connections.insert(connection_id, peer); - Ok(KademliaHandler::new_outbound( + Ok(KademliaHandler::new( self.protocol_config.clone(), self.connection_idle_timeout, - addr.clone(), - role_override, + connected_point, peer, - self.external_addresses.iter().cloned().collect(), + self.mode, )) } @@ -2430,6 +2507,34 @@ where let external_addresses_changed = self.external_addresses.on_swarm_event(&event); if external_addresses_changed && !self.connections.is_empty() { + self.mode = match (self.external_addresses.as_slice(), self.mode) { + ([], Mode::Server) => { + log::debug!("Switching to client-mode because we no longer have any confirmed external addresses"); + + Mode::Client + } + (confirmed_external_addresses, Mode::Client) => { + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); + + log::debug!("Switching to server-mode assuming that one of [{confirmed_external_addresses}] is externally reachable"); + } + + Mode::Server + } + (confirmed_external_addresses, Mode::Server) => { + debug_assert!( + !confirmed_external_addresses.is_empty(), + "Previous match arm handled empty list" + ); + + // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. + + Mode::Server + } + }; + let num_connections = self.connections.len(); log::debug!( @@ -2446,11 +2551,7 @@ where peer_id: *peer_id, handler: NotifyHandler::One(*conn_id), event: KademliaHandlerIn::ReconfigureMode { - external_addresses: self - .external_addresses - .iter() - .cloned() - .collect(), + new_mode: self.mode, }, }), ); @@ -3222,3 +3323,29 @@ pub enum RoutingUpdate { /// peer ID). Failed, } + +#[derive(PartialEq, Copy, Clone, Debug)] +pub enum Mode { + Client, + Server, +} + +impl fmt::Display for Mode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Mode::Client => write!(f, "client"), + Mode::Server => write!(f, "server"), + } + } +} + +fn to_comma_separated_list(confirmed_external_addresses: &[T]) -> String +where + T: ToString, +{ + confirmed_external_addresses + .iter() + .map(|addr| addr.to_string()) + .collect::>() + .join(", ") +} diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 44a86388e08..21134e4e927 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -18,6 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use crate::behaviour::Mode; use crate::protocol::{ KadInStreamSink, KadOutStreamSink, KadPeer, KadRequestMsg, KadResponseMsg, KademliaProtocolConfig, @@ -28,7 +29,7 @@ use either::Either; use futures::prelude::*; use futures::stream::SelectAll; use instant::Instant; -use libp2p_core::{upgrade, ConnectedPoint, Endpoint, Multiaddr}; +use libp2p_core::{upgrade, ConnectedPoint}; use libp2p_identity::PeerId; use libp2p_swarm::handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, @@ -112,12 +113,6 @@ enum ProtocolStatus { Reported, } -#[derive(PartialEq, Copy, Clone, Debug)] -enum Mode { - Client, - Server, -} - /// State of an active outbound substream. enum OutboundSubstreamState { /// Waiting to send a message to the remote. @@ -370,11 +365,8 @@ pub enum KademliaHandlerIn { /// for the query on the remote. Reset(KademliaRequestId), - /// Re-evaluate whether the current connection should operate in client or server mode. - /// - /// When the external addresses of a node change, we may be able to switch into server mode or - /// might have switch from server to client mode on existing connections. - ReconfigureMode { external_addresses: Vec }, + /// Change the connection to the specified mode. + ReconfigureMode { new_mode: Mode }, /// Request for the list of nodes whose IDs are the closest to `key`. The number of nodes /// returned is not specified, but should be around 20. @@ -476,60 +468,31 @@ pub struct KademliaRequestId { struct UniqueConnecId(u64); impl KademliaHandler { - /// Create a [`KademliaHandler`] for a new inbound connection. - pub fn new_inbound( - protocol_config: KademliaProtocolConfig, - idle_timeout: Duration, - local_addr: Multiaddr, - remote_addr: Multiaddr, - remote_peer_id: PeerId, - external_addresses: Vec, - ) -> Self { - Self::new( - protocol_config, - idle_timeout, - ConnectedPoint::Listener { - local_addr, - send_back_addr: remote_addr, - }, - remote_peer_id, - external_addresses, - ) - } - - /// Create a [`KademliaHandler`] for a new outbound connection. - pub fn new_outbound( - protocol_config: KademliaProtocolConfig, - idle_timeout: Duration, - addr: Multiaddr, - role_override: Endpoint, - peer: PeerId, - external_addresses: Vec, - ) -> Self { - Self::new( - protocol_config, - idle_timeout, - ConnectedPoint::Dialer { - address: addr, - role_override, - }, - peer, - external_addresses, - ) - } - - fn new( + pub fn new( protocol_config: KademliaProtocolConfig, idle_timeout: Duration, endpoint: ConnectedPoint, remote_peer_id: PeerId, - external_addresses: Vec, + mode: Mode, ) -> Self { + match &endpoint { + ConnectedPoint::Dialer { .. } => { + log::debug!( + "Operating in {mode}-mode on new outbound connection to {remote_peer_id}" + ); + } + ConnectedPoint::Listener { .. } => { + log::debug!( + "Operating in {mode}-mode on new inbound connection to {remote_peer_id}" + ); + } + } + let keep_alive = KeepAlive::Until(Instant::now() + idle_timeout); KademliaHandler { protocol_config, - mode: infer_mode(external_addresses, &endpoint, remote_peer_id, None), + mode, idle_timeout, endpoint, remote_peer_id, @@ -732,13 +695,21 @@ impl ConnectionHandler for KademliaHandler { } => { self.answer_pending_request(request_id, KadResponseMsg::PutValue { key, value }); } - KademliaHandlerIn::ReconfigureMode { external_addresses } => { - self.mode = infer_mode( - external_addresses, - &self.endpoint, - self.remote_peer_id, - None, - ); + KademliaHandlerIn::ReconfigureMode { new_mode } => { + let peer = self.remote_peer_id; + + match &self.endpoint { + ConnectedPoint::Dialer { .. } => { + log::debug!( + "Now operating in {new_mode}-mode on outbound connection with {peer}" + ) + } + ConnectedPoint::Listener { local_addr, .. } => { + log::debug!("Now operating in {new_mode}-mode on inbound connection with {peer} assuming that one of our external addresses routes to {local_addr}") + } + } + + self.mode = new_mode; } } } @@ -1198,82 +1169,3 @@ fn process_kad_response(event: KadResponseMsg, query_id: QueryId) -> KademliaHan }, } } - -fn infer_mode( - external_addresses: Vec, - endpoint: &ConnectedPoint, - remote_peer_id: PeerId, - current_mode: Option, -) -> Mode { - match (external_addresses.as_slice(), &endpoint, current_mode) { - ([], _, None | Some(Mode::Server)) => { - log::debug!("Operating in client-mode on connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); - - Mode::Client - } - ( - [confirmed_external_addr], - ConnectedPoint::Listener { local_addr, .. }, - None | Some(Mode::Client), - ) => { - // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. - log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that {confirmed_external_addr} routes to {local_addr}"); - - Mode::Server - } - ( - confirmed_external_addresses, - ConnectedPoint::Listener { local_addr, .. }, - None | Some(Mode::Client), - ) => { - if log::log_enabled!(log::Level::Debug) { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); - - log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to {local_addr}"); - } - - Mode::Server - } - ( - confirmed_external_addresses, - ConnectedPoint::Dialer { .. }, - None | Some(Mode::Client), - ) => { - if log::log_enabled!(log::Level::Debug) { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); - - log::debug!("Operating in server-mode on outbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to us"); - } - - // This assumes that the remote node somehow figures out a dialable address for us. - // For example, our observed address may not be reachable if we don't reuse ports on TCP. - // We leave it up to the remote to figure this out. - // Maybe they categorically don't add inbound connections to their routing table. - // We assume to be publicly reachable on `confirmed_external_addr`, hence we allow inbound requests, i.e. server-mode. - Mode::Server - } - (confirmed_external_addresses, _, Some(Mode::Server)) => { - debug_assert!( - !confirmed_external_addresses.is_empty(), - "Previous match arm handled empty list" - ); - - // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. - - Mode::Server - } - } -} - -fn to_comma_separated_list(confirmed_external_addresses: &[T]) -> String -where - T: ToString, -{ - confirmed_external_addresses - .iter() - .map(|addr| addr.to_string()) - .collect::>() - .join(", ") -} From ac0af4dcad2f1787ba65cff2a208f708cc7ba75d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:41:00 +0100 Subject: [PATCH 094/105] Ensure newer external addresses are prioritized --- swarm/src/behaviour/external_addresses.rs | 36 ++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 98e96a28e8e..c7266a06901 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -67,33 +67,49 @@ mod tests { fn new_external_addr_returns_correct_changed_value() { let mut addresses = ExternalAddresses::default(); - let changed = addresses.on_swarm_event(&new_external_addr()); + let changed = addresses.on_swarm_event(&new_external_addr1()); assert!(changed); - let changed = addresses.on_swarm_event(&new_external_addr()); + let changed = addresses.on_swarm_event(&new_external_addr1()); assert!(!changed) } #[test] fn expired_external_addr_returns_correct_changed_value() { let mut addresses = ExternalAddresses::default(); - addresses.on_swarm_event(&new_external_addr()); + addresses.on_swarm_event(&new_external_addr1()); - let changed = addresses.on_swarm_event(&expired_external_addr()); + let changed = addresses.on_swarm_event(&expired_external_addr1()); assert!(changed); - let changed = addresses.on_swarm_event(&expired_external_addr()); + let changed = addresses.on_swarm_event(&expired_external_addr1()); assert!(!changed) } - fn new_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR }) + #[test] + fn more_recent_external_addresses_are_prioritized() { + let mut addresses = ExternalAddresses::default(); + + addresses.on_swarm_event(&new_external_addr1()); + addresses.on_swarm_event(&new_external_addr2()); + + assert_eq!(addresses.as_slice(), &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()]); + } + + fn new_external_addr1() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR_1000 }) + } + + fn new_external_addr2() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR_2000 }) } - fn expired_external_addr() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: &MEMORY_ADDR }) + fn expired_external_addr1() -> FromSwarm<'static, dummy::ConnectionHandler> { + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: &MEMORY_ADDR_1000 }) } - static MEMORY_ADDR: Lazy = + static MEMORY_ADDR_1000: Lazy = Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000))); + static MEMORY_ADDR_2000: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(2000))); } From d9a7a19b20b245d2ae820912f47f0072d7e6102f Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:47:27 +0100 Subject: [PATCH 095/105] Add failing test for prioritizing addresses --- swarm/src/behaviour/external_addresses.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index c7266a06901..ecf06fc28a1 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -62,6 +62,7 @@ mod tests { use crate::dummy; use libp2p_core::multiaddr::Protocol; use once_cell::sync::Lazy; + use rand::Rng; #[test] fn new_external_addr_returns_correct_changed_value() { @@ -96,6 +97,21 @@ mod tests { assert_eq!(addresses.as_slice(), &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()]); } + #[test] + fn when_pushing_more_than_max_addresses_oldest_is_evicted() { + let mut addresses = ExternalAddresses::default(); + + for _ in 0..MAX_LOCAL_EXTERNAL_ADDRS { + let random_address = Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000))); + addresses.on_swarm_event(&FromSwarm::<'_, dummy::ConnectionHandler>::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &random_address })); + } + + addresses.on_swarm_event(&new_external_addr2()); + + assert_eq!(addresses.as_slice().len(), 20); + assert_eq!(addresses.as_slice()[0], (*MEMORY_ADDR_2000).clone()); + } + fn new_external_addr1() -> FromSwarm<'static, dummy::ConnectionHandler> { FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR_1000 }) } From b5eae688eb34a4769102814ff3c068e872e447d6 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:50:25 +0100 Subject: [PATCH 096/105] Make use of newer addresses and optimize insertion --- swarm/src/behaviour/external_addresses.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index ecf06fc28a1..ac5d82ceb1c 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -33,7 +33,11 @@ impl ExternalAddresses { } if self.addresses.len() >= MAX_LOCAL_EXTERNAL_ADDRS { - return false; + // If we hit the limit, we can add the address in O(1): + self.addresses.push((*addr).clone()); + self.addresses.swap_remove(0); + + return true; } self.addresses.insert(0, (*addr).clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. From da38b50748700a2e923b8ed04113ff4acf8e7569 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:52:23 +0100 Subject: [PATCH 097/105] Remove dead code --- protocols/kad/src/behaviour.rs | 68 ---------------------------------- 1 file changed, 68 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index feec175c325..1121249fa7f 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -1954,74 +1954,6 @@ where self.connected_peers.remove(&peer_id); } } - - fn infer_mode( - &self, - endpoint: &ConnectedPoint, - remote_peer_id: PeerId, - current_mode: Option, - ) -> Mode { - match (self.external_addresses.as_slice(), &endpoint, current_mode) { - ([], _, None | Some(Mode::Server)) => { - log::debug!("Operating in client-mode on connection from peer {remote_peer_id} because we don't have any confirmed external addresses"); - - Mode::Client - } - ( - [confirmed_external_addr], - ConnectedPoint::Listener { local_addr, .. }, - None | Some(Mode::Client), - ) => { - // This may not be true if we are listening on multiple interfaces but it is a good enough approximation for now. - log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that {confirmed_external_addr} routes to {local_addr}"); - - Mode::Server - } - ( - confirmed_external_addresses, - ConnectedPoint::Listener { local_addr, .. }, - None | Some(Mode::Client), - ) => { - if log::log_enabled!(log::Level::Debug) { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); - - log::debug!("Operating in server-mode on inbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to {local_addr}"); - } - - Mode::Server - } - ( - confirmed_external_addresses, - ConnectedPoint::Dialer { .. }, - None | Some(Mode::Client), - ) => { - if log::log_enabled!(log::Level::Debug) { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); - - log::debug!("Operating in server-mode on outbound connection from peer {remote_peer_id} assuming that one of [{confirmed_external_addresses}] routes to us"); - } - - // This assumes that the remote node somehow figures out a dialable address for us. - // For example, our observed address may not be reachable if we don't reuse ports on TCP. - // We leave it up to the remote to figure this out. - // Maybe they categorically don't add inbound connections to their routing table. - // We assume to be publicly reachable on `confirmed_external_addr`, hence we allow inbound requests, i.e. server-mode. - Mode::Server - } - (confirmed_external_addresses, _, Some(Mode::Server)) => { - debug_assert!( - !confirmed_external_addresses.is_empty(), - "Previous match arm handled empty list" - ); - - // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. - - Mode::Server - } - } - } } /// Exponentially decrease the given duration (base 2). From 403458c8b6bea3c9c136cc4c9fb7cc005ad94f99 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 13:58:06 +0100 Subject: [PATCH 098/105] Always change mode --- protocols/kad/src/behaviour.rs | 51 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 1121249fa7f..5d4b84c65f6 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -2438,39 +2438,44 @@ where self.listen_addresses.on_swarm_event(&event); let external_addresses_changed = self.external_addresses.on_swarm_event(&event); - if external_addresses_changed && !self.connections.is_empty() { - self.mode = match (self.external_addresses.as_slice(), self.mode) { - ([], Mode::Server) => { - log::debug!("Switching to client-mode because we no longer have any confirmed external addresses"); + self.mode = match (self.external_addresses.as_slice(), self.mode) { + ([], Mode::Server) => { + log::debug!("Switching to client-mode because we no longer have any confirmed external addresses"); - Mode::Client - } - (confirmed_external_addresses, Mode::Client) => { - if log::log_enabled!(log::Level::Debug) { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); + Mode::Client + } + ([], Mode::Client) => { + // Previously client-mode, now also client-mode because no external addresses. - log::debug!("Switching to server-mode assuming that one of [{confirmed_external_addresses}] is externally reachable"); - } + Mode::Client + } + (confirmed_external_addresses, Mode::Client) => { + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); - Mode::Server + log::debug!("Switching to server-mode assuming that one of [{confirmed_external_addresses}] is externally reachable"); } - (confirmed_external_addresses, Mode::Server) => { - debug_assert!( - !confirmed_external_addresses.is_empty(), - "Previous match arm handled empty list" - ); - // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. + Mode::Server + } + (confirmed_external_addresses, Mode::Server) => { + debug_assert!( + !confirmed_external_addresses.is_empty(), + "Previous match arm handled empty list" + ); - Mode::Server - } - }; + // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. + + Mode::Server + } + }; + if external_addresses_changed && !self.connections.is_empty() { let num_connections = self.connections.len(); log::debug!( - "External addresses changed, re-configuring {} connection{}", + "External addresses changed, re-configuring {} established connection{}", num_connections, if num_connections > 1 { "s" } else { "" } ); From fd26ca181b517f44969d55b94aa88822272ec9ff Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 14:01:17 +0100 Subject: [PATCH 099/105] Move debug assert --- protocols/kad/src/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 21134e4e927..080bf11ec7f 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -544,6 +544,7 @@ impl KademliaHandler { future::Either::Left(p) => p, future::Either::Right(p) => void::unreachable(p), }; + debug_assert_eq!(self.mode, Mode::Server); if let ProtocolStatus::Unknown = self.protocol_status { // Upon the first successfully negotiated substream, we know that the @@ -576,7 +577,6 @@ impl KademliaHandler { } } - debug_assert!(self.mode == Mode::Server); let connec_unique_id = self.next_connec_unique_id; self.next_connec_unique_id.0 += 1; self.inbound_substreams From c6000e8674c75a2e603ad9926f2985afd59c474d Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 23:01:58 +1000 Subject: [PATCH 100/105] Update protocols/kad/CHANGELOG.md Co-authored-by: Max Inden --- protocols/kad/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 8381b172bda..061420b676b 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -8,7 +8,7 @@ - Automatically configure client/server mode based on external addresses. If we have or learn about an external address of our node, we operate in server-mode and thus allow inbound requests. - By default, all connections are in client-mode and only allow outbound requests. + By default, a node is in client-mode and only allows outbound requests. See [PR 3877]. [PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715 From 566ebefe8d3f83c4addf68f744f9997ff8d5d371 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 14:02:40 +0100 Subject: [PATCH 101/105] Remove debug assert We could have switched to client mode between requesting the stream and establishing it. --- protocols/kad/src/handler.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/protocols/kad/src/handler.rs b/protocols/kad/src/handler.rs index 080bf11ec7f..948f14f30eb 100644 --- a/protocols/kad/src/handler.rs +++ b/protocols/kad/src/handler.rs @@ -544,7 +544,6 @@ impl KademliaHandler { future::Either::Left(p) => p, future::Either::Right(p) => void::unreachable(p), }; - debug_assert_eq!(self.mode, Mode::Server); if let ProtocolStatus::Unknown = self.protocol_status { // Upon the first successfully negotiated substream, we know that the From ead19e0f83a25e5ad6ce8caf09c608d08f7142b0 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 14:04:14 +0100 Subject: [PATCH 102/105] Fix formatting --- swarm/src/behaviour/external_addresses.rs | 38 ++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index ac5d82ceb1c..6d059f6afc7 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -44,8 +44,14 @@ impl ExternalAddresses { return true; } - FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: expired_addr, .. }) => { - let pos = match self.addresses.iter().position(|candidate| candidate == *expired_addr) { + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { + addr: expired_addr, .. + }) => { + let pos = match self + .addresses + .iter() + .position(|candidate| candidate == *expired_addr) + { None => return false, Some(p) => p, }; @@ -98,7 +104,10 @@ mod tests { addresses.on_swarm_event(&new_external_addr1()); addresses.on_swarm_event(&new_external_addr2()); - assert_eq!(addresses.as_slice(), &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()]); + assert_eq!( + addresses.as_slice(), + &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()] + ); } #[test] @@ -106,8 +115,15 @@ mod tests { let mut addresses = ExternalAddresses::default(); for _ in 0..MAX_LOCAL_EXTERNAL_ADDRS { - let random_address = Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000))); - addresses.on_swarm_event(&FromSwarm::<'_, dummy::ConnectionHandler>::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &random_address })); + let random_address = + Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000))); + addresses.on_swarm_event( + &FromSwarm::<'_, dummy::ConnectionHandler>::ExternalAddrConfirmed( + ExternalAddrConfirmed { + addr: &random_address, + }, + ), + ); } addresses.on_swarm_event(&new_external_addr2()); @@ -117,15 +133,21 @@ mod tests { } fn new_external_addr1() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR_1000 }) + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &MEMORY_ADDR_1000, + }) } fn new_external_addr2() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR_2000 }) + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &MEMORY_ADDR_2000, + }) } fn expired_external_addr1() -> FromSwarm<'static, dummy::ConnectionHandler> { - FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr: &MEMORY_ADDR_1000 }) + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { + addr: &MEMORY_ADDR_1000, + }) } static MEMORY_ADDR_1000: Lazy = From 78450303c8d89efccd5af4f97bffb3cc1ac61660 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 14:17:24 +0100 Subject: [PATCH 103/105] Keep the list of addresses in insertion order --- swarm/src/behaviour/external_addresses.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index 6d059f6afc7..a1785afa7d4 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -32,16 +32,11 @@ impl ExternalAddresses { return false; } + self.addresses.insert(0, (*addr).clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. if self.addresses.len() >= MAX_LOCAL_EXTERNAL_ADDRS { - // If we hit the limit, we can add the address in O(1): - self.addresses.push((*addr).clone()); - self.addresses.swap_remove(0); - - return true; + self.addresses.pop(); } - self.addresses.insert(0, (*addr).clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. - return true; } FromSwarm::ExternalAddrExpired(ExternalAddrExpired { From d9401c92647e89fd89723cdd20bf5a09dd228fea Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 30 May 2023 14:23:52 +0100 Subject: [PATCH 104/105] Refresh newly reported addresses --- swarm/src/behaviour/external_addresses.rs | 35 ++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index a1785afa7d4..d8f2b2d23e1 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -28,12 +28,21 @@ impl ExternalAddresses { pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { match event { FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { - if self.addresses.contains(addr) { - return false; + if let Some(pos) = self + .addresses + .iter() + .position(|candidate| candidate == *addr) + { + // Refresh the existing confirmed address. + self.addresses.remove(pos); + self.push_front(addr); + + return false; // No changes to our external addresses. } - self.addresses.insert(0, (*addr).clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. - if self.addresses.len() >= MAX_LOCAL_EXTERNAL_ADDRS { + self.push_front(addr); + + if self.addresses.len() > MAX_LOCAL_EXTERNAL_ADDRS { self.addresses.pop(); } @@ -59,6 +68,10 @@ impl ExternalAddresses { false } + + fn push_front(&mut self, addr: &Multiaddr) { + self.addresses.insert(0, addr.clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. + } } #[cfg(test)] @@ -127,6 +140,20 @@ mod tests { assert_eq!(addresses.as_slice()[0], (*MEMORY_ADDR_2000).clone()); } + #[test] + fn reporting_existing_external_address_moves_it_to_the_front() { + let mut addresses = ExternalAddresses::default(); + + addresses.on_swarm_event(&new_external_addr1()); + addresses.on_swarm_event(&new_external_addr2()); + addresses.on_swarm_event(&new_external_addr1()); + + assert_eq!( + addresses.as_slice(), + &[(*MEMORY_ADDR_1000).clone(), (*MEMORY_ADDR_2000).clone()] + ); + } + fn new_external_addr1() -> FromSwarm<'static, dummy::ConnectionHandler> { FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr: &MEMORY_ADDR_1000, From cdef2d54f42acc15fb243e1e13387d9227092389 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 31 May 2023 09:43:11 +0100 Subject: [PATCH 105/105] Add log for dropping previously confirmed external address --- swarm/src/behaviour/external_addresses.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/swarm/src/behaviour/external_addresses.rs b/swarm/src/behaviour/external_addresses.rs index d8f2b2d23e1..a3d6e3c0814 100644 --- a/swarm/src/behaviour/external_addresses.rs +++ b/swarm/src/behaviour/external_addresses.rs @@ -37,13 +37,17 @@ impl ExternalAddresses { self.addresses.remove(pos); self.push_front(addr); + log::debug!("Refreshed external address {addr}"); + return false; // No changes to our external addresses. } self.push_front(addr); if self.addresses.len() > MAX_LOCAL_EXTERNAL_ADDRS { - self.addresses.pop(); + let expired = self.addresses.pop().expect("list to be not empty"); + + log::debug!("Removing previously confirmed external address {expired} because we reached the limit of {MAX_LOCAL_EXTERNAL_ADDRS} addresses"); } return true;