summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/swarm/peer/handshake.rs37
-rw-r--r--src/swarm/topology.rs8
-rw-r--r--src/transport/firewall.rs4
-rw-r--r--src/transport/mod.rs4
-rw-r--r--src/transport/mycelium/mod.rs4
-rw-r--r--src/transport/sim.rs5
-rw-r--r--src/transport/traffic.rs4
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
}