Skip to content

Commit

Permalink
Upgrade to discovery v5.1 (#39)
Browse files Browse the repository at this point in the history
* Beginnings of discv5 wire protocol

* Handle packet encoding/decoding and encryption

* Temp commit

* First early draft of basic 5.1 packet handling

* Streamline FindNodes requests

* Initial testing

* Correct all tests

* Handle many findnode distances

* Version bump

* Add node-id filtering

* Update example

* Update the RPC messages

* Include dst-node-id in the signature

* Small packet updates

* Small packet updates

* Temp commit

* Beginning of crypto library shift

* Update cryptography to use k256

* Correct crypto tests

* Further corrections to packet

* Update all tests

* Shift crates under libp2p feature flag

* Add TALK functionality

* Improve distance handling

* Correct RLP encoding

* Appease clippy

* Update simple server

* Respond to all pings

* Update log to tracing

* Improve default timeouts for queries
  • Loading branch information
AgeManning authored Oct 19, 2020
1 parent 015135e commit 275c78e
Show file tree
Hide file tree
Showing 34 changed files with 2,750 additions and 1,997 deletions.
531 changes: 410 additions & 121 deletions Cargo.lock

Large diffs are not rendered by default.

39 changes: 20 additions & 19 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "discv5"
authors = ["Age Manning <[email protected]>"]
edition = "2018"
version = "0.1.0-alpha.13"
version = "0.1.0-beta.1"
description = "Implementation of the p2p discv5 discovery protocol"
license = "MIT"
repository = "https://github.com/sigp/discv5"
Expand All @@ -15,35 +15,36 @@ exclude = [
]

[dependencies]
enr = { version = "0.3.0", features = ["libsecp256k1", "ed25519"] }
enr = { version = "0.4.0", features = ["k256", "ed25519"] }
tokio = { version = "0.2.22", features = ["net", "stream", "time", "sync"] }
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
libp2p-core = { version = "0.22.1", optional = true }
multihash = { version = "0.11.2", optional = true }
libsecp256k1 = "0.3.5"
futures = "0.3.5"
uint = { version = "0.8.3", default-features = false }
log = "0.4.11"
rlp = "0.4.5"
sha2 = "0.8.1"
hkdf = "0.8.0"
multihash = { version = "0.11.4", optional = true }
futures = "0.3.6"
uint = { version = "0.8.5", default-features = false }
rlp = "0.4.6"
sha2 = "0.9.1"
hkdf = "0.9.0"
hex = "0.4.2"
fnv = "1.0.7"
arrayvec = "0.5.1"
digest = "0.8.1"
digest = "0.9.0"
rand = "0.7.3"
smallvec = "1.4.1"
smallvec = "1.4.2"
parking_lot = "0.11.0"
lru_time_cache = "0.10.0"
lru_time_cache = "0.11.1"
lazy_static = "1.4.0"
aes-gcm = "0.6.0"
socket2 = "0.3.12"
aes-gcm = "0.7.0"
socket2 = "0.3.15"
aes-ctr = "0.5.0"
k256 = { version = "0.5.9", features = ["zeroize", "ecdh", "sha2"] }
tracing = "0.1.21"
tracing-subscriber = "0.2.13"

[dev-dependencies]
quickcheck = "0.9.2"
env_logger = "0.7.1"
hex-literal = "0.3.0"
simple_logger = "1.6.0"
hex-literal = "0.3.1"
simple_logger = "1.11.0"
tokio = { version = "0.2.22", features = ["time", "rt-threaded", "macros"] }
rand_xorshift = "0.2.0"
rand_core = "0.5.1"
Expand Down
7 changes: 6 additions & 1 deletion examples/custom_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ use std::net::SocketAddr;

fn main() {
// allows detailed logging with the RUST_LOG env variable
env_logger::init();
let filter_layer = tracing_subscriber::EnvFilter::try_from_default_env()
.or_else(|_| tracing_subscriber::EnvFilter::try_new("info"))
.unwrap();
let _ = tracing_subscriber::fmt()
.with_env_filter(filter_layer)
.try_init();

// listening address and port
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();
Expand Down
34 changes: 19 additions & 15 deletions examples/find_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! participating node in the command line. The nodes should discover each other over a period of
//! time. (It is probabilistic that nodes to find each other on any given query).
//!
//! A single instance listening on a UDP socket `127.0.0.1:9000` (with an ENR that has an empty IP
//! A single instance listening on a UDP socket `0.0.0.0:9000` (with an ENR that has an empty IP
//! and UDP port) can be created via:
//!
//! ```
Expand All @@ -25,8 +25,9 @@
//! ```
//! sh cargo run --example find_nodes -- 127.0.0.1 9001 <GENERATE_KEY> <BASE64_ENR>
//! ```
//!
//! where `<BASE64_ENR>` is the base64 ENR given from executing the first node with an IP and port
//! Here `127.0.0.1` represents the external IP address that others may connect to this node on. The
//! `9001` represents the external port and the port to listen on. The `<BASE64_ENR>` is the base64
//! ENR given from executing the first node with an IP and port
//! given in the CLI.
//! `<GENERATE_KEY>` is a boolean (`true` or `false`) specifying if a new key should be generated.
//! These steps can be repeated to add further nodes to the test network.
Expand All @@ -44,16 +45,17 @@ use std::{

#[tokio::main]
async fn main() {
env_logger::init();
let filter_layer = tracing_subscriber::EnvFilter::try_from_default_env()
.or_else(|_| tracing_subscriber::EnvFilter::try_new("info"))
.unwrap();
let _ = tracing_subscriber::fmt()
.with_env_filter(filter_layer)
.try_init();

// if there is an address specified use it
let address = {
if let Some(address) = std::env::args().nth(1) {
address.parse::<Ipv4Addr>().unwrap()
} else {
Ipv4Addr::new(127, 0, 0, 1)
}
};
let address = std::env::args()
.nth(1)
.map(|addr| addr.parse::<Ipv4Addr>().unwrap());

let port = {
if let Some(udp_port) = std::env::args().nth(2) {
Expand All @@ -66,7 +68,7 @@ async fn main() {
// A fixed key for testing
let raw_key =
hex::decode("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291").unwrap();
let secret_key = secp256k1::SecretKey::parse_slice(&raw_key).unwrap();
let secret_key = k256::ecdsa::SigningKey::new(&raw_key).unwrap();
let mut enr_key = CombinedKey::from(secret_key);

// use a random key if specified
Expand All @@ -80,8 +82,8 @@ async fn main() {
let enr = {
let mut builder = enr::EnrBuilder::new("v4");
// if an IP was specified, use it
if std::env::args().nth(1).is_some() {
builder.ip(address.into());
if let Some(external_address) = address {
builder.ip(external_address.into());
}
// if a port was specified, use it
if std::env::args().nth(2).is_some() {
Expand All @@ -100,7 +102,9 @@ async fn main() {
}

// default configuration with packet filtering
let config = Discv5ConfigBuilder::new().enable_packet_filter().build();
// let config = Discv5ConfigBuilder::new().enable_packet_filter().build();
// default configuration without packet filtering
let config = Discv5ConfigBuilder::new().build();

// the address to listen on
let socket_addr = SocketAddr::new("0.0.0.0".parse().expect("valid ip"), port);
Expand Down
12 changes: 7 additions & 5 deletions examples/request_enr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ fn main() {}
#[cfg(feature = "libp2p")]
#[tokio::main]
async fn main() {
env_logger::init();
let filter_layer = tracing_subscriber::EnvFilter::try_from_default_env()
.or_else(|_| tracing_subscriber::EnvFilter::try_new("info"))
.unwrap();
let _ = tracing_subscriber::fmt()
.with_env_filter(filter_layer)
.try_init();

// listening address and port
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();
Expand All @@ -48,14 +53,11 @@ async fn main() {

// search for the ENR
match discv5.request_enr(multiaddr).await {
Ok(Some(enr)) => {
Ok(enr) => {
println!("ENR Found:");
println!("Base64:{}", enr.to_base64());
println!("{}", enr);
}
Ok(None) => {
println!("No ENR response");
}
Err(e) => {
println!("Error:{:?}", e);
}
Expand Down
49 changes: 44 additions & 5 deletions examples/simple_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,62 @@
//!
//! To run this example simply run:
//! ```
//! $ cargo run --example simple_server <BASE64ENR>
//! $ cargo run --example simple_server -- <ENR-IP> <ENR-PORT> <BASE64ENR>
//! ```
use discv5::{enr, enr::CombinedKey, Discv5, Discv5Config, Discv5Event};
use std::net::SocketAddr;
use std::net::{Ipv4Addr, SocketAddr};

#[tokio::main]
async fn main() {
// allows detailed logging with the RUST_LOG env variable
env_logger::init();
let filter_layer = tracing_subscriber::EnvFilter::try_from_default_env()
.or_else(|_| tracing_subscriber::EnvFilter::try_new("info"))
.unwrap();
let _ = tracing_subscriber::fmt()
.with_env_filter(filter_layer)
.try_init();

// if there is an address specified use it
let address = std::env::args()
.nth(1)
.map(|addr| addr.parse::<Ipv4Addr>().unwrap());

let port = {
if let Some(udp_port) = std::env::args().nth(2) {
u16::from_str_radix(&udp_port, 10).unwrap()
} else {
9000
}
};

// listening address and port
let listen_addr = "0.0.0.0:9000".parse::<SocketAddr>().unwrap();

let enr_key = CombinedKey::generate_secp256k1();

// construct a local ENR
let enr = enr::EnrBuilder::new("v4").build(&enr_key).unwrap();
let enr = {
let mut builder = enr::EnrBuilder::new("v4");
// if an IP was specified, use it
if let Some(external_address) = address {
builder.ip(external_address.into());
}
// if a port was specified, use it
if std::env::args().nth(2).is_some() {
builder.udp(port);
}
builder.build(&enr_key).unwrap()
};

// if the ENR is useful print it
println!("Node Id: {}", enr.node_id());
if enr.udp_socket().is_some() {
println!("Base64 ENR: {}", enr.to_base64());
println!("IP: {}, UDP_PORT:{}", enr.ip().unwrap(), enr.udp().unwrap());
} else {
println!("ENR is not printed as no IP:PORT was specified");
}

// default configuration
let config = Discv5Config::default();
Expand All @@ -32,7 +71,7 @@ async fn main() {
let mut discv5 = Discv5::new(enr, enr_key, config).unwrap();

// if we know of another peer's ENR, add it known peers
if let Some(base64_enr) = std::env::args().nth(1) {
if let Some(base64_enr) = std::env::args().nth(3) {
match base64_enr.parse::<enr::Enr<enr::CombinedKey>>() {
Ok(enr) => {
println!(
Expand Down
27 changes: 25 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct Discv5Config {
/// Whether to enable the incoming packet filter. Default: false.
pub enable_packet_filter: bool,

/// The request timeout for each UDP request. Default: 4 seconds.
/// The request timeout for each UDP request. Default: 2 seconds.
pub request_timeout: Duration,

/// The timeout after which a `QueryPeer` in an ongoing query is marked unresponsive.
Expand All @@ -31,6 +31,9 @@ pub struct Discv5Config {
/// Updates the local ENR IP and port based on PONG responses from peers. Default: true.
pub enr_update: bool,

/// The maximum number of nodes we return to a find nodes request. The default is 16.
pub max_nodes_response: usize,

/// The minimum number of peer's who agree on an external IP port before updating the
/// local ENR. Default: 10.
pub enr_peer_update_min: usize,
Expand All @@ -46,6 +49,11 @@ pub struct Discv5Config {
/// excluded if they do not pass this filter. The default is to accept all nodes.
pub table_filter: fn(&Enr) -> bool,

/// The callback for handling TALKREQ requests. The input to this callback is the protocol and
/// the output is the response sent back to the requester.
// This is a temporary measure and this may change in the future.
pub talkreq_callback: fn(&[u8], &[u8]) -> Vec<u8>,

/// The time between pings to ensure connectivity amongst connected nodes. Default: 300
/// seconds.
pub ping_interval: Duration,
Expand All @@ -70,17 +78,19 @@ impl Default for Discv5Config {
fn default() -> Self {
Self {
enable_packet_filter: false,
request_timeout: Duration::from_secs(4),
request_timeout: Duration::from_secs(1),
query_peer_timeout: Duration::from_secs(2),
query_timeout: Duration::from_secs(60),
request_retries: 1,
session_timeout: Duration::from_secs(86400),
session_cache_capacity: 1000,
enr_update: true,
max_nodes_response: 16,
enr_peer_update_min: 10,
query_parallelism: 3,
ip_limit: false,
table_filter: |_| true,
talkreq_callback: |_, _| Vec::new(),
ping_interval: Duration::from_secs(300),
report_discovered_peers: true,
filter_config: FilterConfig::default(),
Expand Down Expand Up @@ -160,6 +170,12 @@ impl Discv5ConfigBuilder {
self
}

/// The maximum number of nodes we response to a find nodes request.
pub fn max_nodes_response(&mut self, max: usize) -> &mut Self {
self.config.max_nodes_response = max;
self
}

/// The minimum number of peer's who agree on an external IP port before updating the
/// local ENR.
pub fn enr_peer_update_min(&mut self, min: usize) -> &mut Self {
Expand Down Expand Up @@ -190,6 +206,13 @@ impl Discv5ConfigBuilder {
self
}

/// The callback function for handling TALK requests. The input is the protocol in bytes and the request data in bytes and
/// the output will be the response sent back to the requester.
pub fn talkreq_callback(&mut self, callback: fn(&[u8], &[u8]) -> Vec<u8>) -> &mut Self {
self.config.talkreq_callback = callback;
self
}

/// The time between pings to ensure connectivity amongst connected nodes.
pub fn ping_interval(&mut self, interval: Duration) -> &mut Self {
self.config.ping_interval = interval;
Expand Down
Loading

0 comments on commit 275c78e

Please sign in to comment.