diff options
| author | tv <tv@krebsco.de> | 2026-10-26 05:45:20 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-26 05:45:20 +0100 |
| commit | 51e8c8e38c05d98487784e21f9df54cc18875b8b (patch) | |
| tree | 5b3a6f85ed79549bea8c104d39b9069b40a8d2ef /src | |
| parent | f12a839d6e6bdd29a86ac52d5de836519a41aaf0 (diff) | |
send bounded peer lists
Diffstat (limited to 'src')
| -rw-r--r-- | src/swarm/peer/handshake.rs | 37 | ||||
| -rw-r--r-- | src/swarm/topology.rs | 8 | ||||
| -rw-r--r-- | src/transport/firewall.rs | 4 | ||||
| -rw-r--r-- | src/transport/mod.rs | 4 | ||||
| -rw-r--r-- | src/transport/mycelium/mod.rs | 4 | ||||
| -rw-r--r-- | src/transport/sim.rs | 5 | ||||
| -rw-r--r-- | src/transport/traffic.rs | 4 |
7 files changed, 59 insertions, 7 deletions
diff --git a/src/swarm/peer/handshake.rs b/src/swarm/peer/handshake.rs index c46df9c..038dac6 100644 --- a/src/swarm/peer/handshake.rs +++ b/src/swarm/peer/handshake.rs @@ -13,6 +13,12 @@ use crate::swarm::sync::SyncEvent; use crate::swarm::topology; use crate::transport::{self, ParsedMessage}; +/// Most peers a HELLO_REPLY lists, and most we take from one: each listed peer may get +/// a HELLO, so a long list (a hub's hundreds of neighbours, or a peer making up keys) +/// costs the recipient as much as it is long. A joining node needs only a few to find +/// the swarm; the member list gives it the rest once it is ready. +const MAX_LISTED_PEERS: usize = 64; + pub(crate) async fn remember_peer(state: &AppState, peer: &str) { if peer.is_empty() { warn!("Ignoring empty peer public key"); @@ -183,6 +189,12 @@ pub(super) async fn send_hello_reply( peers.push(known); } } + // Those the recipient would most likely pick as neighbours itself (lowest pair + // score with it, as `topology` ranks them), if there are too many. + if peers.len() > MAX_LISTED_PEERS { + peers.sort_by_cached_key(|known| topology::pair_score(peer, known)); + peers.truncate(MAX_LISTED_PEERS); + } let auth = state .swarm_key @@ -215,18 +227,31 @@ pub(super) async fn send_hello_reply( Ok(()) } +/// Greets the peers a HELLO_REPLY listed: at most `MAX_LISTED_PEERS`, only valid +/// identities, and, once the member list is known, only those that are our neighbours +/// (it says whom we greet; a listed peer that isn't one would get nothing from us, so +/// it isn't remembered either). pub(super) async fn discover_peers(state: &AppState, from_peer: &str, advertised_peers: &[String]) { - // TODO here we could add a trace t oshwo advertised_peers + let view_known = topology::view_known(state); + let mut taken = 0; for discovered_peer in advertised_peers { - if discovered_peer == from_peer { + if discovered_peer == from_peer || !state.transport.is_peer_id(discovered_peer) { continue; } - - remember_peer(state, discovered_peer).await; - - if !topology::is_neighbour(state, discovered_peer) { + if view_known && !topology::is_neighbour(state, discovered_peer) { continue; } + if taken == MAX_LISTED_PEERS { + debug!( + from = %from_peer, + listed = advertised_peers.len(), + "HELLO_REPLY listed more peers than we take; ignoring the rest" + ); + break; + } + taken += 1; + + remember_peer(state, discovered_peer).await; if let Err(error) = send_hello_once(state, discovered_peer).await { warn!( peer = %discovered_peer, diff --git a/src/swarm/topology.rs b/src/swarm/topology.rs index 73c2f2c..e396a9d 100644 --- a/src/swarm/topology.rs +++ b/src/swarm/topology.rs @@ -39,6 +39,12 @@ pub(crate) struct NeighbourView { full_mesh: bool, } +/// Whether the neighbours are computed from the member list yet (before, everyone +/// counts as a neighbour). +pub(crate) fn view_known(state: &AppState) -> bool { + state.neighbours.read_or_recover().is_some() +} + /// Whether every reachable member is our neighbour (also while the view is unknown). pub(crate) fn is_full_mesh(state: &AppState) -> bool { state @@ -337,7 +343,7 @@ pub(crate) fn graph( } /// Symmetric: the same for (a, b) and (b, a). -fn pair_score(a: &str, b: &str) -> [u8; 32] { +pub(crate) fn pair_score(a: &str, b: &str) -> [u8; 32] { let (low, high) = if a <= b { (a, b) } else { (b, a) }; hash(PAIR_LABEL, &[low, high]) } diff --git a/src/transport/firewall.rs b/src/transport/firewall.rs index c8592bd..23d91fe 100644 --- a/src/transport/firewall.rs +++ b/src/transport/firewall.rs @@ -65,6 +65,10 @@ impl Transport for FirewallTransport { self.inner.local_id() } + fn is_peer_id(&self, id: &str) -> bool { + self.inner.is_peer_id(id) + } + async fn resolve_seed(&self, seed: &str) -> Result<String> { self.inner.resolve_seed(seed).await } diff --git a/src/transport/mod.rs b/src/transport/mod.rs index 14c5ee4..b2ce63f 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -323,6 +323,10 @@ pub trait Transport: Send + Sync { /// This node's identity on the transport (for Mycelium: its public key). fn local_id(&self) -> &str; + /// Whether `id` can be a node's identity on this transport (for Mycelium: a public + /// key). Identities other peers name to us (in a HELLO_REPLY) are checked with it. + fn is_peer_id(&self, id: &str) -> bool; + /// Resolves a seed (anything that leads to a node, e.g. an address) to that node's /// identity, as `local_id` gives it on that node. async fn resolve_seed(&self, seed: &str) -> Result<String>; diff --git a/src/transport/mycelium/mod.rs b/src/transport/mycelium/mod.rs index cbae567..0b74793 100644 --- a/src/transport/mycelium/mod.rs +++ b/src/transport/mycelium/mod.rs @@ -168,6 +168,10 @@ impl Transport for MyceliumTransport { &self.local_id } + fn is_peer_id(&self, id: &str) -> bool { + is_pubkey(id) + } + async fn resolve_seed(&self, hint: &str) -> Result<String> { if is_pubkey(hint) { return Ok(hint.to_owned()); diff --git a/src/transport/sim.rs b/src/transport/sim.rs index 675f382..05160f8 100644 --- a/src/transport/sim.rs +++ b/src/transport/sim.rs @@ -255,6 +255,11 @@ impl Transport for SimTransport { &self.id } + /// Any name will do. + fn is_peer_id(&self, id: &str) -> bool { + !id.is_empty() + } + /// A seed is a node ID; one no node has doesn't resolve (yet). async fn resolve_seed(&self, seed: &str) -> Result<String> { if self.net.is_known(seed) { diff --git a/src/transport/traffic.rs b/src/transport/traffic.rs index 3d17860..78c5fee 100644 --- a/src/transport/traffic.rs +++ b/src/transport/traffic.rs @@ -138,6 +138,10 @@ impl Transport for CountingTransport { self.inner.local_id() } + fn is_peer_id(&self, id: &str) -> bool { + self.inner.is_peer_id(id) + } + async fn resolve_seed(&self, seed: &str) -> Result<String> { self.inner.resolve_seed(seed).await } |
