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): allow to explicitly set Mode::{Client,Server} #4132

Merged
merged 33 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ef09472
feat: Add Kademlia::set_explicit_mode
dariusc93 Jun 29, 2023
a62fa23
chore: Return on ExplicitMode::Auto
dariusc93 Jun 29, 2023
94e8ddf
chore: Formatted code
dariusc93 Jun 29, 2023
d9f7ab8
chore: Remove ExplicitMode and add auto_mode and waker field
dariusc93 Jun 30, 2023
048607b
chore: Remove ExplicitMode
dariusc93 Jun 30, 2023
437be0f
chore: Move into a single function
dariusc93 Jun 30, 2023
884ce0c
chore: Group fields together
dariusc93 Jun 30, 2023
6faeade
chore: added test set_client_to_server_mode
dariusc93 Jul 1, 2023
b7f55b7
Merge branch 'master' into kad-explicit-mode
dariusc93 Jul 1, 2023
ce2bb2a
chore: Update test
dariusc93 Jul 1, 2023
7e93c8b
Merge branch 'kad-explicit-mode' of github.com:dariusc93/rust-libp2p …
dariusc93 Jul 1, 2023
bb42582
chore: Update CHANGELOG.md
dariusc93 Jul 1, 2023
6521bff
Merge branch 'master' into kad-explicit-mode
dariusc93 Jul 2, 2023
35f8013
Merge branch 'kad-explicit-mode' of github.com:dariusc93/rust-libp2p …
dariusc93 Jul 2, 2023
0423bcb
chore: Redetermine mode when setting mode to None
dariusc93 Jul 2, 2023
a3f43ce
Update protocols/kad/tests/client_mode.rs
dariusc93 Jul 2, 2023
7974cef
Update protocols/kad/src/behaviour.rs
dariusc93 Jul 2, 2023
f53172c
Merge branch 'kad-explicit-mode' of github.com:dariusc93/rust-libp2p …
dariusc93 Jul 2, 2023
bc260c7
fix: Correct test
dariusc93 Jul 2, 2023
f23a7f9
chore: Format code
dariusc93 Jul 2, 2023
82be8cd
Update protocols/kad/CHANGELOG.md
dariusc93 Jul 3, 2023
9af4772
chore: if statement into reconfigure_mode
dariusc93 Jul 3, 2023
bb18f70
chore: Move log into reconfigure_mode
dariusc93 Jul 3, 2023
5613545
Update protocols/kad/src/behaviour.rs
dariusc93 Jul 3, 2023
9ed97ed
Merge branch 'master' into kad-explicit-mode
dariusc93 Jul 3, 2023
39481fe
chore: Rename method
dariusc93 Jul 3, 2023
6e37c3c
chore: Return early and minor cleanup
dariusc93 Jul 3, 2023
aed03b7
Update protocols/kad/src/behaviour.rs
dariusc93 Jul 3, 2023
2b588e2
Merge branch 'kad-explicit-mode' of github.com:dariusc93/rust-libp2p …
dariusc93 Jul 3, 2023
6f500d3
Merge branch 'master' into kad-explicit-mode
dariusc93 Jul 3, 2023
365b3c5
chore: Update version
dariusc93 Jul 3, 2023
2ffb5ff
Merge branch 'kad-explicit-mode' of github.com:dariusc93/rust-libp2p …
dariusc93 Jul 3, 2023
2592177
chore: Bump version
dariusc93 Jul 3, 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
7 changes: 7 additions & 0 deletions protocols/kad/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.44.2 - unreleased
dariusc93 marked this conversation as resolved.
Show resolved Hide resolved

- Allow to explicitly set `Mode::{Client,Server}`.
See [PR 4132]

[PR 4132]: https://github.com/libp2p/rust-libp2p/pull/4132

## 0.44.1

- Expose `KBucketDistance`.
Expand Down
152 changes: 97 additions & 55 deletions protocols/kad/src/behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use smallvec::SmallVec;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::num::NonZeroUsize;
use std::task::{Context, Poll};
use std::task::{Context, Poll, Waker};
use std::time::Duration;
use std::vec;
use thiserror::Error;
Expand Down Expand Up @@ -114,6 +114,8 @@ pub struct Kademlia<TStore> {
local_peer_id: PeerId,

mode: Mode,
auto_mode: bool,
no_events_waker: Option<Waker>,

/// The record storage.
store: TStore,
Expand Down Expand Up @@ -456,6 +458,8 @@ where
local_peer_id: id,
connections: Default::default(),
mode: Mode::Client,
auto_mode: true,
no_events_waker: None,
}
}

Expand Down Expand Up @@ -990,6 +994,94 @@ where
id
}

/// Set the [`Mode`] in which we should operate.
///
/// By default, we are in [`Mode::Client`] and will swap into [`Mode::Server`] as soon as we have a confirmed, external address via [`FromSwarm::ExternalAddrConfirmed`].
///
/// Setting a mode via this function disables this automatic behaviour and unconditionally operates in the specified mode.
/// To reactivate the automatic configuration, pass [`None`] instead.
pub fn set_mode(&mut self, mode: Option<Mode>) {
match mode {
Some(mode) => {
self.mode = mode;
self.auto_mode = false;
self.reconfigure_mode();
}
None => {
self.auto_mode = true;
self.determine_mode_from_external_addresses();
}
}

if let Some(waker) = self.no_events_waker.take() {
waker.wake();
}
}

fn reconfigure_mode(&mut self) {
if self.connections.is_empty() {
return;
}

let num_connections = self.connections.len();

log::debug!(
"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,
},
}),
);
}

fn determine_mode_from_external_addresses(&mut self) {
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
}
};

self.reconfigure_mode();
}

/// Processes discovered peers from a successful request in an iterative `Query`.
fn discovered<'a, I>(&'a mut self, query_id: &QueryId, source: &PeerId, peers: I)
where
Expand Down Expand Up @@ -2428,6 +2520,8 @@ where
// If no new events have been queued either, signal `NotReady` to
// be polled again later.
if self.queued_events.is_empty() {
self.no_events_waker = Some(cx.waker().clone());

return Poll::Pending;
}
}
Expand All @@ -2437,60 +2531,8 @@ where
self.listen_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,
},
}),
);
if self.auto_mode && external_addresses_changed {
self.determine_mode_from_external_addresses();
}

match event {
Expand Down
2 changes: 1 addition & 1 deletion protocols/kad/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub use behaviour::{
AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult,
BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk,
GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError,
GetRecordOk, GetRecordResult, InboundRequest, NoKnownPeers, PeerRecord, PutRecordContext,
GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext,
PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef,
QueryResult, QueryStats, RoutingUpdate,
};
Expand Down
46 changes: 45 additions & 1 deletion protocols/kad/tests/client_mode.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use libp2p_identify as identify;
use libp2p_identity as identity;
use libp2p_kad::store::MemoryStore;
use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent};
use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent, Mode};
use libp2p_swarm::Swarm;
use libp2p_swarm_test::SwarmExt;

Expand Down Expand Up @@ -111,6 +111,50 @@ async fn adding_an_external_addresses_activates_server_mode_on_existing_connecti
}
}

#[async_std::test]
async fn set_client_to_server_mode() {
let _ = env_logger::try_init();

let mut client = Swarm::new_ephemeral(MyBehaviour::new);
client.behaviour_mut().kad.set_mode(Some(Mode::Client));

let mut server = Swarm::new_ephemeral(MyBehaviour::new);

server.listen().await;
client.connect(&mut server).await;

let server_peer_id = *server.local_peer_id();

match libp2p_swarm_test::drive(&mut client, &mut server).await {
(
[MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer, .. })],
[MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(identify::Event::Received { info, .. })],
) => {
assert_eq!(peer, server_peer_id);
assert!(info
.protocols
.iter()
.all(|proto| libp2p_kad::PROTOCOL_NAME.ne(proto)))
}
other => panic!("Unexpected events: {other:?}"),
}

client.behaviour_mut().kad.set_mode(Some(Mode::Server));

match libp2p_swarm_test::drive(&mut client, &mut server).await {
(
[MyBehaviourEvent::Identify(_)],
[MyBehaviourEvent::Identify(identify::Event::Received { info, .. }), MyBehaviourEvent::Kad(_)],
) => {
assert!(info
.protocols
.iter()
.any(|proto| libp2p_kad::PROTOCOL_NAME.eq(proto)))
}
other => panic!("Unexpected events: {other:?}"),
}
}

#[derive(libp2p_swarm::NetworkBehaviour)]
#[behaviour(prelude = "libp2p_swarm::derive_prelude")]
struct MyBehaviour {
Expand Down