From 8124270037d2949fb455f704f6bc45716179767e Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 31 Oct 2026 20:51:53 +0100 Subject: libp2p: identify replaces addresses --- src/transport/libp2p/book.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'src/transport/libp2p/book.rs') diff --git a/src/transport/libp2p/book.rs b/src/transport/libp2p/book.rs index 6706512..ceb9d27 100644 --- a/src/transport/libp2p/book.rs +++ b/src/transport/libp2p/book.rs @@ -36,6 +36,11 @@ pub(super) struct NamedRelay { #[derive(Default)] pub(super) struct Book { pub(super) addresses: HashMap>, + /// Where connected peers say they are, by their last identify (`identified`): while + /// connected, the only direct addresses of theirs kept. Forgotten when the last + /// connection to the peer closes, as a peer restarted elsewhere is found by what + /// the others say. + pub(super) identified: HashMap>, pub(super) listening: Vec, /// Where the router forwards to our listen addresses (UPnP, `Libp2pConfig::upnp`). pub(super) mapped: Vec, @@ -205,6 +210,7 @@ impl Book { .collect(); for peer in &stale { self.addresses.remove(peer); + self.identified.remove(peer); self.relays.remove(peer); self.public_relays.remove(peer); self.continuum.remove(peer); @@ -439,18 +445,50 @@ impl Book { .all(|address| known.contains(&without_peer(address.clone()))) } - /// `peer` is at `address` (without its `/p2p` part): kept first. + /// `peer` is at `address` (without its `/p2p` part): kept first. Not a direct + /// address the peer's identify left out (`identified`): one it no longer has, or + /// one it doesn't publish (`Libp2pConfig::ignore`). pub(super) fn remember(&mut self, peer: PeerId, address: Multiaddr) { let address = without_peer(address); if !self.addresses.contains_key(&peer) && self.addresses.len() >= MAX_PEERS { return; } + if self + .identified + .get(&peer) + .is_some_and(|said| is_ip(&address) && !said.contains(&address)) + { + return; + } self.touch(peer); let addresses = self.addresses.entry(peer).or_default(); addresses.retain(|known| *known != address); addresses.insert(0, address); addresses.truncate(ADDRESSES_PER_PEER); } + + /// `peer`, connected, identified itself at `listen` (`published`): its direct + /// addresses are those from now on, while connected (`remember`); those through + /// relays and by name stay. + pub(super) fn identified(&mut self, peer: PeerId, listen: Vec) { + let listen: Vec = listen.into_iter().map(without_peer).collect(); + if let Some(known) = self.addresses.get_mut(&peer) { + known.retain(|address| !is_ip(address) || listen.contains(address)); + } + self.identified.insert(peer, listen.clone()); + // Last first: `remember` puts each in front. + for address in listen.into_iter().rev() { + self.remember(peer, address); + } + } +} + +/// Whether `address` is a direct one at an IP address (not through a relay, nor a name). +pub(super) fn is_ip(address: &Multiaddr) -> bool { + matches!( + address.iter().next(), + Some(Protocol::Ip4(_) | Protocol::Ip6(_)) + ) && !is_relayed(address) } /// Whether `address` goes through a relay. -- cgit v1.3.1