Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

client/network: Allow configuring Kademlia's disjoint query paths #7356

Merged
1 commit merged into from
Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions client/cli/src/params/network_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ pub struct NetworkParams {
/// By default this option is true for `--dev` and false otherwise.
#[structopt(long)]
pub discover_local: bool,

/// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in the
/// presence of potentially adversarial nodes.
///
/// See the S/Kademlia paper for more information on the high level design as well as its
/// security improvements.
#[structopt(long)]
pub kademlia_disjoint_query_paths: bool,
}

impl NetworkParams {
Expand Down Expand Up @@ -162,6 +170,7 @@ impl NetworkParams {
},
max_parallel_downloads: self.max_parallel_downloads,
allow_non_globals_in_dht: self.discover_local || is_dev,
kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
}
}
}
4 changes: 4 additions & 0 deletions client/network/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,9 @@ pub struct NetworkConfiguration {
pub max_parallel_downloads: u32,
/// Should we insert non-global addresses into the DHT?
pub allow_non_globals_in_dht: bool,
/// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in the
/// presence of potentially adversarial nodes.
pub kademlia_disjoint_query_paths: bool,
}

impl NetworkConfiguration {
Expand Down Expand Up @@ -455,6 +458,7 @@ impl NetworkConfiguration {
},
max_parallel_downloads: 5,
allow_non_globals_in_dht: false,
kademlia_disjoint_query_paths: false,
}
}

Expand Down
92 changes: 56 additions & 36 deletions client/network/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ pub struct DiscoveryConfig {
allow_non_globals_in_dht: bool,
discovery_only_if_under_num: u64,
enable_mdns: bool,
kademlias: HashMap<ProtocolId, Kademlia<MemoryStore>>
kademlia_disjoint_query_paths: bool,
protocol_ids: HashSet<ProtocolId>,
}

impl DiscoveryConfig {
Expand All @@ -97,7 +98,8 @@ impl DiscoveryConfig {
allow_non_globals_in_dht: false,
discovery_only_if_under_num: std::u64::MAX,
enable_mdns: false,
kademlias: HashMap::new()
kademlia_disjoint_query_paths: false,
protocol_ids: HashSet::new()
}
}

Expand All @@ -112,12 +114,7 @@ impl DiscoveryConfig {
where
I: IntoIterator<Item = (PeerId, Multiaddr)>
{
for (peer_id, addr) in user_defined {
for kad in self.kademlias.values_mut() {
kad.add_address(&peer_id, addr.clone());
}
self.user_defined.push((peer_id, addr))
}
self.user_defined.extend(user_defined);
self
}

Expand All @@ -144,48 +141,71 @@ impl DiscoveryConfig {

/// Add discovery via Kademlia for the given protocol.
pub fn add_protocol(&mut self, id: ProtocolId) -> &mut Self {
let name = protocol_name_from_protocol_id(&id);
self.add_kademlia(id, name);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creation of Kademlia instances is moved into fn finish. Otherwise users could first call add_protocol and then use_kademlia_disjoint_query_paths with the latter not taking any effect given that the Kademlias are already created.

self
}

fn add_kademlia(&mut self, id: ProtocolId, proto_name: Vec<u8>) {
if self.kademlias.contains_key(&id) {
if self.protocol_ids.contains(&id) {
warn!(target: "sub-libp2p", "Discovery already registered for protocol {:?}", id);
return
return self;
}

let mut config = KademliaConfig::default();
config.set_protocol_name(proto_name);
// By default Kademlia attempts to insert all peers into its routing table once a dialing
// attempt succeeds. In order to control which peer is added, disable the auto-insertion and
// instead add peers manually.
config.set_kbucket_inserts(KademliaBucketInserts::Manual);
self.protocol_ids.insert(id);

let store = MemoryStore::new(self.local_peer_id.clone());
let mut kad = Kademlia::with_config(self.local_peer_id.clone(), store, config);

for (peer_id, addr) in &self.user_defined {
kad.add_address(peer_id, addr.clone());
}
self
}

self.kademlias.insert(id, kad);
/// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in the
/// presence of potentially adversarial nodes.
pub fn use_kademlia_disjoint_query_paths(&mut self, value: bool) -> &mut Self {
self.kademlia_disjoint_query_paths = value;
self
}

/// Create a `DiscoveryBehaviour` from this config.
pub fn finish(self) -> DiscoveryBehaviour {
let DiscoveryConfig {
local_peer_id,
user_defined,
allow_private_ipv4,
allow_non_globals_in_dht,
discovery_only_if_under_num,
enable_mdns,
kademlia_disjoint_query_paths,
protocol_ids,
} = self;

let kademlias = protocol_ids.into_iter()
.map(|protocol_id| {
let proto_name = protocol_name_from_protocol_id(&protocol_id);

let mut config = KademliaConfig::default();
config.set_protocol_name(proto_name);
// By default Kademlia attempts to insert all peers into its routing table once a
// dialing attempt succeeds. In order to control which peer is added, disable the
// auto-insertion and instead add peers manually.
config.set_kbucket_inserts(KademliaBucketInserts::Manual);
config.disjoint_query_paths(kademlia_disjoint_query_paths);

let store = MemoryStore::new(local_peer_id.clone());
let mut kad = Kademlia::with_config(local_peer_id.clone(), store, config);

for (peer_id, addr) in &user_defined {
kad.add_address(peer_id, addr.clone());
}

(protocol_id, kad)
})
.collect();

DiscoveryBehaviour {
user_defined: self.user_defined,
kademlias: self.kademlias,
user_defined,
kademlias,
next_kad_random_query: Delay::new(Duration::new(0, 0)),
duration_to_next_kad: Duration::from_secs(1),
pending_events: VecDeque::new(),
local_peer_id: self.local_peer_id,
local_peer_id,
num_connections: 0,
allow_private_ipv4: self.allow_private_ipv4,
discovery_only_if_under_num: self.discovery_only_if_under_num,
allow_private_ipv4,
discovery_only_if_under_num,
#[cfg(not(target_os = "unknown"))]
mdns: if self.enable_mdns {
mdns: if enable_mdns {
match Mdns::new() {
Ok(mdns) => Some(mdns).into(),
Err(err) => {
Expand All @@ -196,7 +216,7 @@ impl DiscoveryConfig {
} else {
None.into()
},
allow_non_globals_in_dht: self.allow_non_globals_in_dht,
allow_non_globals_in_dht,
known_external_addresses: LruHashSet::new(
NonZeroUsize::new(MAX_KNOWN_EXTERNAL_ADDRESSES)
.expect("value is a constant; constant is non-zero; qed.")
Expand Down
1 change: 1 addition & 0 deletions client/network/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
config.discovery_limit(u64::from(params.network_config.out_peers) + 15);
config.add_protocol(params.protocol_id.clone());
config.allow_non_globals_in_dht(params.network_config.allow_non_globals_in_dht);
config.use_kademlia_disjoint_query_paths(params.network_config.kademlia_disjoint_query_paths);

match params.network_config.transport {
TransportConfig::MemoryOnly => {
Expand Down