Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

protocols/kad: Expose kbucket distance range #1680

Merged
merged 8 commits into from
Aug 6, 2020
4 changes: 4 additions & 0 deletions protocols/kad/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

- Update `libp2p-core` and `libp2p-swarm` dependencies.

- 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`
Expand Down
59 changes: 59 additions & 0 deletions protocols/kad/src/kbucket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ impl BucketIndex {
self.0
}

/// Returns the minimum inclusive and maximum inclusive [`Distance`]
/// included in the bucket for this index.
fn range(&self) -> (Distance, Distance) {
let min = Distance(U256::pow(U256::from(2), U256::from(self.0)));
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.
fn rand_distance(&self, rng: &mut impl rand::Rng) -> Distance {
let mut bytes = [0u8; 32];
Expand Down Expand Up @@ -447,6 +459,12 @@ where
TKey: Clone + AsRef<KeyBytes>,
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
Expand Down Expand Up @@ -525,6 +543,47 @@ 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::<KeyBytes, ()>::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() {
romanb marked this conversation as resolved.
Show resolved Hide resolved
fn prop(ix: u8) {
let index = BucketIndex(ix as usize);
let mut bucket = KBucket::<Key<PeerId>, ()>::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 {
Expand Down