-
Notifications
You must be signed in to change notification settings - Fork 985
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
Support client mode in kademlia #2184
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.
There is still a lot missing here. Let me know if you need more details. Also see my comment #2032 (comment).
02a0a74
to
fc218a0
Compare
Hi @mxinden, |
Hi @mxinden, Could you give me some pointers about how to proceed from this point? |
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.
Great steps forward @whereistejas!
In case you are interested in working on specifications as well: It would be great to have the Client Mode feature described in the Kademlia specification.
examples/kademlia-example.rs
Outdated
@@ -0,0 +1,157 @@ | |||
#![allow(dead_code, unused_variables)] |
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.
Instead of an example, could you add a test to kad/src/behaviour/tests.rs
?
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.
Writing this test in the kademlia
module will be kind of difficult, as I'm using a lot of other things as well, for building the swarm or transport. Is it okay if I put this is in a test directory?
I can further enhance kademlia-example
to create four peers and cross-check if the peer in client
mode is actually, absent from the other's routing tables.
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.
Writing this test in the kademlia module will be kind of difficult, as I'm using a lot of other things as well, for building the swarm or transport. Is it okay if I put this is in a test directory?
No need for most of it. You can take the manual_bucket_insert
and bootstrap
test as an example.
I can further enhance kademlia-example to create four peers and cross-check if the peer in client mode is actually, absent from the other's routing tables.
I don't think there is any need for an example. The "cross-check" sounds great, but should really be done in a test instead of an example.
protocols/kad/src/handler.rs
Outdated
|
||
// If Mode::Client, node will act in `client` mode in the Kademlia network. | ||
pub client: Mode, |
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.
The Mode
logic you are introducing in this pull request replaces the KademliaHandlerConfig::allow_listening
logic. The latter can thus be replaced in favor of the former 🎉
protocols/kad/src/handler.rs
Outdated
// TODO: | ||
// Just because another peer is sending us Kademlia requests, doesn't necessarily | ||
// mean that it will answer the Kademlia requests that we send to it. | ||
// Thus, Commenting this code out for now. | ||
// if let ProtocolStatus::Unconfirmed = self.protocol_status { | ||
// // Upon the first successfully negotiated substream, we know that the | ||
// // remote is configured with the same protocol name and we want | ||
// // the behaviour to add this peer to the routing table, if possible. | ||
// self.protocol_status = ProtocolStatus::Confirmed; | ||
// } |
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.
Correct. I am in favor of documenting this, though obviously both the TODO
and the commented out code needs to be removed.
@@ -46,6 +46,18 @@ pub const DEFAULT_PROTO_NAME: &[u8] = b"/ipfs/kad/1.0.0"; | |||
/// The default maximum size for a varint length-delimited packet. | |||
pub const DEFAULT_MAX_PACKET_SIZE: usize = 16 * 1024; | |||
|
|||
#[derive(Debug, Clone)] |
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.
#[derive(Debug, Clone)] | |
/// See [`crate::KademliaConfig::set_mode`]. | |
#[derive(Debug, Clone)] |
Sure! I can do this. The current code changes in the |
Nothing comes to my mind. What exactly is not working? I don't think this is related to |
|
Okay, so something really weird is happening. I put some Update: I think this is happening because of the following code: MdnsEvent::Discovered(nodes) => {
for (peer_id, multiaddr) in nodes {
swarm
.behaviour_mut()
.kademlia
.add_address(&peer_id, multiaddr);
}
} SInce, I'm directly adding addresses, the peers are not performing a protocol negotiation. |
So, I guess the CI failure for commit |
protocols/kad/src/behaviour/test.rs
Outdated
if swarm.local_peer_id().clone() == peers[0] { | ||
is_server_present = peer == peers[1]; | ||
} | ||
return Poll::Ready(()); |
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.
I suggest reading the async book: https://rust-lang.github.io/async-book/
With the early return we might not check whether the server stores the client in its routing table.
protocols/kad/src/behaviour/test.rs
Outdated
.collect(); | ||
|
||
swarms[0].1.dial_addr(addrs[1].clone()).unwrap(); | ||
swarms[1].1.dial_addr(addrs[0].clone()).unwrap(); |
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.
Why does the server dial the client?
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 there to guarantee that server tried to reach out to the client, before we check if the client is absent from the server's routing table.
protocols/kad/src/behaviour/test.rs
Outdated
Poll::Ready(_) => { | ||
// Check if the client peer is NOT present in the server peer's routing table. | ||
if swarm.local_peer_id().clone() == peers[1] { | ||
is_client_absent = swarm.behaviour_mut().kbucket(peers[0]).is_none(); |
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 is kind of hard to reliably check for absence of something, especially in an async setting.
How about:
- The server stores a value
Kademlia::put_value
, waiting for aKademliaEvent::OutboundQueryCompleted
. - The client connects to the server.
- The client does a
Kademlia::get_record
, waiting for aKademliaEvent::OutboundQueryCompleted
. - Check that the client has the server in its routing table and check that the server does not have the client in its routing table.
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.
Sure. I will implement this. I will try to finish before the same time, tomorrow. Please, keep an eye out for any notifications from this PR. I want to get this PR closed, as early as possible (ofcourse, without sacrificing code quality).
Are we finished with the other parts of this PR except the test?
Hi @mxinden, the CI tests are passing on my local machine. Could you take a look at the code, please? |
Hi @mxinden, this PR ready for review. Just a gentle reminder. 😄 |
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.
Good progress here.
Just to make sure, does your test fail without the patch?
cfg.set_mode(Mode::Client); | ||
let mut client = build_node_with_config(cfg); | ||
|
||
// Fitler out peer Ids. |
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.
// Fitler out peer Ids. | |
// Filter out peer Ids. |
protocols/kad/src/behaviour/test.rs
Outdated
Ok(PutRecordOk { .. }) => { | ||
// Check if the server peer is not connected to the client peer. | ||
assert!( | ||
swarm | ||
.behaviour_mut() | ||
.connected_peers | ||
.iter() | ||
.all(|p| *p != peers[2]), | ||
"The server peer is connected to the client peer." | ||
); | ||
} |
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.
How about calling swarm[2].behaviour_mut().get_record(..)
once the first server is done with the PutRecord
query. That would prevent any races between the PUT and the GET.
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.
Hi, I have a doubt with the present test we have:
It is kind of hard to reliably check for absence of something, especially in an async setting.
- The server stores a value
Kademlia::put_value
, waiting for aKademliaEvent::OutboundQueryCompleted
.- The client connects to the server.
- The client does a
Kademlia::get_record
, waiting for aKademliaEvent::OutboundQueryCompleted
.- Check that the client has the server in its routing table and check that the server does not have the client in its routing table.
Are we sure that using the GetRecord operation will update the routing tables of the server peer? I'm trying to find the code, which does the "update"-ing. But, I'm unable to find it. Could you please point me towards the appropriate code?
Update: (Please correct me if I'm wrong) I don't think Kademlia is bi-directional. For example, if peer A adds peer B to its routing table, then peer B will not add peer A to its routing table.
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.
Update: (Please correct me if I'm wrong) I don't think Kademlia is bi-directional. For example, if peer A adds peer B to its routing table, then peer B will not add peer A to its routing table.
Before this pull request both peers would likely add each other to their respective routing table. After this pull request, whether the two peers add each other to their routing table depends on the Mode
the other peer is running in.
The test is not failing without the patch, which is worrying me. I will try to debug and find out why. |
|
||
// Create a client peer. | ||
let mut cfg = KademliaConfig::default(); | ||
cfg.set_mode(Mode::Server); |
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.
I am a bit confused. Why is the client initialized with Mode::Server
?
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.
Oops, that's just a typing mistake on my side. Anyways, this test should fail when the client is in Server
mode, which it is not. I have some doubts regarding the way Kademlia
works, which I have expressed here. Could you please help me out with those?
Closing here in favor of #2521. |
Fix #2032
Feature description:
From https://docs.ipfs.io/concepts/dht/#undialable-peers