diff --git a/util/network-devp2p/src/discovery.rs b/util/network-devp2p/src/discovery.rs
index 987eded7e92..19a7f0b76b6 100644
--- a/util/network-devp2p/src/discovery.rs
+++ b/util/network-devp2p/src/discovery.rs
@@ -14,9 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Open Ethereum. If not, see .
+//! Ethereum Node Discovery Protocol V4
+
use std::collections::{HashMap, HashSet, VecDeque};
use std::collections::hash_map::Entry;
-use std::default::Default;
+use std::convert::{TryFrom, TryInto};
use std::net::SocketAddr;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@@ -24,32 +26,49 @@ use ethereum_types::{H256, H520};
use keccak_hash::keccak;
use log::{debug, trace, warn};
use lru_cache::LruCache;
+use network::{Error, IpFilter};
use parity_bytes::Bytes;
+use parity_crypto::publickey::{KeyPair, recover, Secret, Signature, sign};
use rlp::{Rlp, RlpStream};
-use parity_crypto::publickey::{KeyPair, recover, Secret, sign};
-use network::Error;
-use network::IpFilter;
-
-use crate::node_table::*;
+use crate::node_table::{NodeEndpoint, NodeId};
use crate::PROTOCOL_VERSION;
-const ADDRESS_BYTES_SIZE: usize = 32; // Size of address type in bytes.
-const ADDRESS_BITS: usize = 8 * ADDRESS_BYTES_SIZE; // Denoted by n in [Kademlia].
-const DISCOVERY_MAX_STEPS: u16 = 8; // Max iterations of discovery. (discover)
-const BUCKET_SIZE: usize = 16; // Denoted by k in [Kademlia]. Number of nodes stored in each bucket.
-const ALPHA: usize = 3; // Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.
+/// Maximum Node discovery packet size
pub const MAX_DATAGRAM_SIZE: usize = 1280;
+/// Minimum node discovery packet size
+// TODO(niklasad1): why 4?
+const MIN_DATAGRAM_SIZE: usize = HEADER_SIZE + 4;
+
+/// Size of the `hash` and `signature` in bytes (denoted MAC)
+const HEADER_MAC_SIZE: usize = ADDRESS_BYTES_SIZE + SIGNATURE_BYTES_LEN;
+/// Size of the Node discovery wire protocol header
+const HEADER_SIZE: usize = HEADER_MAC_SIZE + PACKET_TYPE_BYTES_LEN;
+/// Size of address in bytes.
+const ADDRESS_BYTES_SIZE: usize = 32;
+/// Denoted by n in [Kademlia].
+const ADDRESS_BITS: usize = 8 * ADDRESS_BYTES_SIZE;
+/// Max iterations of discovery. (discover)
+const DISCOVERY_MAX_STEPS: u16 = 8;
+/// Denoted by k in [Kademlia]. Number of nodes stored in each bucket.
+const BUCKET_SIZE: usize = 16;
+/// Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.
+const ALPHA: usize = 3;
const PACKET_PING: u8 = 1;
const PACKET_PONG: u8 = 2;
const PACKET_FIND_NODE: u8 = 3;
const PACKET_NEIGHBOURS: u8 = 4;
+/// The length of the packet type in bytes
+const PACKET_TYPE_BYTES_LEN: usize = 1;
+/// Length of `Ethereum signature` in number of bytes
+const SIGNATURE_BYTES_LEN: usize = 65;
const PING_TIMEOUT: Duration = Duration::from_millis(500);
const FIND_NODE_TIMEOUT: Duration = Duration::from_secs(2);
const EXPIRY_TIME: Duration = Duration::from_secs(20);
-const MAX_NODES_PING: usize = 32; // Max nodes to add/ping at once
+/// Max nodes to add/ping at once
+const MAX_NODES_PING: usize = 32;
const REQUEST_BACKOFF: [Duration; 4] = [
Duration::from_secs(1),
Duration::from_secs(4),
@@ -57,10 +76,34 @@ const REQUEST_BACKOFF: [Duration; 4] = [
Duration::from_secs(64)
];
-const NODE_LAST_SEEN_TIMEOUT: Duration = Duration::from_secs(24*60*60);
-
+const NODE_LAST_SEEN_TIMEOUT: Duration = Duration::from_secs(24 * 60 * 60);
const OBSERVED_NODES_MAX_SIZE: usize = 10_000;
+
+/// Node discovery packet kinds
+// TODO: Add support for `Node Discovery v4 ENR Extension`
+#[derive(Debug)]
+enum PacketType {
+ Ping = 1,
+ Pong = 2,
+ FindNode = 3,
+ Neighbours = 4,
+}
+
+impl TryFrom for PacketType {
+ type Error = Error;
+
+ fn try_from(kind: u8) -> Result {
+ match kind {
+ PACKET_PING => Ok(Self::Ping),
+ PACKET_PONG => Ok(Self::Pong),
+ PACKET_FIND_NODE => Ok(Self::FindNode),
+ PACKET_NEIGHBOURS => Ok(Self::Neighbours),
+ _ => Err(Error::BadProtocol),
+ }
+ }
+}
+
#[derive(Clone, Debug)]
pub struct NodeEntry {
pub id: NodeId,
@@ -372,7 +415,7 @@ impl Discovery {
self.public_endpoint.to_rlp_list(&mut rlp);
node.endpoint.to_rlp_list(&mut rlp);
append_expiration(&mut rlp);
- let hash = self.send_packet(PACKET_PING, node.endpoint.udp_address(), rlp.drain())?;
+ let hash = self.send_packet(PacketType::Ping, node.endpoint.udp_address(), rlp.drain())?;
self.in_flight_pings.insert(node.id, PingRequest {
sent_at: Instant::now(),
@@ -389,7 +432,7 @@ impl Discovery {
let mut rlp = RlpStream::new_list(2);
rlp.append(target);
append_expiration(&mut rlp);
- self.send_packet(PACKET_FIND_NODE, node.endpoint.udp_address(), rlp.drain())?;
+ self.send_packet(PacketType::FindNode, node.endpoint.udp_address(), rlp.drain())?;
self.in_flight_find_nodes.insert(node.id, FindNodeRequest {
sent_at: Instant::now(),
@@ -401,9 +444,8 @@ impl Discovery {
Ok(())
}
- fn send_packet(&mut self, packet_id: u8, address: SocketAddr, payload: Bytes) -> Result {
- let packet = assemble_packet(packet_id, payload, &self.secret)?;
- let hash = H256::from_slice(&packet[0..32]);
+ fn send_packet(&mut self, packet_id: PacketType, address: SocketAddr, payload: Bytes) -> Result {
+ let (packet, hash) = assemble_packet(packet_id, payload, &self.secret)?;
self.send_to(packet, address);
Ok(hash)
}
@@ -464,30 +506,13 @@ impl Discovery {
}
pub fn on_packet(&mut self, packet: &[u8], from: SocketAddr) -> Result