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

feat(kad): implement automatic client mode #3877

Merged
merged 123 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from 122 commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
f2801fb
Report changes in supported protocols back to `ConnectionHandler`
thomaseizinger Mar 20, 2023
9a28cd2
Allow reporting of supported protocols by remote
thomaseizinger Mar 22, 2023
e536dea
Consume supported protocols in identify
thomaseizinger Mar 24, 2023
7699a1e
Report a remote's protocols to other handlers
thomaseizinger Mar 24, 2023
450fc1e
Add test for kademlia client mode
thomaseizinger Mar 24, 2023
a972b9c
Implement kademlia client-mode
thomaseizinger Mar 24, 2023
f9cf33f
Add tests for two servers connecting
thomaseizinger Mar 24, 2023
deb30f3
Report additions and removals of protocols instead
thomaseizinger Apr 13, 2023
3c1bf5f
Report listen protocols on startup to connection
thomaseizinger Apr 13, 2023
9e70729
Introduce `SupportedProtocols` type
thomaseizinger Apr 13, 2023
f1328c5
Deduplicate code and propagate supported protocols only once
thomaseizinger Apr 19, 2023
a4fbcda
Extend docs
thomaseizinger Apr 19, 2023
3e33108
Merge branch 'master' into 2680-explore
thomaseizinger Apr 19, 2023
572ed90
Fix compile errors
thomaseizinger Apr 19, 2023
586394b
Report changes to listen/external address set
thomaseizinger Apr 26, 2023
b329a09
Simplify identify protocol
thomaseizinger Apr 26, 2023
a63af89
Check for changes in inbound protocols on every poll
thomaseizinger Apr 26, 2023
72510dd
Fix clippy lints
thomaseizinger Apr 26, 2023
8ffafdd
Report changes in `SupportedProtocols`
thomaseizinger Apr 26, 2023
ea1a087
Only report changes to handler if there actually was a change
thomaseizinger Apr 26, 2023
27bc507
Do not implicitly dial peers upon push
thomaseizinger Apr 27, 2023
58039d9
Merge branch 'master' into 2680-explore
thomaseizinger Apr 27, 2023
74dd94a
Remove unused import
thomaseizinger Apr 27, 2023
628b519
Fix compile error
thomaseizinger Apr 27, 2023
c7b5011
Remove dbg!
thomaseizinger Apr 27, 2023
a02ca55
Update swarm/src/handler.rs
thomaseizinger Apr 27, 2023
c347c8a
Merge branch '2680-explore' of github.com:libp2p/rust-libp2p into 268…
thomaseizinger May 2, 2023
f3e5e71
Combine match arms where possible
thomaseizinger May 2, 2023
b7fa7ef
Add comment explaining static hashset
thomaseizinger May 2, 2023
2bd9d73
Add docs
thomaseizinger May 2, 2023
e90c40d
Update supported protocols for push messages
thomaseizinger May 2, 2023
84979e4
Use `pop` to avoid panicking branch in `remove`
thomaseizinger May 2, 2023
dbfc7e7
Use `poll_unpin`
thomaseizinger May 2, 2023
f2d2c88
Use `if let` for consistency
thomaseizinger May 2, 2023
b41aeb8
Rewrite to `if let` for consistency
thomaseizinger May 2, 2023
bcd872b
Fill in todo in toggle
thomaseizinger May 2, 2023
021f1d4
Merge branch 'master' into 2680-explore
thomaseizinger May 2, 2023
fb096ad
Merge branch 'master' into 2680-explore
thomaseizinger May 2, 2023
e95c738
Merge branch 'master' into 2680-explore
thomaseizinger May 4, 2023
82642b8
Restore `libp2p-kad`
thomaseizinger May 4, 2023
eb66489
Fix formatting
thomaseizinger May 4, 2023
e9e5d24
Implement kademlia client-mode
thomaseizinger May 4, 2023
6aeef70
Update cargo lock
thomaseizinger May 4, 2023
8c47bd6
Fix unit tests
thomaseizinger May 4, 2023
bf9421e
Change test to assert actual events received
thomaseizinger May 4, 2023
a82343a
Don't report empty set of protocols to handler
thomaseizinger May 4, 2023
19cd9b9
Use constructor
thomaseizinger May 4, 2023
6d3e9ee
Introduce test helper
thomaseizinger May 4, 2023
df93a4e
Make tests less noisy
thomaseizinger May 4, 2023
c50bcfd
Further simplify test and add comments
thomaseizinger May 4, 2023
a799798
Add failing test
thomaseizinger May 4, 2023
0260ad1
Make test pass, i.e. only report actual changes back to the handler
thomaseizinger May 4, 2023
bf99654
Extract helpers to make fields crate-private and add docs
thomaseizinger May 4, 2023
46f4e96
Return `SmallVec` from `from_full_sets` which allows iteration
thomaseizinger May 4, 2023
ae7fc93
Fix clippy warnings
thomaseizinger May 4, 2023
fe9a6e3
Fix rustdoc
thomaseizinger May 4, 2023
7a74139
Merge branch '2680-explore' into feat/kademlia-automatic-client-mode
thomaseizinger May 4, 2023
1adb1fe
Only update protocol support on change message
thomaseizinger May 7, 2023
0f4d7a9
Update docs
thomaseizinger May 7, 2023
91560a0
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 8, 2023
16f2656
Minimize diff
thomaseizinger May 8, 2023
3b0e3a7
More resilient pattern match
thomaseizinger May 8, 2023
44c26c1
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 14, 2023
73b48b5
Initial refactoring to new events
thomaseizinger May 16, 2023
d5f45b5
Generate candidates based on address translation
thomaseizinger May 16, 2023
ab12bb7
Use candidates in AutoNAT
thomaseizinger May 16, 2023
43058e1
Only keep minimal external address state in swarm
thomaseizinger May 16, 2023
b844600
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 16, 2023
2ae6f34
Always enable server mode for inbound connections
thomaseizinger May 16, 2023
58eb192
Determine mode based in inbound/outbound connection
thomaseizinger May 16, 2023
5a945a8
Update identify docs
thomaseizinger May 17, 2023
4bcb4c1
Insert and remove external addresses
thomaseizinger May 17, 2023
7fef679
Update docs
thomaseizinger May 17, 2023
5f0c01d
Apply suggestions from code review
thomaseizinger May 18, 2023
0256f8a
Fix doc link
thomaseizinger May 22, 2023
3c38b3f
Remove private NAT status test
thomaseizinger May 22, 2023
b1edd30
Fix more doc links
thomaseizinger May 22, 2023
7efd6ab
Merge branch 'master' into 2032/external-addr
thomaseizinger May 22, 2023
fc58b54
Fix compile error with latest master
thomaseizinger May 22, 2023
62ec482
Delete tests
thomaseizinger May 22, 2023
1c5d243
Update docs
thomaseizinger May 22, 2023
ccc9bf3
Revert "Delete tests"
thomaseizinger May 23, 2023
a4886f5
Fix tests by adding a new API to AutoNAT
thomaseizinger May 23, 2023
d7da756
Rename to `probe_address`
thomaseizinger May 23, 2023
5545130
Merge branch '2032/external-addr' into feat/kademlia-automatic-client…
thomaseizinger May 23, 2023
5d3084a
Merge branch 'master' into 2032/external-addr
thomaseizinger May 23, 2023
cd165e5
Merge branch '2032/external-addr' into feat/kademlia-automatic-client…
thomaseizinger May 23, 2023
fbea5de
Split handler ctor into inbound and outbound
thomaseizinger May 23, 2023
78dbdd3
Fix starved connection handler on local-protocols change event
thomaseizinger May 23, 2023
5d20d54
Push updated protocols to peer
thomaseizinger May 23, 2023
2cf984a
Add test for automatic switch to server mode on existing connections
thomaseizinger May 23, 2023
1233784
Remove handler config
thomaseizinger May 23, 2023
f565979
Outbound connections are never in server mode
thomaseizinger May 23, 2023
b703468
Add log for outbound client mode
thomaseizinger May 23, 2023
c47cc34
Activate server mode for inbound connections based on our external ad…
thomaseizinger May 23, 2023
a0622ca
Reconfigure existing connections to server mode
thomaseizinger May 23, 2023
e5b7c37
Add more logs for routing table updates
thomaseizinger May 23, 2023
5d7d648
Include reported in condition
thomaseizinger May 23, 2023
2145c7d
Fix formatting
thomaseizinger May 23, 2023
12b85f6
Remove log that we can't guarantee
thomaseizinger May 23, 2023
05b31cb
Only log if we are actually connected
thomaseizinger May 23, 2023
c102609
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 24, 2023
61fd753
Set listen address as external address to trigger server mode
thomaseizinger May 24, 2023
1b1d3df
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 25, 2023
56c1879
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 30, 2023
82985ff
Have a global mode for kademlia
thomaseizinger May 30, 2023
dcf84ca
Only allocate in debug mode
thomaseizinger May 30, 2023
5779b6b
Inline `limit` field
thomaseizinger May 30, 2023
9d874b5
Rewrite `ExternalAddresses` to be backed by `Vec`
thomaseizinger May 30, 2023
ca4f67c
Move mode variable to `Behaviour`
thomaseizinger May 30, 2023
ac0af4d
Ensure newer external addresses are prioritized
thomaseizinger May 30, 2023
d9a7a19
Add failing test for prioritizing addresses
thomaseizinger May 30, 2023
b5eae68
Make use of newer addresses and optimize insertion
thomaseizinger May 30, 2023
da38b50
Remove dead code
thomaseizinger May 30, 2023
403458c
Always change mode
thomaseizinger May 30, 2023
fd26ca1
Move debug assert
thomaseizinger May 30, 2023
c6000e8
Update protocols/kad/CHANGELOG.md
thomaseizinger May 30, 2023
566ebef
Remove debug assert
thomaseizinger May 30, 2023
ead19e0
Fix formatting
thomaseizinger May 30, 2023
7845030
Keep the list of addresses in insertion order
thomaseizinger May 30, 2023
d9401c9
Refresh newly reported addresses
thomaseizinger May 30, 2023
9fa7590
Merge branch 'master' into feat/kademlia-automatic-client-mode
thomaseizinger May 31, 2023
cdef2d5
Add log for dropping previously confirmed external address
thomaseizinger May 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions protocols/kad/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
- Remove deprecated public modules `handler`, `protocol` and `kbucket`.
See [PR 3896].

- Automatically configure client/server mode based on external addresses.
If we have or learn about an external address of our node, we operate in server-mode and thus allow inbound requests.
By default, a node is in client-mode and only allows outbound requests.
See [PR 3877].

[PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715
[PR 3877]: https://github.com/libp2p/rust-libp2p/pull/3877
[PR 3896]: https://github.com/libp2p/rust-libp2p/pull/3896

## 0.43.3
Expand Down
4 changes: 4 additions & 0 deletions protocols/kad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
thiserror = "1"

[dev-dependencies]
async-std = { version = "1.12.0", features = ["attributes"] }
env_logger = "0.10.0"
futures-timer = "3.0"
libp2p-identify = { path = "../identify" }
libp2p-noise = { workspace = true }
libp2p-swarm = { path = "../../swarm", features = ["macros"] }
libp2p-swarm-test = { path = "../../swarm-test" }
libp2p-yamux = { workspace = true }
quickcheck = { workspace = true }

Expand Down
151 changes: 125 additions & 26 deletions protocols/kad/src/behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@
mod test;

use crate::addresses::Addresses;
use crate::handler::{
KademliaHandler, KademliaHandlerConfig, KademliaHandlerEvent, KademliaHandlerIn,
KademliaRequestId,
};
use crate::handler::{KademliaHandler, KademliaHandlerEvent, KademliaHandlerIn, KademliaRequestId};
use crate::jobs::*;
use crate::kbucket::{self, Distance, KBucketsTable, NodeStatus};
use crate::protocol::{KadConnectionType, KadPeer, KademliaProtocolConfig};
Expand All @@ -52,7 +49,7 @@ use libp2p_swarm::{
};
use log::{debug, info, warn};
use smallvec::SmallVec;
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::num::NonZeroUsize;
use std::task::{Context, Poll};
Expand Down Expand Up @@ -109,11 +106,15 @@ pub struct Kademlia<TStore> {

external_addresses: ExternalAddresses,

connections: HashMap<ConnectionId, PeerId>,

/// See [`KademliaConfig::caching`].
caching: KademliaCaching,

local_peer_id: PeerId,

mode: Mode,

/// The record storage.
store: TStore,
}
Expand Down Expand Up @@ -453,6 +454,8 @@ where
connection_idle_timeout: config.connection_idle_timeout,
external_addresses: Default::default(),
local_peer_id: id,
connections: Default::default(),
mode: Mode::Client,
}
}

Expand Down Expand Up @@ -1937,9 +1940,12 @@ where
ConnectionClosed {
peer_id,
remaining_established,
connection_id,
..
}: ConnectionClosed<<Self as NetworkBehaviour>::ConnectionHandler>,
) {
self.connections.remove(&connection_id);

if remaining_established == 0 {
for query in self.queries.iter_mut() {
query.on_failure(&peer_id);
Expand All @@ -1964,43 +1970,45 @@ where

fn handle_established_inbound_connection(
&mut self,
_connection_id: ConnectionId,
connection_id: ConnectionId,
peer: PeerId,
local_addr: &Multiaddr,
remote_addr: &Multiaddr,
) -> Result<THandler<Self>, ConnectionDenied> {
let connected_point = ConnectedPoint::Listener {
local_addr: local_addr.clone(),
send_back_addr: remote_addr.clone(),
};
self.connections.insert(connection_id, peer);

Ok(KademliaHandler::new(
KademliaHandlerConfig {
protocol_config: self.protocol_config.clone(),
allow_listening: true,
idle_timeout: self.connection_idle_timeout,
},
ConnectedPoint::Listener {
local_addr: local_addr.clone(),
send_back_addr: remote_addr.clone(),
},
self.protocol_config.clone(),
self.connection_idle_timeout,
connected_point,
peer,
self.mode,
))
}

fn handle_established_outbound_connection(
&mut self,
_connection_id: ConnectionId,
connection_id: ConnectionId,
peer: PeerId,
addr: &Multiaddr,
role_override: Endpoint,
) -> Result<THandler<Self>, ConnectionDenied> {
let connected_point = ConnectedPoint::Dialer {
address: addr.clone(),
role_override,
};
self.connections.insert(connection_id, peer);

Ok(KademliaHandler::new(
KademliaHandlerConfig {
protocol_config: self.protocol_config.clone(),
allow_listening: true,
idle_timeout: self.connection_idle_timeout,
},
ConnectedPoint::Dialer {
address: addr.clone(),
role_override,
},
self.protocol_config.clone(),
self.connection_idle_timeout,
connected_point,
peer,
self.mode,
))
}

Expand Down Expand Up @@ -2055,9 +2063,18 @@ where
ConnectedPoint::Dialer { address, .. } => Some(address),
ConnectedPoint::Listener { .. } => None,
};

self.connection_updated(source, address, NodeStatus::Connected);
}

KademliaHandlerEvent::ProtocolNotSupported { endpoint } => {
let address = match endpoint {
ConnectedPoint::Dialer { address, .. } => Some(address),
ConnectedPoint::Listener { .. } => None,
};
self.connection_updated(source, address, NodeStatus::Disconnected);
}

KademliaHandlerEvent::FindNodeReq { key, request_id } => {
let closer_peers = self.find_closest(&kbucket::Key::new(key), &source);

Expand Down Expand Up @@ -2419,7 +2436,63 @@ where

fn on_swarm_event(&mut self, event: FromSwarm<Self::ConnectionHandler>) {
self.listen_addresses.on_swarm_event(&event);
self.external_addresses.on_swarm_event(&event);
let external_addresses_changed = self.external_addresses.on_swarm_event(&event);

self.mode = match (self.external_addresses.as_slice(), self.mode) {
([], Mode::Server) => {
log::debug!("Switching to client-mode because we no longer have any confirmed external addresses");

Mode::Client
}
([], Mode::Client) => {
// Previously client-mode, now also client-mode because no external addresses.

Mode::Client
}
(confirmed_external_addresses, Mode::Client) => {
if log::log_enabled!(log::Level::Debug) {
let confirmed_external_addresses =
to_comma_separated_list(confirmed_external_addresses);

log::debug!("Switching to server-mode assuming that one of [{confirmed_external_addresses}] is externally reachable");
}

Mode::Server
}
(confirmed_external_addresses, Mode::Server) => {
debug_assert!(
!confirmed_external_addresses.is_empty(),
"Previous match arm handled empty list"
);

// Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam.

Mode::Server
}
};

if external_addresses_changed && !self.connections.is_empty() {
let num_connections = self.connections.len();

log::debug!(
"External addresses changed, re-configuring {} established connection{}",
num_connections,
if num_connections > 1 { "s" } else { "" }
);

self.queued_events
.extend(
self.connections
.iter()
.map(|(conn_id, peer_id)| ToSwarm::NotifyHandler {
peer_id: *peer_id,
handler: NotifyHandler::One(*conn_id),
event: KademliaHandlerIn::ReconfigureMode {
new_mode: self.mode,
},
}),
);
}

match event {
FromSwarm::ConnectionEstablished(connection_established) => {
Expand Down Expand Up @@ -3187,3 +3260,29 @@ pub enum RoutingUpdate {
/// peer ID).
Failed,
}

#[derive(PartialEq, Copy, Clone, Debug)]
pub enum Mode {
Client,
Server,
}

impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Mode::Client => write!(f, "client"),
Mode::Server => write!(f, "server"),
}
}
}

fn to_comma_separated_list<T>(confirmed_external_addresses: &[T]) -> String
where
T: ToString,
{
confirmed_external_addresses
.iter()
.map(|addr| addr.to_string())
.collect::<Vec<_>>()
.join(", ")
}
1 change: 1 addition & 0 deletions protocols/kad/src/behaviour/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ fn build_node_with_config(cfg: KademliaConfig) -> (Multiaddr, TestSwarm) {

let address: Multiaddr = Protocol::Memory(random::<u64>()).into();
swarm.listen_on(address.clone()).unwrap();
swarm.add_external_address(address.clone());

(address, swarm)
}
Expand Down
Loading