From eafeee2e8b000f00492fb99bbb196f185ffadf5c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 27 Jul 2020 15:40:15 +0200 Subject: [PATCH 1/7] protocols/kad: Kademlia::kbuckets include all Instead of only returning non-empty kbuckets via `Kademlia::kbuckets`, return empty ones as well. Doing so enables consumers to (1) reason about each and every bucket and (2) depend on a stable order of the buckets. --- protocols/kad/CHANGELOG.md | 5 +++++ protocols/kad/src/behaviour.rs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 44cb3f18e5c..62c28292519 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,3 +1,8 @@ +# Next + +- Include empty buckets in `Kademlia::kbuckets`. + + # 0.21.0 [2020-07-01] - Remove `KademliaEvent::Discovered` diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 8fd60e6d69d..5d6c8735cfe 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -529,11 +529,11 @@ where } } - /// Returns an iterator over all non-empty buckets in the routing table. + /// Returns an iterator over all buckets in the routing table. pub fn kbuckets(&mut self) -> impl Iterator, Addresses>> { - self.kbuckets.iter().filter(|b| !b.is_empty()) + self.kbuckets.iter() } /// Returns the k-bucket for the distance to the given key. From b9ee2cfd0653ffaaba01aa4ad607fd1952640cb7 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 31 Jul 2020 09:41:06 +0200 Subject: [PATCH 2/7] Revert "protocols/kad: Kademlia::kbuckets include all" This reverts commit eafeee2e8b000f00492fb99bbb196f185ffadf5c. --- protocols/kad/CHANGELOG.md | 5 ----- protocols/kad/src/behaviour.rs | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 62c28292519..44cb3f18e5c 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,8 +1,3 @@ -# Next - -- Include empty buckets in `Kademlia::kbuckets`. - - # 0.21.0 [2020-07-01] - Remove `KademliaEvent::Discovered` diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 5d6c8735cfe..8fd60e6d69d 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -529,11 +529,11 @@ where } } - /// Returns an iterator over all buckets in the routing table. + /// Returns an iterator over all non-empty buckets in the routing table. pub fn kbuckets(&mut self) -> impl Iterator, Addresses>> { - self.kbuckets.iter() + self.kbuckets.iter().filter(|b| !b.is_empty()) } /// Returns the k-bucket for the distance to the given key. From df8eda5f7c095e47b4aab3f70d9577bdb85a2dcb Mon Sep 17 00:00:00 2001 From: Max Inden Date: Fri, 31 Jul 2020 11:04:51 +0200 Subject: [PATCH 3/7] protocols/kad: Expose kbucket distance range Add `KBucketRef::range` exposing the minimum inclusive and maximum inclusive `Distance` for the bucket --- protocols/kad/src/kbucket.rs | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/protocols/kad/src/kbucket.rs b/protocols/kad/src/kbucket.rs index 2ea7f362443..ff0b662cc0b 100644 --- a/protocols/kad/src/kbucket.rs +++ b/protocols/kad/src/kbucket.rs @@ -116,6 +116,15 @@ impl BucketIndex { self.0 } + /// Returns the minimum inclusive and maximum inclusive [`Distance`] + /// included in the bucket for this index. + fn range(&self) -> (Distance, Distance) { + ( + Distance(U256::pow(U256::from(2), U256::from(self.0))), + Distance(U256::pow(U256::from(2), U256::from(self.0 + 1)) - 1), + ) + } + /// Generates a random distance that falls into the bucket for this index. fn rand_distance(&self, rng: &mut impl rand::Rng) -> Distance { let mut bytes = [0u8; 32]; @@ -447,6 +456,12 @@ where TKey: Clone + AsRef, TVal: Clone { + /// Returns the minimum inclusive and maximum inclusive [`Distance`] for + /// this bucket. + pub fn range(&self) -> (Distance, Distance) { + self.index.range() + } + /// Checks whether the bucket is empty. pub fn is_empty(&self) -> bool { self.num_entries() == 0 @@ -525,6 +540,30 @@ mod tests { } } + #[test] + fn bucket_contains_range() { + fn prop(ix: u8) { + let index = BucketIndex(ix as usize); + let mut bucket = KBucket::, ()>::new(Duration::from_secs(0)); + let bucket_ref = KBucketRef { + index, + bucket: &mut bucket, + }; + + let (min, max) = bucket_ref.range(); + + assert!(min <= max); + + assert!(bucket_ref.contains(&min)); + assert!(bucket_ref.contains(&max)); + + assert!(!bucket_ref.contains(&Distance(min.0 - 1))); + assert!(!bucket_ref.contains(&Distance(max.0 + 1))); + } + + quickcheck(prop as fn(_)); + } + #[test] fn rand_distance() { fn prop(ix: u8) -> bool { From 501e80c5c73adbcceff4e01848874a560b560ee3 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 2 Aug 2020 22:15:56 +0200 Subject: [PATCH 4/7] protocols/kad: Add test for overlapping and exhaustive buckets --- protocols/kad/src/kbucket.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/protocols/kad/src/kbucket.rs b/protocols/kad/src/kbucket.rs index ff0b662cc0b..b74cbe15ee8 100644 --- a/protocols/kad/src/kbucket.rs +++ b/protocols/kad/src/kbucket.rs @@ -540,6 +540,23 @@ mod tests { } } + #[test] + fn buckets_are_non_overlapping_and_exhaustive() { + let local_key = Key::from(PeerId::random()); + let timeout = Duration::from_secs(0); + let mut table = KBucketsTable::::new(local_key.into(), timeout); + + let mut prev_max = U256::from(0); + + for bucket in table.iter() { + let (min, max) = bucket.range(); + assert_eq!(Distance(prev_max + U256::from(1)), min); + prev_max = max.0; + } + + assert_eq!(U256::MAX, prev_max); + } + #[test] fn bucket_contains_range() { fn prop(ix: u8) { From f8ba555ae4a75fd4d9f263ff86c882b66dd43b25 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Sun, 2 Aug 2020 22:17:05 +0200 Subject: [PATCH 5/7] protocols/kad: Fix overflow panic in range() --- protocols/kad/src/kbucket.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/protocols/kad/src/kbucket.rs b/protocols/kad/src/kbucket.rs index b74cbe15ee8..ebf2c728879 100644 --- a/protocols/kad/src/kbucket.rs +++ b/protocols/kad/src/kbucket.rs @@ -119,10 +119,13 @@ impl BucketIndex { /// Returns the minimum inclusive and maximum inclusive [`Distance`] /// included in the bucket for this index. fn range(&self) -> (Distance, Distance) { - ( - Distance(U256::pow(U256::from(2), U256::from(self.0))), - Distance(U256::pow(U256::from(2), U256::from(self.0 + 1)) - 1), - ) + let min = Distance(U256::pow(U256::from(2), U256::from(self.0))); + let max = match U256::overflowing_pow(U256::from(2), U256::from(self.0 + 1)) { + (exp, false) => Distance(exp - 1), + (_, true) => Distance(U256::MAX), + }; + + (min, max) } /// Generates a random distance that falls into the bucket for this index. From 283f71f79d68735959be40036f23928d5ccc097c Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 3 Aug 2020 11:47:29 +0200 Subject: [PATCH 6/7] protocols/kad: Refactor BucketIndex::range() --- protocols/kad/src/kbucket.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocols/kad/src/kbucket.rs b/protocols/kad/src/kbucket.rs index ebf2c728879..3de74fad6bf 100644 --- a/protocols/kad/src/kbucket.rs +++ b/protocols/kad/src/kbucket.rs @@ -120,12 +120,12 @@ impl BucketIndex { /// included in the bucket for this index. fn range(&self) -> (Distance, Distance) { let min = Distance(U256::pow(U256::from(2), U256::from(self.0))); - let max = match U256::overflowing_pow(U256::from(2), U256::from(self.0 + 1)) { - (exp, false) => Distance(exp - 1), - (_, true) => Distance(U256::MAX), - }; - - (min, max) + if self.0 == u8::MAX.into() { + (min, Distance(U256::MAX)) + } else { + let max = Distance(U256::pow(U256::from(2), U256::from(self.0 + 1)) - 1); + (min, max) + } } /// Generates a random distance that falls into the bucket for this index. From 44c58cb7893c9a7e93b110cff093f9c3f0a3f9f9 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Thu, 6 Aug 2020 09:35:17 +0200 Subject: [PATCH 7/7] protocols/kad/CHANGELOG: Add entry --- protocols/kad/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index 44cb3f18e5c..f308df05b21 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.22.0 [unreleased] + +- Add `KBucketRef::range` exposing the minimum inclusive and maximum inclusive + `Distance` for the bucket + ([PR 1680](https://github.com/libp2p/rust-libp2p/pull/1680)). + # 0.21.0 [2020-07-01] - Remove `KademliaEvent::Discovered`