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 521aa0026cc..7aa0dd87822 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(_) => {} } } @@ -410,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(); @@ -470,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/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..926445c9e49 100644 --- a/protocols/autonat/src/behaviour.rs +++ b/protocols/autonat/src/behaviour.rs @@ -36,10 +36,10 @@ 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, + 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(), } } @@ -279,6 +279,12 @@ impl Behaviour { self.servers.retain(|p| p != peer); } + /// 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(); + } + fn as_client(&mut self) -> AsClient { AsClient { inner: &mut self.inner, @@ -294,7 +300,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 +538,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) => { @@ -561,14 +566,17 @@ 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(_) => { - self.inner.on_swarm_event(external_addr); - self.as_client().on_new_address(); + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { + self.inner + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr }, + )); + self.probe_address(addr.to_owned()); } listen_failure @ FromSwarm::ListenFailure(_) => { self.inner.on_swarm_event(listen_failure) @@ -580,6 +588,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..e57523afaf8 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, ListenAddresses, PollParameters, ToSwarm}; use rand::{seq::SliceRandom, thread_rng}; use std::{ collections::{HashMap, HashSet, VecDeque}, @@ -97,13 +95,13 @@ 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> { 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 @@ -201,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/protocols/autonat/tests/test_client.rs b/protocols/autonat/tests/test_client.rs index 221833c9377..1911d1a6b2d 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; @@ -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(), AddressScore::Infinite); - - 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:?}."), @@ -198,7 +158,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.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 5f6a9a269d4..0f14c6edb27 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}; @@ -131,10 +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(), - AddressScore::Infinite, - ); + client + .behaviour_mut() + .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 { @@ -274,10 +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(), - AddressScore::Infinite, - ); + client + .behaviour_mut() + .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 { diff --git a/protocols/dcutr/src/behaviour_impl.rs b/protocols/dcutr/src/behaviour_impl.rs index 57692d38898..7e669ad9b3d 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..8081924fffd 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; @@ -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. @@ -295,10 +294,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 +402,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 5d6a20fd859..a690b8153b6 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/relay/tests/lib.rs b/protocols/relay/tests/lib.rs index f3cb950c3c9..f865b64856f 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(); @@ -281,7 +281,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/protocols/rendezvous/src/client.rs b/protocols/rendezvous/src/client.rs index a8527ba5dfe..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)); @@ -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-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 c5e5b7f25c3..a6560190e48 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, + 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", @@ -299,21 +290,28 @@ 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`](crate::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 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. + /// - We made an educated guess based on one of our listen addresses. + /// - We established a new relay connection. + 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 /// with the given peer. @@ -351,9 +349,6 @@ impl ToSwarm { handler, event: f(event), }, - ToSwarm::ReportObservedAddr { address, score } => { - ToSwarm::ReportObservedAddr { address, score } - } ToSwarm::CloseConnection { peer_id, connection, @@ -361,6 +356,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 +378,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 +447,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 an external address was removed. - ExpiredExternalAddr(ExpiredExternalAddr<'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 of the local node was removed. + ExternalAddrConfirmed(ExternalAddrConfirmed<'a>), + /// Informs the behaviour that an external address of the local node expired, i.e. is no-longer confirmed. + ExternalAddrExpired(ExternalAddrExpired<'a>), } /// [`FromSwarm`] variant that informs the behaviour about a newly established connection to a peer. @@ -549,15 +549,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 +663,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..89e06e26608 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::{ExternalAddrConfirmed, ExternalAddrExpired, FromSwarm}; 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::ExternalAddrConfirmed(ExternalAddrConfirmed { 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::ExternalAddrConfirmed(ExternalAddrConfirmed { 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 0f2f3fc1093..66e9ba8a874 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)] @@ -77,13 +76,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 +107,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}; @@ -121,10 +121,10 @@ 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}; +use crate::behaviour::ExternalAddrConfirmed; use crate::handler::UpgradeInfoSend; use connection::pool::{EstablishedConnection, Pool, PoolConfig, PoolEvent}; use connection::IncomingInfo; @@ -142,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}; @@ -340,13 +339,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. @@ -654,60 +651,30 @@ 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. + /// Add a **confirmed** external address 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::NewExternalAddr(NewExternalAddr { addr: &a })); - expired - } - AddAddressResult::Updated { expired } => expired, - }; - for a in expired { - self.behaviour - .on_swarm_event(FromSwarm::ExpiredExternalAddr(ExpiredExternalAddr { - addr: &a.addr, - })); - } - result + /// 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 { + addr: &a, + })); + self.confirmed_external_addr.insert(a); } - /// Removes an external address of the local node, regardless of - /// its current score. See [`Swarm::add_external_address`] - /// for details. + /// Remove an external address for the local node. /// - /// 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::ExpiredExternalAddr(ExpiredExternalAddr { addr })); - true - } else { - false - } + /// 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 })); + self.confirmed_external_addr.remove(addr); } /// Disconnects a peer by its peer ID, closing all connections to said peer. @@ -1162,25 +1129,21 @@ 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`]. + 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, &address)) + .filter_map(|server| self.transport.address_translation(server, &addr)) .collect(); // remove duplicates @@ -1189,9 +1152,18 @@ where addrs }; for addr in translated_addresses { - self.add_external_address(addr, score); + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); } } + ToSwarm::ExternalAddrConfirmed(addr) => { + self.add_external_address(addr); + } + ToSwarm::ExternalAddrExpired(addr) => { + self.remove_external_address(&addr); + } ToSwarm::CloseConnection { peer_id, connection, @@ -1263,7 +1235,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) }; @@ -1431,13 +1402,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() @@ -1447,10 +1416,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 } @@ -1639,8 +1604,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(_) -> _) - } -} 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(_) => {} } } }