Base Kademlia protocol name on genesis hash and fork ID (#12545)

This commit is contained in:
Dmitrii Markin
2022-10-24 14:47:58 +03:00
committed by GitHub
parent a877b0ccec
commit d0dcf008ec
2 changed files with 104 additions and 38 deletions
+96 -36
View File
@@ -46,6 +46,7 @@
//! active mechanism that asks nodes for the addresses they are listening on. Whenever we learn //! active mechanism that asks nodes for the addresses they are listening on. Whenever we learn
//! of a node's address, you must call `add_self_reported_address`. //! of a node's address, you must call `add_self_reported_address`.
use array_bytes::bytes2hex;
use futures::prelude::*; use futures::prelude::*;
use futures_timer::Delay; use futures_timer::Delay;
use ip_network::IpNetwork; use ip_network::IpNetwork;
@@ -101,7 +102,7 @@ pub struct DiscoveryConfig {
discovery_only_if_under_num: u64, discovery_only_if_under_num: u64,
enable_mdns: bool, enable_mdns: bool,
kademlia_disjoint_query_paths: bool, kademlia_disjoint_query_paths: bool,
kademlia_protocol_id: Option<ProtocolId>, kademlia_protocols: Vec<Vec<u8>>,
} }
impl DiscoveryConfig { impl DiscoveryConfig {
@@ -116,7 +117,7 @@ impl DiscoveryConfig {
discovery_only_if_under_num: std::u64::MAX, discovery_only_if_under_num: std::u64::MAX,
enable_mdns: false, enable_mdns: false,
kademlia_disjoint_query_paths: false, kademlia_disjoint_query_paths: false,
kademlia_protocol_id: None, kademlia_protocols: Vec::new(),
} }
} }
@@ -161,9 +162,18 @@ impl DiscoveryConfig {
} }
/// Add discovery via Kademlia for the given protocol. /// Add discovery via Kademlia for the given protocol.
pub fn with_kademlia(&mut self, id: ProtocolId) -> &mut Self { ///
self.kademlia_protocol_id = Some(id); /// Currently accepts `protocol_id`. This should be removed once all the nodes
/// are upgraded to genesis hash- and fork ID-based Kademlia protocol name.
pub fn with_kademlia<Hash: AsRef<[u8]>>(
&mut self,
genesis_hash: Hash,
fork_id: Option<&str>,
protocol_id: &ProtocolId,
) -> &mut Self {
self.kademlia_protocols = Vec::new();
self.kademlia_protocols.push(kademlia_protocol_name(genesis_hash, fork_id));
self.kademlia_protocols.push(legacy_kademlia_protocol_name(protocol_id));
self self
} }
@@ -185,14 +195,12 @@ impl DiscoveryConfig {
discovery_only_if_under_num, discovery_only_if_under_num,
enable_mdns, enable_mdns,
kademlia_disjoint_query_paths, kademlia_disjoint_query_paths,
kademlia_protocol_id, kademlia_protocols,
} = self; } = self;
let kademlia = kademlia_protocol_id.map(|protocol_id| { let kademlia = if !kademlia_protocols.is_empty() {
let proto_name = protocol_name_from_protocol_id(&protocol_id);
let mut config = KademliaConfig::default(); let mut config = KademliaConfig::default();
config.set_protocol_names(std::iter::once(proto_name.into()).collect()); config.set_protocol_names(kademlia_protocols.into_iter().map(Into::into).collect());
// By default Kademlia attempts to insert all peers into its routing table once a // 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 // dialing attempt succeeds. In order to control which peer is added, disable the
// auto-insertion and instead add peers manually. // auto-insertion and instead add peers manually.
@@ -206,8 +214,10 @@ impl DiscoveryConfig {
kad.add_address(peer_id, addr.clone()); kad.add_address(peer_id, addr.clone());
} }
kad Some(kad)
}); } else {
None
};
DiscoveryBehaviour { DiscoveryBehaviour {
permanent_addresses, permanent_addresses,
@@ -866,35 +876,50 @@ impl NetworkBehaviour for DiscoveryBehaviour {
} }
} }
// NB: If this protocol name derivation is changed, check if /// Legacy (fallback) Kademlia protocol name based on `protocol_id`.
// `DiscoveryBehaviour::new_handler` is still correct. fn legacy_kademlia_protocol_name(id: &ProtocolId) -> Vec<u8> {
fn protocol_name_from_protocol_id(id: &ProtocolId) -> Vec<u8> {
let mut v = vec![b'/']; let mut v = vec![b'/'];
v.extend_from_slice(id.as_ref().as_bytes()); v.extend_from_slice(id.as_ref().as_bytes());
v.extend_from_slice(b"/kad"); v.extend_from_slice(b"/kad");
v v
} }
/// Kademlia protocol name based on `genesis_hash` and `fork_id`.
fn kademlia_protocol_name<Hash: AsRef<[u8]>>(genesis_hash: Hash, fork_id: Option<&str>) -> Vec<u8> {
let genesis_hash_hex = bytes2hex("", genesis_hash.as_ref());
if let Some(fork_id) = fork_id {
format!("/{}/{}/kad", genesis_hash_hex, fork_id).as_bytes().into()
} else {
format!("/{}/kad", genesis_hash_hex).as_bytes().into()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{protocol_name_from_protocol_id, DiscoveryConfig, DiscoveryOut}; use super::{
kademlia_protocol_name, legacy_kademlia_protocol_name, DiscoveryConfig, DiscoveryOut,
};
use futures::prelude::*; use futures::prelude::*;
use libp2p::{ use libp2p::{
core::{ core::{
transport::{MemoryTransport, Transport}, transport::{MemoryTransport, Transport},
upgrade, upgrade,
}, },
identity::Keypair, identity::{ed25519, Keypair},
noise, noise,
swarm::{Swarm, SwarmEvent}, swarm::{Swarm, SwarmEvent},
yamux, Multiaddr, PeerId, yamux, Multiaddr,
}; };
use sc_network_common::config::ProtocolId; use sc_network_common::config::ProtocolId;
use sp_core::hash::H256;
use std::{collections::HashSet, task::Poll}; use std::{collections::HashSet, task::Poll};
#[test] #[test]
fn discovery_working() { fn discovery_working() {
let mut first_swarm_peer_id_and_addr = None; let mut first_swarm_peer_id_and_addr = None;
let genesis_hash = H256::from_low_u64_be(1);
let fork_id = Some("test-fork-id");
let protocol_id = ProtocolId::from("dot"); let protocol_id = ProtocolId::from("dot");
// Build swarms whose behaviour is `DiscoveryBehaviour`, each aware of // Build swarms whose behaviour is `DiscoveryBehaviour`, each aware of
@@ -919,7 +944,7 @@ mod tests {
.allow_private_ipv4(true) .allow_private_ipv4(true)
.allow_non_globals_in_dht(true) .allow_non_globals_in_dht(true)
.discovery_limit(50) .discovery_limit(50)
.with_kademlia(protocol_id.clone()); .with_kademlia(genesis_hash, fork_id, &protocol_id);
config.finish() config.finish()
}; };
@@ -972,12 +997,19 @@ mod tests {
} }
}) })
.unwrap(); .unwrap();
// Test both genesis hash-based and legacy
// protocol names.
let protocol_name = if swarm_n % 2 == 0 {
kademlia_protocol_name(genesis_hash, fork_id)
} else {
legacy_kademlia_protocol_name(&protocol_id)
};
swarms[swarm_n] swarms[swarm_n]
.0 .0
.behaviour_mut() .behaviour_mut()
.add_self_reported_address( .add_self_reported_address(
&other, &other,
&[protocol_name_from_protocol_id(&protocol_id)], &[protocol_name],
addr, addr,
); );
@@ -1012,6 +1044,8 @@ mod tests {
#[test] #[test]
fn discovery_ignores_peers_with_unknown_protocols() { fn discovery_ignores_peers_with_unknown_protocols() {
let supported_genesis_hash = H256::from_low_u64_be(1);
let unsupported_genesis_hash = H256::from_low_u64_be(2);
let supported_protocol_id = ProtocolId::from("a"); let supported_protocol_id = ProtocolId::from("a");
let unsupported_protocol_id = ProtocolId::from("b"); let unsupported_protocol_id = ProtocolId::from("b");
@@ -1022,19 +1056,34 @@ mod tests {
.allow_private_ipv4(true) .allow_private_ipv4(true)
.allow_non_globals_in_dht(true) .allow_non_globals_in_dht(true)
.discovery_limit(50) .discovery_limit(50)
.with_kademlia(supported_protocol_id.clone()); .with_kademlia(supported_genesis_hash, None, &supported_protocol_id);
config.finish() config.finish()
}; };
let remote_peer_id = PeerId::random(); let predictable_peer_id = |bytes: &[u8; 32]| {
let remote_addr: Multiaddr = format!("/memory/{}", rand::random::<u64>()).parse().unwrap(); Keypair::Ed25519(ed25519::Keypair::from(
ed25519::SecretKey::from_bytes(bytes.to_owned()).unwrap(),
))
.public()
.to_peer_id()
};
// Add remote peer with unsupported protocol. let remote_peer_id = predictable_peer_id(b"00000000000000000000000000000001");
let remote_addr: Multiaddr = "/memory/1".parse().unwrap();
let another_peer_id = predictable_peer_id(b"00000000000000000000000000000002");
let another_addr: Multiaddr = "/memory/2".parse().unwrap();
// Try adding remote peers with unsupported protocols.
discovery.add_self_reported_address( discovery.add_self_reported_address(
&remote_peer_id, &remote_peer_id,
&[protocol_name_from_protocol_id(&unsupported_protocol_id)], &[kademlia_protocol_name(unsupported_genesis_hash, None)],
remote_addr.clone(), remote_addr.clone(),
); );
discovery.add_self_reported_address(
&another_peer_id,
&[legacy_kademlia_protocol_name(&unsupported_protocol_id)],
another_addr.clone(),
);
{ {
let kademlia = discovery.kademlia.as_mut().unwrap(); let kademlia = discovery.kademlia.as_mut().unwrap();
@@ -1045,23 +1094,34 @@ mod tests {
.is_empty(), .is_empty(),
"Expect peer with unsupported protocol not to be added." "Expect peer with unsupported protocol not to be added."
); );
assert!(
kademlia
.kbucket(another_peer_id)
.expect("Remote peer id not to be equal to local peer id.")
.is_empty(),
"Expect peer with unsupported protocol not to be added."
);
} }
// Add remote peer with supported protocol. // Add remote peers with supported protocols.
discovery.add_self_reported_address( discovery.add_self_reported_address(
&remote_peer_id, &remote_peer_id,
&[protocol_name_from_protocol_id(&supported_protocol_id)], &[kademlia_protocol_name(supported_genesis_hash, None)],
remote_addr.clone(), remote_addr.clone(),
); );
discovery.add_self_reported_address(
let kademlia = discovery.kademlia.as_mut().unwrap(); &another_peer_id,
assert_eq!( &[legacy_kademlia_protocol_name(&supported_protocol_id)],
1, another_addr.clone(),
kademlia
.kbucket(remote_peer_id)
.expect("Remote peer id not to be equal to local peer id.")
.num_entries(),
"Expect peer with supported protocol to be added."
); );
{
let kademlia = discovery.kademlia.as_mut().unwrap();
assert_eq!(
2,
kademlia.kbuckets().fold(0, |acc, bucket| acc + bucket.num_entries()),
"Expect peers with supported protocol to be added."
);
}
} }
} }
+8 -2
View File
@@ -76,7 +76,7 @@ use sc_network_common::{
use sc_peerset::PeersetHandle; use sc_peerset::PeersetHandle;
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
use sp_blockchain::HeaderBackend; use sp_blockchain::HeaderBackend;
use sp_runtime::traits::{Block as BlockT, NumberFor}; use sp_runtime::traits::{Block as BlockT, NumberFor, Zero};
use std::{ use std::{
cmp, cmp,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@@ -282,7 +282,13 @@ where
config.discovery_limit( config.discovery_limit(
u64::from(params.network_config.default_peers_set.out_peers) + 15, u64::from(params.network_config.default_peers_set.out_peers) + 15,
); );
config.with_kademlia(params.protocol_id.clone()); let genesis_hash = params
.chain
.hash(Zero::zero())
.ok()
.flatten()
.expect("Genesis block exists; qed");
config.with_kademlia(genesis_hash, params.fork_id.as_deref(), &params.protocol_id);
config.with_dht_random_walk(params.network_config.enable_dht_random_walk); config.with_dht_random_walk(params.network_config.enable_dht_random_walk);
config.allow_non_globals_in_dht(params.network_config.allow_non_globals_in_dht); config.allow_non_globals_in_dht(params.network_config.allow_non_globals_in_dht);
config.use_kademlia_disjoint_query_paths( config.use_kademlia_disjoint_query_paths(