From 9bdbbb2b9ec30d88a9cf39eaf35729d1ec3e53a7 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 3 Mar 2023 21:07:19 +0100 Subject: [PATCH] fix(disc): trigger bootstrap if table empty (#1621) --- crates/net/discv4/src/lib.rs | 44 ++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index eb1424ba47..d0fadddac8 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -24,8 +24,8 @@ use crate::{ use discv5::{ kbucket, kbucket::{ - BucketInsertResult, Distance, Entry as BucketEntry, KBucketsTable, NodeStatus, - MAX_NODES_PER_BUCKET, + BucketInsertResult, Distance, Entry as BucketEntry, InsertResult, KBucketsTable, + NodeStatus, MAX_NODES_PER_BUCKET, }, ConnectionDirection, ConnectionState, }; @@ -553,20 +553,24 @@ impl Discv4Service { /// **Note:** This is a noop if there are no bootnodes. pub fn bootstrap(&mut self) { for record in self.config.bootstrap_nodes.clone() { - debug!(target : "discv4", ?record, "Adding bootstrap node"); + debug!(target : "discv4", ?record, "pinging boot node"); let key = kad_key(record.id); let entry = NodeEntry::new(record); // insert the boot node in the table - let _ = self.kbuckets.insert_or_update( + match self.kbuckets.insert_or_update( &key, entry, NodeStatus { state: ConnectionState::Connected, direction: ConnectionDirection::Outgoing, }, - ); - self.try_ping(record, PingReason::Initial); + ) { + InsertResult::Failed(_) => {} + _ => { + self.try_ping(record, PingReason::Initial); + } + } } } @@ -625,13 +629,23 @@ impl Discv4Service { target_key.clone(), self.kbuckets .closest_values(&target_key) + .filter(|node| !self.pending_find_nodes.contains_key(&node.key.preimage().0)) .take(MAX_NODES_PER_BUCKET) .map(|n| (target_key.distance(&n.key), n.value.record)), tx, ); // From those 16, pick the 3 closest to start the concurrent lookup. - let closest = ctx.closest(ALPHA, |node| !self.pending_find_nodes.contains_key(&node.id)); + let closest = ctx.closest(ALPHA); + + if closest.is_empty() && self.pending_find_nodes.is_empty() { + // no closest nodes, and no lookup in progress: table is empty. + // This could happen if all records were deleted from the table due to missed pongs + // (e.g. connectivity problems over a long period of time, or issues during initial + // bootstrapping) so we attempt to bootstrap again + self.bootstrap(); + return + } trace!(target : "net::discv4", ?target, num = closest.len(), "Start lookup closest nodes"); @@ -1172,7 +1186,8 @@ impl Discv4Service { } // get the next closest nodes, not yet queried nodes and start over. - let closest = ctx.closest(ALPHA, |node| !self.pending_find_nodes.contains_key(&node.id)); + let closest = + ctx.filter_closest(ALPHA, |node| !self.pending_find_nodes.contains_key(&node.id)); for closest in closest { let key = kad_key(closest.id); @@ -1704,8 +1719,19 @@ impl LookupContext { self.inner.target.preimage().0 } + fn closest(&self, num: usize) -> Vec { + self.inner + .closest_nodes + .borrow() + .iter() + .filter(|(_, node)| !node.queried) + .map(|(_, n)| n.record) + .take(num) + .collect() + } + /// Returns the closest nodes that have not been queried yet. - fn closest

(&self, num: usize, filter: P) -> Vec + fn filter_closest

(&self, num: usize, filter: P) -> Vec where P: FnMut(&NodeRecord) -> bool, {