-
Notifications
You must be signed in to change notification settings - Fork 973
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: Add Kademlia::get_closest_local_peers #2436
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late review.
Do I understand correctly that Kademlia::kbucket
is not enough for your use-case?
protocols/kad/src/behaviour.rs
Outdated
/// Returns closest peers to the given key; takes peers from local routing table | ||
pub fn local_closest_peers<'s, 'k: 's, K: 's>( | ||
&'s mut self, | ||
key: &'k kbucket::Key<K>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
key: &'k kbucket::Key<K>, | |
key: K, | |
// ... | |
where | |
K: Into<kbucket::Key<K>> + Into<Vec<u8>> + Clone, |
Why not mirror the signature of get_closest_peers
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we have to pass key
as a reference, so function can return an impl Iterator
that would use that reference.
We can't pass key by value, because then fn get_local_closest_peers
will own it and drop, thus invalidating impl Iterator
. Borrow checker would be unhappy about that :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible to do it this way:
pub fn get_local_closest_peers<'s, 'k: 's, K: 's>(
&'s mut self,
key: &'k K,
) -> impl ...
where
K: AsRef<kbucket::Key<K>> + Clone,
{
self.kbuckets.closest_keys(key.as_ref())
}
Do you find that better than the current version?
P.S. Into<Vec<u8>>
isn't required for get_local_closest_peers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a second thought, K: AsRef<kbucket::Key<K>> + Clone
doesn't make sense. It does make sense to do K: Into<Key<K>>
, cuz then if refers to the following From
impls:
impl From<Multihash> for Key<Multihash>
impl From<PeerId> for Key<PeerId>
impl From<Vec<u8>> for Key<Vec<u8>>
But since there aren't any analogous AsRef<Key<_>>
impls, the bound K: AsRef<kbucket::Key<K>>
doesn't make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I see best to stay with key: &'k kbucket::Key<K>
and rename K
to T
for naming consistency. WDYT?
Did that. Now it looks like this:
pub fn get_local_closest_peers<'s, 't: 's, T: 's>(
&'s mut self,
key: &'t kbucket::Key<T>,
) -> impl ...
where
T: Clone,
{
self.kbuckets.closest_keys(key)
}
That's right. It doesn't follow the XOR-metric sorting, so I can't have a consistent set of peers for a |
Co-authored-by: Max Inden <mail@max-inden.de>
Thanks for the explanation above on key needs to be borrowed. How about the following? /// Returns closest peers to the given key; takes peers from local routing table only.
pub fn get_closest_local_peers<'a, K: Clone>(
&'a mut self,
key: &'a kbucket::Key<K>,
) -> impl Iterator<Item = kbucket::Key<PeerId>> + 'a {
self.kbuckets.closest_keys(key)
}
Let me know what you think @folex. |
Thanks for the thorough comments!
Lifetime name Updated to reflect all your comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Final ask (I promise) would be to add a changelog entry in protocols/kad/CHANGELOG.md
. Sorry for the many back and forths.
It took me almost a year to submit this PR, so I'm really happy with review timings 😅 |
## Description Fixes #5626 ## Notes & open questions This is the nicest way I came up with, I decided to leave `get_closest_local_peers` as is since it does return all peers, not just `replication_factor` peers. Looking at #2436 it is not clear if @folex really needed all peers returned or it just happened that way. I'm also happy to change proposed API to return all peers if that is preferred by others. It is very unfortunate that `&mut self` is needed for this, I created #5644 that if resolved will allow to have `&self` instead. ## Change checklist - [x] I have performed a self-review of my own code - [x] I have made corresponding changes to the documentation - [ ] I have added tests that prove my fix is effective or that my feature works - [x] A changelog entry has been made in the appropriate crates Co-authored-by: João Oliveira <hello@jxs.pt>
Hi! I'm developing a project where a need came for a network discovery on top of the Kademlia. Here's an example fluencelabs/quickstart/oracle.
For this, I expose API to gather Kademlia's k-closest peers. However, exposing
get_closest_peers
to users isn't secure: it may (it sure does, I've tested 🥲) lead to amplification attack for obvious reasons. So, I ended up using Kademlia's local routing table for that, and it's working great for my purposes.However, maintaining a GitHub fork of libp2p is tedious. So it would be great to have this change upstream so I can use libp2p from crates.io instead of a GitHub fork. And I hope that may be useful for other people out there.
And that's what this PR is about: it adds a simple single-line method that runs
ClosestBucketsIter
over local kbuckets and returns it as iterator.Thanks in advance!