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

Commit

Permalink
Node Discovery v4 ENR Extension (EIP-868)
Browse files Browse the repository at this point in the history
  • Loading branch information
vorot93 committed Mar 24, 2020
1 parent 836d47a commit d46c9b3
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 12 deletions.
57 changes: 57 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions util/network-devp2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ edition = "2018"
[dependencies]
ansi_term = "0.11"
bytes = "0.4"
derive_more = "0.99"
enr = { version = "0.1.0-alpha.4", default-features = false, features = ["rust-secp256k1"] }
ethcore-io = { path = "../io", features = ["mio"] }
ethereum-types = "0.8.0"
igd = "0.10.0"
Expand All @@ -28,6 +30,7 @@ parity-snappy = "0.1"
parking_lot = "0.10.0"
rand = "0.7"
rlp = "0.4.0"
secp256k1 = "0.17"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
slab = "0.2"
Expand Down
68 changes: 58 additions & 10 deletions util/network-devp2p/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use parity_crypto::publickey::{KeyPair, recover, Secret, sign};
use network::Error;
use network::IpFilter;

use crate::node_record::*;
use crate::node_table::*;
use crate::PROTOCOL_VERSION;

Expand All @@ -45,6 +46,8 @@ const PACKET_PING: u8 = 1;
const PACKET_PONG: u8 = 2;
const PACKET_FIND_NODE: u8 = 3;
const PACKET_NEIGHBOURS: u8 = 4;
const PACKET_ENR_REQUEST: u8 = 5;
const PACKET_ENR_RESPONSE: u8 = 6;

const PING_TIMEOUT: Duration = Duration::from_millis(500);
const FIND_NODE_TIMEOUT: Duration = Duration::from_secs(2);
Expand Down Expand Up @@ -155,6 +158,7 @@ pub struct Discovery {
id_hash: H256,
secret: Secret,
public_endpoint: NodeEndpoint,
enr: Enr,
discovery_initiated: bool,
discovery_round: Option<u16>,
discovery_id: NodeId,
Expand All @@ -180,11 +184,12 @@ pub struct TableUpdates {
}

impl Discovery {
pub fn new(key: &KeyPair, public: NodeEndpoint, ip_filter: IpFilter) -> Discovery {
pub fn new(key: &KeyPair, public: NodeEndpoint, enr: Enr, ip_filter: IpFilter) -> Discovery {
Discovery {
id: *key.public(),
id_hash: keccak(key.public()),
secret: key.secret().clone(),
enr,
public_endpoint: public,
discovery_initiated: false,
discovery_round: None,
Expand Down Expand Up @@ -372,6 +377,7 @@ impl Discovery {
self.public_endpoint.to_rlp_list(&mut rlp);
node.endpoint.to_rlp_list(&mut rlp);
append_expiration(&mut rlp);
rlp.append(&self.enr.seq());
let hash = self.send_packet(PACKET_PING, node.endpoint.udp_address(), rlp.drain())?;

self.in_flight_pings.insert(node.id, PingRequest {
Expand Down Expand Up @@ -484,6 +490,11 @@ impl Discovery {
PACKET_PONG => self.on_pong(&rlp, node_id, from),
PACKET_FIND_NODE => self.on_find_node(&rlp, node_id, from),
PACKET_NEIGHBOURS => self.on_neighbours(&rlp, node_id, from),
PACKET_ENR_REQUEST => self.on_enr_request(&rlp, node_id, from, hash_signed.as_bytes()),
PACKET_ENR_RESPONSE => {
debug!(target: "discovery", "ENR response handling is not implemented");
Ok(None)
}
_ => {
debug!(target: "discovery", "Unknown UDP packet: {}", packet_id);
Ok(None)
Expand Down Expand Up @@ -522,7 +533,8 @@ impl Discovery {
let ping_to = NodeEndpoint::from_rlp(&rlp.at(2)?)?;
let timestamp: u64 = rlp.val_at(3)?;
self.check_timestamp(timestamp)?;
let mut response = RlpStream::new_list(3);
// let enr_seq = rlp.val_at::<u64>(4).ok();
let mut response = RlpStream::new_list(4);
let pong_to = NodeEndpoint {
address: from,
udp_port: ping_from.udp_port
Expand All @@ -537,6 +549,7 @@ impl Discovery {

response.append(&echo_hash);
append_expiration(&mut response);
response.append(&self.enr.seq());
self.send_packet(PACKET_PONG, from, response.drain())?;

let entry = NodeEntry { id: node_id, endpoint: pong_to };
Expand All @@ -556,6 +569,7 @@ impl Discovery {
let echo_hash: H256 = rlp.val_at(1)?;
let timestamp: u64 = rlp.val_at(2)?;
self.check_timestamp(timestamp)?;
// let enr_seq = rlp.val_at::<u64>(3).ok();

let expected_node = match self.in_flight_pings.entry(node_id) {
Entry::Occupied(entry) if entry.get().echo_hash != echo_hash => {
Expand Down Expand Up @@ -733,6 +747,32 @@ impl Discovery {
Ok(None)
}

fn on_enr_request(&mut self, rlp: &Rlp, node_id: NodeId, from: SocketAddr, request_hash: &[u8]) -> Result<Option<TableUpdates>, Error> {
let timestamp = rlp.val_at::<u64>(0)?;
self.check_timestamp(timestamp)?;

let node = NodeEntry {
id: node_id.clone(),
endpoint: NodeEndpoint {
address: from,
udp_port: from.port()
}
};

match self.check_validity(&node) {
NodeValidity::Ourselves => (), // It makes no sense to respond to the discovery request from ourselves
NodeValidity::ValidNode(_) => {
let mut response = RlpStream::new_list(2);
response.append(&request_hash);
response.append(&self.enr);
self.send_packet(PACKET_ENR_RESPONSE, from, response.drain())?;
}
// Make sure the request source is actually there and responds to pings before actually responding
invalidity_reason => self.try_ping(node, PingReason::FromDiscoveryRequest(node_id, invalidity_reason))
}
Ok(None)
}

fn check_expired(&mut self, time: Instant) {
let mut nodes_to_expire = Vec::new();
self.in_flight_pings.retain(|node_id, ping_request| {
Expand Down Expand Up @@ -895,7 +935,8 @@ mod tests {
fn ping_queue() {
let key = Random.generate();
let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 };
let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default());
let enr = EnrManager::new(key.secret().clone(), 0).unwrap().with_node_endpoint(&ep).into_enr();
let mut discovery = Discovery::new(&key, ep.clone(), enr , IpFilter::default());

for i in 1..(MAX_NODES_PING+1) {
discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() });
Expand All @@ -919,7 +960,8 @@ mod tests {
address: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 41000 + i),
udp_port: 41000 + i,
};
Discovery::new(&key, ep, IpFilter::default())
let enr = EnrManager::new(key.secret().clone(), 0).unwrap().with_node_endpoint(&ep).into_enr();
Discovery::new(&key, ep, enr, IpFilter::default())
})
.collect::<Vec<_>>();

Expand Down Expand Up @@ -966,7 +1008,8 @@ mod tests {
fn removes_expired() {
let key = Random.generate();
let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40446").unwrap(), udp_port: 40447 };
let discovery = Discovery::new(&key, ep.clone(), IpFilter::default());
let enr = EnrManager::new(key.secret().clone(), 0).unwrap().with_node_endpoint(&ep).into_enr();
let discovery = Discovery::new(&key, ep.clone(), enr, IpFilter::default());

let mut discovery = Discovery { request_backoff: &[], ..discovery };

Expand Down Expand Up @@ -1058,7 +1101,8 @@ mod tests {

let key = Random.generate();
let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 };
let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default());
let enr = EnrManager::new(key.secret().clone(), 0).unwrap().with_node_endpoint(&ep).into_enr();
let mut discovery = Discovery::new(&key, ep.clone(), enr, IpFilter::default());

for _ in 0..(16 + 10) {
let entry = BucketEntry::new(NodeEntry { id: NodeId::zero(), endpoint: ep.clone() });
Expand Down Expand Up @@ -1115,7 +1159,8 @@ mod tests {
let key = Secret::from_str(secret_hex)
.and_then(|secret| KeyPair::from_secret(secret))
.unwrap();
let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default());
let enr = EnrManager::new(key.secret().clone(), 0).unwrap().with_node_endpoint(&ep).into_enr();
let mut discovery = Discovery::new(&key, ep.clone(), enr, IpFilter::default());

discovery.init_node_list(node_entries.clone());

Expand Down Expand Up @@ -1160,7 +1205,8 @@ mod tests {
fn packets() {
let key = Random.generate();
let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40449").unwrap(), udp_port: 40449 };
let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default());
let enr = EnrManager::new(key.secret().clone(), 0).unwrap().with_node_endpoint(&ep).into_enr();
let mut discovery = Discovery::new(&key, ep.clone(), enr, IpFilter::default());
discovery.check_timestamps = false;
let from = SocketAddr::from_str("99.99.99.99:40445").unwrap();

Expand Down Expand Up @@ -1229,8 +1275,10 @@ mod tests {
let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40344").unwrap(), udp_port: 40344 };
let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40345").unwrap(), udp_port: 40345 };
let ep3 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40346").unwrap(), udp_port: 40345 };
let mut discovery1 = Discovery::new(&key1, ep1.clone(), IpFilter::default());
let mut discovery2 = Discovery::new(&key2, ep2.clone(), IpFilter::default());
let enr1 = EnrManager::new(key1.secret().clone(), 0).unwrap().with_node_endpoint(&ep1).into_enr();
let enr2 = EnrManager::new(key2.secret().clone(), 0).unwrap().with_node_endpoint(&ep2).into_enr();
let mut discovery1 = Discovery::new(&key1, ep1.clone(), enr1, IpFilter::default());
let mut discovery2 = Discovery::new(&key2, ep2.clone(), enr2, IpFilter::default());

discovery1.ping(&NodeEntry { id: discovery2.id, endpoint: ep2.clone() }, PingReason::Default).unwrap();
let ping_data = discovery1.dequeue_send().unwrap();
Expand Down
23 changes: 21 additions & 2 deletions util/network-devp2p/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use crate::{
discovery::{Discovery, MAX_DATAGRAM_SIZE, NodeEntry, TableUpdates},
disk::{save, load},
ip_utils::{map_external_address, select_public_address},
node_record::*,
node_table::*,
PROTOCOL_VERSION,
session::{Session, SessionData}
Expand Down Expand Up @@ -217,6 +218,8 @@ impl<'s> NetworkContextTrait for NetworkContext<'s> {
pub struct HostInfo {
/// Our private and public keys.
keys: KeyPair,
/// Node record.
enr: EnrManager,
/// Current network configuration
config: NetworkConfiguration,
/// Connection nonce.
Expand Down Expand Up @@ -284,11 +287,13 @@ impl Host {
Some(addr) => addr,
};

let mut key_created = false;
let keys = if let Some(ref secret) = config.use_secret {
KeyPair::from_secret(secret.clone())?
} else {
config.config_path.clone().and_then(|ref p| load(Path::new(&p)))
.map_or_else(|| {
key_created = true;
let key = Random.generate();
if let Some(path) = config.config_path.clone() {
save(Path::new(&path), key.secret());
Expand All @@ -297,6 +302,15 @@ impl Host {
},
|s| KeyPair::from_secret(s).expect("Error creating node secret key"))
};
let mut enr = None;
if !key_created {
if let Some(path) = &config.config_path {
if let Some(data) = load(Path::new(&path)) {
enr = EnrManager::load(keys.secret().clone(), data);
}
}
}
let enr = enr.unwrap_or_else(|| EnrManager::new(keys.secret().clone(), 0).expect("Key is always valid; qed"));
let path = config.net_config_path.clone();
// Setup the server socket
let tcp_listener = TcpListener::bind(&listen_address)?;
Expand All @@ -312,6 +326,7 @@ impl Host {
let mut host = Host {
info: RwLock::new(HostInfo {
keys,
enr,
config,
nonce: H256::random(),
protocol_version: PROTOCOL_VERSION,
Expand Down Expand Up @@ -474,7 +489,11 @@ impl Host {
Some(addr) => NodeEndpoint { address: addr, udp_port: local_endpoint.udp_port }
};

self.info.write().public_endpoint = Some(public_endpoint.clone());
{
let mut info = self.info.write();
info.public_endpoint = Some(public_endpoint.clone());
info.enr.set_node_endpoint(&public_endpoint);
}

if let Some(url) = self.external_url() {
io.message(NetworkIoMessage::NetworkStarted(url)).unwrap_or_else(|e| warn!("Error sending IO notification: {:?}", e));
Expand All @@ -484,7 +503,7 @@ impl Host {
let discovery = {
let info = self.info.read();
if info.config.discovery_enabled && info.config.non_reserved_mode == NonReservedPeerMode::Accept {
Some(Discovery::new(&info.keys, public_endpoint, allow_ips))
Some(Discovery::new(&info.keys, public_endpoint, info.enr.as_enr().clone(), allow_ips))
} else { None }
};

Expand Down
Loading

0 comments on commit d46c9b3

Please sign in to comment.