feat(swarm): replace address scoring with explicit candidates

Previously, a `NetworkBehaviour` could report an `AddressScore` for an external address. This score was a `u32` and addresses would be ranked amongst those.

In reality, an address is either confirmed to be publicly reachable (via a protocol such as AutoNAT) or merely represents a candidate that might be an external address. In a way, addresses are guilty (private) until proven innocent (publicly reachable).

When a `NetworkBehaviour` reports an address candidate, we perform address translation on it to potentially correct for ephemeral ports of TCP. These candidates are then injected back into the `NetworkBehaviour`. Protocols such as AutoNAT can use these addresses as a source for probing their NAT status. Once confirmed, they can emit a `ToSwarm::ExternalAddrConfirmed` event which again will be passed to all `NetworkBehaviour`s.

This simplified approach will allow us implement Kademlia's client-mode (https://github.com/libp2p/rust-libp2p/issues/2032) without additional configuration options: As soon as an address is reported as publicly reachable, we can activate server-mode for that connection.

Related: https://github.com/libp2p/rust-libp2p/pull/3877.
Related: https://github.com/libp2p/rust-libp2p/issues/3953.
Related: https://github.com/libp2p/rust-libp2p/issues/2032.
Related: https://github.com/libp2p/go-libp2p/issues/2229.

Co-authored-by: Max Inden <mail@max-inden.de>

Pull-Request: #3954.
This commit is contained in:
Thomas Eizinger
2023-05-24 09:52:16 +02:00
committed by GitHub
parent a424310d60
commit 5e8f2e82e4
32 changed files with 273 additions and 822 deletions

View File

@ -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<Item = Vec<u8>>;
/// Iterator returned by [`listened_addresses`](PollParameters::listened_addresses).
type ListenedAddressesIter: ExactSizeIterator<Item = Multiaddr>;
/// Iterator returned by [`external_addresses`](PollParameters::external_addresses).
type ExternalAddressesIter: ExactSizeIterator<Item = AddressRecord>;
/// 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<TOutEvent, TInEvent> {
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<TOutEvent, TInEventOld> ToSwarm<TOutEvent, TInEventOld> {
handler,
event: f(event),
},
ToSwarm::ReportObservedAddr { address, score } => {
ToSwarm::ReportObservedAddr { address, score }
}
ToSwarm::CloseConnection {
peer_id,
connection,
@ -361,6 +356,9 @@ impl<TOutEvent, TInEventOld> ToSwarm<TOutEvent, TInEventOld> {
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<TOutEvent, THandlerIn> ToSwarm<TOutEvent, THandlerIn> {
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)),
}
}
}