From c7bd02caaf1086558d83babd01df1458d6270e02 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 11:20:13 +1100 Subject: [PATCH 01/24] Propogate valid attestations accross the network --- beacon_node/rpc/src/attestation.rs | 7 +++---- beacon_node/rpc/src/lib.rs | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/beacon_node/rpc/src/attestation.rs b/beacon_node/rpc/src/attestation.rs index 0f585b7e7..a64ec620f 100644 --- a/beacon_node/rpc/src/attestation.rs +++ b/beacon_node/rpc/src/attestation.rs @@ -136,11 +136,10 @@ impl AttestationService for AttestationServiceInstance { "type" => "valid_attestation", ); - // TODO: Obtain topics from the network service properly. - let topic = types::TopicBuilder::new("beacon_chain".to_string()).build(); + // valid attestation, propagate to the network + let topic = types::TopicBuilder::new("attestations".to_string()).build(); let message = PubsubMessage::Attestation(attestation); - // Publish the attestation to the p2p network via gossipsub. self.network_chan .send(NetworkMessage::Publish { topics: vec![topic], @@ -150,7 +149,7 @@ impl AttestationService for AttestationServiceInstance { error!( self.log, "PublishAttestation"; - "type" => "failed to publish to gossipsub", + "type" => "failed to publish attestation to gossipsub", "error" => format!("{:?}", e) ); }); diff --git a/beacon_node/rpc/src/lib.rs b/beacon_node/rpc/src/lib.rs index f2f1b2abf..4506d90fc 100644 --- a/beacon_node/rpc/src/lib.rs +++ b/beacon_node/rpc/src/lib.rs @@ -60,6 +60,7 @@ pub fn start_server( }; let attestation_service = { let instance = AttestationServiceInstance { + network_chan, chain: beacon_chain.clone(), network_chan, log: log.clone(), From 4e24c8e651fd1c8dfc7641bc5c8e4352c60d71ae Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 12:25:05 +1100 Subject: [PATCH 02/24] Add topics to chain id --- beacon_node/client/src/client_config.rs | 15 +++++++++++++++ beacon_node/eth2-libp2p/src/config.rs | 7 +++++-- eth2/types/src/chain_spec.rs | 9 ++++++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index 166725b61..9ed2a7c6e 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -18,6 +18,21 @@ pub struct ClientConfig { impl Default for ClientConfig { fn default() -> Self { + let data_dir = { + let home = dirs::home_dir().expect("Unable to determine home dir."); + home.join(".lighthouse/") + }; + fs::create_dir_all(&data_dir) + .unwrap_or_else(|_| panic!("Unable to create {:?}", &data_dir)); + + let default_spec = ChainSpec::lighthouse_testnet(); + let default_pubsub_topics = vec![ + default_spec.beacon_chain_topic.clone(), + default_spec.shard_topic_prefix.clone(), + ]; // simple singular attestation topic for now. + let default_net_conf = + NetworkConfig::new(default_spec.boot_nodes.clone(), default_pubsub_topics); + Self { data_dir: PathBuf::from(".lighthouse"), db_type: "disk".to_string(), diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index ee2add75e..97559343a 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -2,6 +2,7 @@ use clap::ArgMatches; use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; use serde_derive::{Deserialize, Serialize}; use types::multiaddr::{Error as MultiaddrError, Multiaddr}; +//use std::time::Duration; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] @@ -30,19 +31,21 @@ impl Default for Config { listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000".to_string()], gs_config: GossipsubConfigBuilder::new() .max_gossip_size(4_000_000) + // .inactivity_timeout(Duration::from_secs(90)) .build(), identify_config: IdentifyConfig::default(), boot_nodes: vec![], client_version: version::version(), - topics: vec![String::from("beacon_chain")], + topics: Vec::new(), } } } impl Config { - pub fn new(boot_nodes: Vec) -> Self { + pub fn new(boot_nodes: Vec, topics: Vec) -> Self { let mut conf = Config::default(); conf.boot_nodes = boot_nodes; + conf.topics = topics; conf } diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 74ce40671..4fa79adcd 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -106,10 +106,11 @@ pub struct ChainSpec { /* * Network specific parameters - * */ pub boot_nodes: Vec, pub chain_id: u8, + pub beacon_chain_topic: String, + pub shard_topic_prefix: String, } impl ChainSpec { @@ -216,10 +217,12 @@ impl ChainSpec { domain_transfer: 5, /* - * Boot nodes + * Network specific */ boot_nodes: vec![], - chain_id: 1, // mainnet chain id + chain_id: 1, // foundation chain id + beacon_chain_topic: String::from("beacon_chain"), + shard_topic_prefix: String::from("attestations"), // simple single attestation topic for now } } From a31d6bcb22f6010a9b737efe780cff1fef940f02 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 13:50:11 +1100 Subject: [PATCH 03/24] RPC methods get pubsub topics from chain spec --- beacon_node/network/src/sync/simple_sync.rs | 3 ++- beacon_node/rpc/src/attestation.rs | 5 ++++- beacon_node/rpc/src/beacon_block.rs | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/beacon_node/network/src/sync/simple_sync.rs b/beacon_node/network/src/sync/simple_sync.rs index 2382e47a4..0e2103900 100644 --- a/beacon_node/network/src/sync/simple_sync.rs +++ b/beacon_node/network/src/sync/simple_sync.rs @@ -4,7 +4,8 @@ use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; use eth2_libp2p::rpc::methods::*; use eth2_libp2p::rpc::{RPCRequest, RPCResponse, RequestId}; use eth2_libp2p::PeerId; -use slog::{debug, error, info, o, warn}; +use slog::{debug, error, info, o, trace, warn}; +use ssz::TreeHash; use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; diff --git a/beacon_node/rpc/src/attestation.rs b/beacon_node/rpc/src/attestation.rs index a64ec620f..c89cbbbb0 100644 --- a/beacon_node/rpc/src/attestation.rs +++ b/beacon_node/rpc/src/attestation.rs @@ -136,8 +136,11 @@ impl AttestationService for AttestationServiceInstance { "type" => "valid_attestation", ); + // get the network topic to send on + let topic_string = self.chain.get_spec().shard_topic_prefix.clone(); + // valid attestation, propagate to the network - let topic = types::TopicBuilder::new("attestations".to_string()).build(); + let topic = types::TopicBuilder::new(topic_string).build(); let message = PubsubMessage::Attestation(attestation); self.network_chan diff --git a/beacon_node/rpc/src/beacon_block.rs b/beacon_node/rpc/src/beacon_block.rs index 533fd285a..791c5008d 100644 --- a/beacon_node/rpc/src/beacon_block.rs +++ b/beacon_node/rpc/src/beacon_block.rs @@ -104,9 +104,9 @@ impl BeaconBlockService for BeaconBlockServiceInstance { "block_root" => format!("{}", block_root), ); - // TODO: Obtain topics from the network service properly. - let topic = - types::TopicBuilder::new("beacon_chain".to_string()).build(); + // get the network topic to send on + let topic_string = self.chain.get_spec().beacon_chain_topic.clone(); + let topic = types::TopicBuilder::new(topic_string).build(); let message = PubsubMessage::Block(block); // Publish the block to the p2p network via gossipsub. From 64abd0bc5b0f4c862c118a17536ebdeeb561be45 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 16:00:09 +1100 Subject: [PATCH 04/24] Removes network parameters from chain spec --- beacon_node/client/src/client_config.rs | 9 ++-- beacon_node/eth2-libp2p/src/behaviour.rs | 2 +- beacon_node/eth2-libp2p/src/config.rs | 57 +++++++++++++++++++++--- beacon_node/eth2-libp2p/src/lib.rs | 5 ++- beacon_node/eth2-libp2p/src/service.rs | 14 ++++-- beacon_node/network/src/service.rs | 2 +- beacon_node/rpc/src/attestation.rs | 3 +- eth2/types/src/chain_spec.rs | 6 --- eth2/types/src/lib.rs | 3 -- 9 files changed, 71 insertions(+), 30 deletions(-) diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index 9ed2a7c6e..685f58c61 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -25,13 +25,10 @@ impl Default for ClientConfig { fs::create_dir_all(&data_dir) .unwrap_or_else(|_| panic!("Unable to create {:?}", &data_dir)); + // currently lighthouse spec let default_spec = ChainSpec::lighthouse_testnet(); - let default_pubsub_topics = vec![ - default_spec.beacon_chain_topic.clone(), - default_spec.shard_topic_prefix.clone(), - ]; // simple singular attestation topic for now. - let default_net_conf = - NetworkConfig::new(default_spec.boot_nodes.clone(), default_pubsub_topics); + // builds a chain-specific network config + let net_conf = NetworkConfig::from(default_spec.chain_id); Self { data_dir: PathBuf::from(".lighthouse"), diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index 10b140c3b..f362f5795 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -1,5 +1,6 @@ use crate::rpc::{RPCEvent, RPCMessage, Rpc}; use crate::NetworkConfig; +use crate::{Topic, TopicHash}; use futures::prelude::*; use libp2p::{ core::{ @@ -15,7 +16,6 @@ use libp2p::{ use slog::{debug, o, trace, warn}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; use types::{Attestation, BeaconBlock}; -use types::{Topic, TopicHash}; /// Builds the network behaviour for the libp2p Swarm. /// Implements gossipsub message routing. diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index 97559343a..88f315d0c 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -20,8 +20,12 @@ pub struct Config { boot_nodes: Vec, /// Client version pub client_version: String, - /// List of topics to subscribe to as strings + /// List of extra topics to initially subscribe to as strings. pub topics: Vec, + /// Shard pubsub topic prefix. + pub shard_prefix: String, + /// The main beacon chain topic to subscribe to. + pub beacon_chain_topic: String, } impl Default for Config { @@ -37,17 +41,16 @@ impl Default for Config { boot_nodes: vec![], client_version: version::version(), topics: Vec::new(), + beacon_chain_topic: String::from("beacon_chain"), + shard_prefix: String::from("attestations"), // single topic for all attestation for the moment. } } } +/// Generates a default Config. impl Config { - pub fn new(boot_nodes: Vec, topics: Vec) -> Self { - let mut conf = Config::default(); - conf.boot_nodes = boot_nodes; - conf.topics = topics; - - conf + pub fn new() -> Self { + Config::default() } pub fn listen_addresses(&self) -> Result, MultiaddrError> { @@ -90,3 +93,43 @@ impl Default for IdentifyConfig { } } } + +/// Creates a standard network config from a chain_id. +/// +/// This creates specified network parameters for each chain type. +impl From for Config { + fn from(chain_type: ChainType) -> Self { + match chain_type { + ChainType::Foundation => Config::default(), + + ChainType::LighthouseTestnet => { + let boot_nodes = vec!["/ip4/127.0.0.1/tcp/9000" + .parse() + .expect("correct multiaddr")]; + Self { + boot_nodes, + ..Config::default() + } + } + + ChainType::Other => Config::default(), + } + } +} + +pub enum ChainType { + Foundation, + LighthouseTestnet, + Other, +} + +/// Maps a chain id to a ChainType. +impl From for ChainType { + fn from(chain_id: u8) -> Self { + match chain_id { + 1 => ChainType::Foundation, + 2 => ChainType::LighthouseTestnet, + _ => ChainType::Other, + } + } +} diff --git a/beacon_node/eth2-libp2p/src/lib.rs b/beacon_node/eth2-libp2p/src/lib.rs index 659d6b01c..4bd775802 100644 --- a/beacon_node/eth2-libp2p/src/lib.rs +++ b/beacon_node/eth2-libp2p/src/lib.rs @@ -10,6 +10,9 @@ mod service; pub use behaviour::PubsubMessage; pub use config::Config as NetworkConfig; +pub use libp2p::floodsub::{Topic, TopicBuilder, TopicHash}; +pub use libp2p::multiaddr; +pub use libp2p::Multiaddr; pub use libp2p::{ gossipsub::{GossipsubConfig, GossipsubConfigBuilder}, PeerId, @@ -17,5 +20,3 @@ pub use libp2p::{ pub use rpc::RPCEvent; pub use service::Libp2pEvent; pub use service::Service; -pub use types::multiaddr; -pub use types::Multiaddr; diff --git a/beacon_node/eth2-libp2p/src/service.rs b/beacon_node/eth2-libp2p/src/service.rs index 18f7ca98c..99de38de6 100644 --- a/beacon_node/eth2-libp2p/src/service.rs +++ b/beacon_node/eth2-libp2p/src/service.rs @@ -3,6 +3,7 @@ use crate::error; use crate::multiaddr::Protocol; use crate::rpc::RPCEvent; use crate::NetworkConfig; +use crate::{TopicBuilder, TopicHash}; use futures::prelude::*; use futures::Stream; use libp2p::core::{ @@ -17,7 +18,6 @@ use libp2p::{core, secio, PeerId, Swarm, Transport}; use slog::{debug, info, trace, warn}; use std::io::{Error, ErrorKind}; use std::time::Duration; -use types::{TopicBuilder, TopicHash}; type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>; type Libp2pBehaviour = Behaviour>; @@ -85,9 +85,17 @@ impl Service { } // subscribe to default gossipsub topics + let mut topics = vec![]; + //TODO: Handle multiple shard attestations. For now we simply use a separate topic for + //attestations + topics.push(config.shard_prefix); + topics.push(config.beacon_chain_topic); + + topics.append(&mut config.topics.clone()); + let mut subscribed_topics = vec![]; - for topic in config.topics { - let t = TopicBuilder::new(topic.to_string()).build(); + for topic in topics { + let t = TopicBuilder::new(topic.clone()).build(); if swarm.subscribe(t) { trace!(log, "Subscribed to topic: {:?}", topic); subscribed_topics.push(topic); diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index 9c71a60f7..c19aef004 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -4,6 +4,7 @@ use crate::NetworkConfig; use beacon_chain::{BeaconChain, BeaconChainTypes}; use crossbeam_channel::{unbounded as channel, Sender, TryRecvError}; use eth2_libp2p::Service as LibP2PService; +use eth2_libp2p::Topic; use eth2_libp2p::{Libp2pEvent, PeerId}; use eth2_libp2p::{PubsubMessage, RPCEvent}; use futures::prelude::*; @@ -13,7 +14,6 @@ use slog::{debug, info, o, trace}; use std::marker::PhantomData; use std::sync::Arc; use tokio::runtime::TaskExecutor; -use types::Topic; /// Service that handles communication between internal services and the eth2_libp2p network service. pub struct Service { diff --git a/beacon_node/rpc/src/attestation.rs b/beacon_node/rpc/src/attestation.rs index c89cbbbb0..b9b05b7cd 100644 --- a/beacon_node/rpc/src/attestation.rs +++ b/beacon_node/rpc/src/attestation.rs @@ -1,5 +1,6 @@ use beacon_chain::{BeaconChain, BeaconChainTypes}; use eth2_libp2p::PubsubMessage; +use eth2_libp2p::TopicBuilder; use futures::Future; use grpcio::{RpcContext, RpcStatus, RpcStatusCode, UnarySink}; use network::NetworkMessage; @@ -140,7 +141,7 @@ impl AttestationService for AttestationServiceInstance { let topic_string = self.chain.get_spec().shard_topic_prefix.clone(); // valid attestation, propagate to the network - let topic = types::TopicBuilder::new(topic_string).build(); + let topic = TopicBuilder::new(topic_string).build(); let message = PubsubMessage::Attestation(attestation); self.network_chan diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 4fa79adcd..8e4bd9c9c 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -107,10 +107,7 @@ pub struct ChainSpec { /* * Network specific parameters */ - pub boot_nodes: Vec, pub chain_id: u8, - pub beacon_chain_topic: String, - pub shard_topic_prefix: String, } impl ChainSpec { @@ -219,10 +216,7 @@ impl ChainSpec { /* * Network specific */ - boot_nodes: vec![], chain_id: 1, // foundation chain id - beacon_chain_topic: String::from("beacon_chain"), - shard_topic_prefix: String::from("attestations"), // simple single attestation topic for now } } diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 4d0ec5fae..2406c3a18 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -82,6 +82,3 @@ pub type ProposerMap = HashMap; pub use bls::{AggregatePublicKey, AggregateSignature, Keypair, PublicKey, SecretKey, Signature}; pub use fixed_len_vec::{typenum, typenum::Unsigned, FixedLenVec}; -pub use libp2p::floodsub::{Topic, TopicBuilder, TopicHash}; -pub use libp2p::multiaddr; -pub use libp2p::Multiaddr; From 7920f8098f1098db85bc90f945af1cbb6f675fe3 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 16:33:12 +1100 Subject: [PATCH 05/24] Complete moving network logc into beacon node --- beacon_node/client/Cargo.toml | 1 + beacon_node/client/src/client_config.rs | 9 ++++++++- beacon_node/eth2-libp2p/src/config.rs | 10 ++++------ beacon_node/eth2-libp2p/src/lib.rs | 2 +- beacon_node/eth2-libp2p/src/service.rs | 5 +++-- beacon_node/network/src/lib.rs | 2 +- beacon_node/rpc/src/attestation.rs | 6 ++---- beacon_node/rpc/src/beacon_block.rs | 6 +++--- 8 files changed, 23 insertions(+), 18 deletions(-) diff --git a/beacon_node/client/Cargo.toml b/beacon_node/client/Cargo.toml index 7c8ee9c7c..3b2e3ead8 100644 --- a/beacon_node/client/Cargo.toml +++ b/beacon_node/client/Cargo.toml @@ -9,6 +9,7 @@ beacon_chain = { path = "../beacon_chain" } network = { path = "../network" } store = { path = "../store" } http_server = { path = "../http_server" } +eth2-libp2p = { path = "../eth2-libp2p" } rpc = { path = "../rpc" } prometheus = "^0.6" types = { path = "../../eth2/types" } diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index 685f58c61..c533cbcc8 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -1,7 +1,13 @@ use clap::ArgMatches; +use eth2_libp2p::multiaddr::Protocol; +use eth2_libp2p::multiaddr::ToMultiaddr; +use eth2_libp2p::Multiaddr; +use fork_choice::ForkChoiceAlgorithm; use http_server::HttpServerConfig; use network::NetworkConfig; +use network::{ChainType, NetworkConfig}; use serde_derive::{Deserialize, Serialize}; +use slog::error; use std::fs; use std::path::PathBuf; @@ -27,8 +33,9 @@ impl Default for ClientConfig { // currently lighthouse spec let default_spec = ChainSpec::lighthouse_testnet(); + let chain_type = ChainType::from(default_spec.chain_id); // builds a chain-specific network config - let net_conf = NetworkConfig::from(default_spec.chain_id); + let net_conf = NetworkConfig::from(chain_type); Self { data_dir: PathBuf::from(".lighthouse"), diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index 88f315d0c..0757f1cbb 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -4,6 +4,10 @@ use serde_derive::{Deserialize, Serialize}; use types::multiaddr::{Error as MultiaddrError, Multiaddr}; //use std::time::Duration; +/// The beacon node topic string to subscribe to. +pub const BEACON_PUBSUB_TOPIC: &str = "beacon_node"; +pub const SHARD_TOPIC_PREFIX: &str = "attestations"; // single topic for all attestation for the moment. + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] /// Network configuration for lighthouse. @@ -22,10 +26,6 @@ pub struct Config { pub client_version: String, /// List of extra topics to initially subscribe to as strings. pub topics: Vec, - /// Shard pubsub topic prefix. - pub shard_prefix: String, - /// The main beacon chain topic to subscribe to. - pub beacon_chain_topic: String, } impl Default for Config { @@ -41,8 +41,6 @@ impl Default for Config { boot_nodes: vec![], client_version: version::version(), topics: Vec::new(), - beacon_chain_topic: String::from("beacon_chain"), - shard_prefix: String::from("attestations"), // single topic for all attestation for the moment. } } } diff --git a/beacon_node/eth2-libp2p/src/lib.rs b/beacon_node/eth2-libp2p/src/lib.rs index 4bd775802..5597f9107 100644 --- a/beacon_node/eth2-libp2p/src/lib.rs +++ b/beacon_node/eth2-libp2p/src/lib.rs @@ -9,7 +9,7 @@ pub mod rpc; mod service; pub use behaviour::PubsubMessage; -pub use config::Config as NetworkConfig; +pub use config::{ChainType, Config as NetworkConfig, BEACON_PUBSUB_TOPIC, SHARD_TOPIC_PREFIX}; pub use libp2p::floodsub::{Topic, TopicBuilder, TopicHash}; pub use libp2p::multiaddr; pub use libp2p::Multiaddr; diff --git a/beacon_node/eth2-libp2p/src/service.rs b/beacon_node/eth2-libp2p/src/service.rs index 99de38de6..9cbceda8d 100644 --- a/beacon_node/eth2-libp2p/src/service.rs +++ b/beacon_node/eth2-libp2p/src/service.rs @@ -4,6 +4,7 @@ use crate::multiaddr::Protocol; use crate::rpc::RPCEvent; use crate::NetworkConfig; use crate::{TopicBuilder, TopicHash}; +use crate::{BEACON_PUBSUB_TOPIC, SHARD_TOPIC_PREFIX}; use futures::prelude::*; use futures::Stream; use libp2p::core::{ @@ -88,8 +89,8 @@ impl Service { let mut topics = vec![]; //TODO: Handle multiple shard attestations. For now we simply use a separate topic for //attestations - topics.push(config.shard_prefix); - topics.push(config.beacon_chain_topic); + topics.push(SHARD_TOPIC_PREFIX.to_string()); + topics.push(BEACON_PUBSUB_TOPIC.to_string()); topics.append(&mut config.topics.clone()); diff --git a/beacon_node/network/src/lib.rs b/beacon_node/network/src/lib.rs index b805c1d75..d00c16292 100644 --- a/beacon_node/network/src/lib.rs +++ b/beacon_node/network/src/lib.rs @@ -4,6 +4,6 @@ pub mod message_handler; pub mod service; pub mod sync; -pub use eth2_libp2p::NetworkConfig; +pub use eth2_libp2p::{ChainType, NetworkConfig}; pub use service::NetworkMessage; pub use service::Service; diff --git a/beacon_node/rpc/src/attestation.rs b/beacon_node/rpc/src/attestation.rs index b9b05b7cd..86f4331f1 100644 --- a/beacon_node/rpc/src/attestation.rs +++ b/beacon_node/rpc/src/attestation.rs @@ -1,6 +1,7 @@ use beacon_chain::{BeaconChain, BeaconChainTypes}; use eth2_libp2p::PubsubMessage; use eth2_libp2p::TopicBuilder; +use eth2_libp2p::SHARD_TOPIC_PREFIX; use futures::Future; use grpcio::{RpcContext, RpcStatus, RpcStatusCode, UnarySink}; use network::NetworkMessage; @@ -137,11 +138,8 @@ impl AttestationService for AttestationServiceInstance { "type" => "valid_attestation", ); - // get the network topic to send on - let topic_string = self.chain.get_spec().shard_topic_prefix.clone(); - // valid attestation, propagate to the network - let topic = TopicBuilder::new(topic_string).build(); + let topic = TopicBuilder::new(SHARD_TOPIC_PREFIX).build(); let message = PubsubMessage::Attestation(attestation); self.network_chan diff --git a/beacon_node/rpc/src/beacon_block.rs b/beacon_node/rpc/src/beacon_block.rs index 791c5008d..cdf46a1ab 100644 --- a/beacon_node/rpc/src/beacon_block.rs +++ b/beacon_node/rpc/src/beacon_block.rs @@ -1,6 +1,7 @@ use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; use crossbeam_channel; -use eth2_libp2p::PubsubMessage; +use eth2_libp2p::BEACON_PUBSUB_TOPIC; +use eth2_libp2p::{PubsubMessage, TopicBuilder}; use futures::Future; use grpcio::{RpcContext, RpcStatus, RpcStatusCode, UnarySink}; use network::NetworkMessage; @@ -105,8 +106,7 @@ impl BeaconBlockService for BeaconBlockServiceInstance { ); // get the network topic to send on - let topic_string = self.chain.get_spec().beacon_chain_topic.clone(); - let topic = types::TopicBuilder::new(topic_string).build(); + let topic = TopicBuilder::new(BEACON_PUBSUB_TOPIC).build(); let message = PubsubMessage::Block(block); // Publish the block to the p2p network via gossipsub. From 2d710f19fc883fc13439d745f84b50cde2463af6 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 17:13:55 +1100 Subject: [PATCH 06/24] Update to latest libp2p --- beacon_node/eth2-libp2p/Cargo.toml | 2 +- beacon_node/network/src/sync/simple_sync.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index ada5faebb..264d77d76 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" # SigP repository until PR is merged -libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "b3c32d9a821ae6cc89079499cc6e8a6bab0bffc3" } +libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "fb852bcc2b9b3935555cc93930e913cbec2b0688" } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/network/src/sync/simple_sync.rs b/beacon_node/network/src/sync/simple_sync.rs index 0e2103900..99e427c8c 100644 --- a/beacon_node/network/src/sync/simple_sync.rs +++ b/beacon_node/network/src/sync/simple_sync.rs @@ -533,6 +533,11 @@ impl SimpleSync { // Add this block to the queue self.import_queue .enqueue_full_blocks(vec![block], peer_id.clone()); + trace!( + self.log, + "NewGossipBlock"; + "peer" => format!("{:?}", peer_id), + ); // Unless the parent is in the queue, request the parent block from the peer. // From be6ebb5ffa3fc9a1def43407eb7ba1d6f37f6284 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Wed, 3 Apr 2019 17:16:32 +1100 Subject: [PATCH 07/24] Add custom inactivity timeout to gossipsub --- beacon_node/eth2-libp2p/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index 0757f1cbb..1a3f3ad3d 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -1,8 +1,8 @@ use clap::ArgMatches; use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; use serde_derive::{Deserialize, Serialize}; +use std::time::Duration; use types::multiaddr::{Error as MultiaddrError, Multiaddr}; -//use std::time::Duration; /// The beacon node topic string to subscribe to. pub const BEACON_PUBSUB_TOPIC: &str = "beacon_node"; @@ -35,7 +35,7 @@ impl Default for Config { listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000".to_string()], gs_config: GossipsubConfigBuilder::new() .max_gossip_size(4_000_000) - // .inactivity_timeout(Duration::from_secs(90)) + .inactivity_timeout(Duration::from_secs(90)) .build(), identify_config: IdentifyConfig::default(), boot_nodes: vec![], From a38f4c4cd1dc1a3c9a22b2c8747364b7316daed1 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 9 Apr 2019 09:15:11 +1000 Subject: [PATCH 08/24] Adds Kademlia for peer discovery --- beacon_node/eth2-libp2p/Cargo.toml | 1 + beacon_node/eth2-libp2p/src/behaviour.rs | 72 ++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index 264d77d76..31c0a7f2d 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -19,3 +19,4 @@ version = { path = "../version" } tokio = "0.1.16" futures = "0.1.25" error-chain = "0.12.0" +tokio-timer = "0.2.10" diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index f362f5795..591d93bb5 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -9,14 +9,21 @@ use libp2p::{ }, gossipsub::{Gossipsub, GossipsubEvent}, identify::{protocol::IdentifyInfo, Identify, IdentifyEvent}, + kad::{Kademlia, KademliaOut}, ping::{Ping, PingEvent}, tokio_io::{AsyncRead, AsyncWrite}, NetworkBehaviour, PeerId, }; use slog::{debug, o, trace, warn}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; +use std::time::{Duration, Instant}; +use tokio_timer::Delay; +>>>>>>> Adds Kademlia for peer discovery use types::{Attestation, BeaconBlock}; +//TODO: Make this dynamic +const TIME_BETWEEN_KAD_REQUESTS: Duration = Duration::from_secs(30); + /// Builds the network behaviour for the libp2p Swarm. /// Implements gossipsub message routing. #[derive(NetworkBehaviour)] @@ -24,17 +31,20 @@ use types::{Attestation, BeaconBlock}; pub struct Behaviour { /// The routing pub-sub mechanism for eth2. gossipsub: Gossipsub, - // TODO: Add Kademlia for peer discovery /// The events generated by this behaviour to be consumed in the swarm poll. serenity_rpc: Rpc, /// Allows discovery of IP addresses for peers on the network. identify: Identify, /// Keep regular connection to peers and disconnect if absent. - // TODO: Keepalive, likely remove this later. - // TODO: Make the ping time customizeable. ping: Ping, + /// Kademlia for peer discovery. + kad: Kademlia, + /// Queue of behaviour events to be processed. #[behaviour(ignore)] events: Vec, + /// The delay until we next search for more peers. + #[behaviour(ignore)] + kad_delay: Delay, /// Logger for behaviour actions. #[behaviour(ignore)] log: slog::Logger, @@ -121,6 +131,33 @@ impl NetworkBehaviourEventProcess } } +// implement the kademlia behaviour +impl NetworkBehaviourEventProcess + for Behaviour +{ + fn inject_event(&mut self, out: KademliaOut) { + match out { + KademliaOut::Discovered { .. } => { + // send this to our topology behaviour + } + KademliaOut::KBucketAdded { .. } => { + // send this to our topology behaviour + } + KademliaOut::FindNodeResult { closer_peers, .. } => { + debug!( + self.log, + "Kademlia query found {} peers", + closer_peers.len() + ); + if closer_peers.is_empty() { + warn!(self.log, "Kademlia random query yielded empty results"); + } + } + KademliaOut::GetProvidersResult { .. } => (), + } + } +} + impl Behaviour { pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { let local_peer_id = local_public_key.clone().into_peer_id(); @@ -128,8 +165,9 @@ impl Behaviour { let behaviour_log = log.new(o!()); Behaviour { - gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()), serenity_rpc: Rpc::new(log), + gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), + kad: Kademlia::new(local_peer_id), identify: Identify::new( identify_config.version, identify_config.user_agent, @@ -137,6 +175,7 @@ impl Behaviour { ), ping: Ping::new(), events: Vec::new(), + kad_delay: Delay::new(Instant::now()), log: behaviour_log, } } @@ -149,6 +188,19 @@ impl Behaviour { return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))); } + // check to see if it's time to search for me peers with kademlia + loop { + match self.kad_delay.poll() { + Ok(Async::Ready(_)) => { + self.get_kad_peers(); + } + Ok(Async::NotReady) => break, + Err(e) => { + warn!(self.log, "Error getting peers from Kademlia. Err: {:?}", e); + } + } + } + Async::NotReady } } @@ -172,6 +224,18 @@ impl Behaviour { self.gossipsub.publish(topic, message_bytes.clone()); } } + + /// Queries for more peers randomly using Kademlia. + pub fn get_kad_peers(&mut self) { + // pick a random PeerId + let random_peer = PeerId::random(); + debug!(self.log, "Running kademlia random peer query"); + self.kad.find_node(random_peer); + + // update the kademlia timeout + self.kad_delay + .reset(Instant::now() + TIME_BETWEEN_KAD_REQUESTS); + } } /// The types of events than can be obtained from polling the behaviour. From e36fa3152d7ee975c323f0a3084faa3aacde8b88 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 15 Apr 2019 11:29:08 +1000 Subject: [PATCH 09/24] Adds verbosity cli flag --- beacon_node/Cargo.toml | 1 + beacon_node/client/Cargo.toml | 4 +++- beacon_node/client/src/client_config.rs | 11 ++------- beacon_node/eth2-libp2p/src/behaviour.rs | 3 ++- beacon_node/src/main.rs | 30 +++++++++++++++++------- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 309f162e5..0bc7de4cc 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -22,3 +22,4 @@ tokio-timer = "0.2.10" futures = "0.1.25" exit-future = "0.1.3" state_processing = { path = "../eth2/state_processing" } +slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] } diff --git a/beacon_node/client/Cargo.toml b/beacon_node/client/Cargo.toml index 3b2e3ead8..7c5a67b89 100644 --- a/beacon_node/client/Cargo.toml +++ b/beacon_node/client/Cargo.toml @@ -19,8 +19,10 @@ slot_clock = { path = "../../eth2/utils/slot_clock" } serde = "1.0" serde_derive = "1.0" error-chain = "0.12.0" -slog = "^2.2.3" eth2_ssz = { path = "../../eth2/utils/ssz" } +slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] } +slog-term = "^2.4.0" +slog-async = "^2.3.0" tokio = "0.1.15" clap = "2.32.0" dirs = "1.0.3" diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index c533cbcc8..4d0e286b0 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -7,7 +7,7 @@ use http_server::HttpServerConfig; use network::NetworkConfig; use network::{ChainType, NetworkConfig}; use serde_derive::{Deserialize, Serialize}; -use slog::error; +use slog::{error, o, Drain, Level}; use std::fs; use std::path::PathBuf; @@ -57,13 +57,6 @@ impl ClientConfig { .and_then(|path| Some(path.join(&self.db_name))) } - /// Returns the core path for the client. - pub fn data_dir(&self) -> Option { - let path = dirs::home_dir()?.join(&self.data_dir); - fs::create_dir_all(&path).ok()?; - Some(path) - } - /// Apply the following arguments to `self`, replacing values if they are specified in `args`. /// /// Returns an error if arguments are obviously invalid. May succeed even if some values are @@ -81,6 +74,6 @@ impl ClientConfig { self.rpc.apply_cli_args(args)?; self.http.apply_cli_args(args)?; - Ok(()) + Ok(log) } } diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index 591d93bb5..b80881853 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -137,7 +137,8 @@ impl NetworkBehaviourEventProcess { + KademliaOut::Discovered { peer_id, .. } => { + debug!(self.log, "Kademlia peer discovered: {:?}", peer_id); // send this to our topology behaviour } KademliaOut::KBucketAdded { .. } => { diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index d6274befc..60b51303a 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -1,5 +1,3 @@ -extern crate slog; - mod run; use clap::{App, Arg}; @@ -14,11 +12,6 @@ pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml"; pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; fn main() { - let decorator = slog_term::TermDecorator::new().build(); - let drain = slog_term::CompactFormat::new(decorator).build().fuse(); - let drain = slog_async::Async::new(drain).build().fuse(); - let logger = slog::Logger::root(drain, o!()); - let matches = App::new("Lighthouse") .version(version::version().as_str()) .author("Sigma Prime ") @@ -116,8 +109,29 @@ fn main() { .short("r") .help("When present, genesis will be within 30 minutes prior. Only for testing"), ) + .arg( + Arg::with_name("verbosity") + .short("v") + .multiple(true) + .help("Sets the verbosity level") + .takes_value(true), + ) .get_matches(); + // build the initial logger + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::CompactFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build(); + + let drain = match matches.occurrences_of("verbosity") { + 0 => drain.filter_level(Level::Info), + 1 => drain.filter_level(Level::Debug), + 2 => drain.filter_level(Level::Trace), + _ => drain.filter_level(Level::Info), + }; + + let logger = slog::Logger::root(drain.fuse(), o!()); + let data_dir = match get_data_dir(&matches, PathBuf::from(DEFAULT_DATA_DIR)) { Ok(dir) => dir, Err(e) => { @@ -128,7 +142,7 @@ fn main() { let client_config_path = data_dir.join(CLIENT_CONFIG_FILENAME); - // Attempt to lead the `ClientConfig` from disk. + // Attempt to load the `ClientConfig` from disk. // // If file doesn't exist, create a new, default one. let mut client_config = match read_from_file::(client_config_path.clone()) { From d2f80e3b2a649826ec3df6685b331a13460388a7 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 15 Apr 2019 13:58:41 +1000 Subject: [PATCH 10/24] Adds env logger to output libp2p logs --- beacon_node/Cargo.toml | 1 + beacon_node/src/main.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 0bc7de4cc..43e75d0a6 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -23,3 +23,4 @@ futures = "0.1.25" exit-future = "0.1.3" state_processing = { path = "../eth2/state_processing" } slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] } +env_logger = "0.6.1" diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 60b51303a..96353ddf9 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -2,6 +2,7 @@ mod run; use clap::{App, Arg}; use client::{ClientConfig, Eth2Config}; +use env_logger::{Builder, Env}; use eth2_config::{get_data_dir, read_from_file, write_to_file}; use slog::{crit, o, Drain}; use std::path::PathBuf; @@ -12,6 +13,9 @@ pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml"; pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; fn main() { + // debugging output for libp2p and external crates + Builder::from_env(Env::default()).init(); + let matches = App::new("Lighthouse") .version(version::version().as_str()) .author("Sigma Prime ") From f80c34b74fa1ca3ba1cfb48113c816170733770a Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 15 Apr 2019 18:29:49 +1000 Subject: [PATCH 11/24] Builds on discovery. Adds identify to discovery --- beacon_node/eth2-libp2p/Cargo.toml | 3 +- beacon_node/eth2-libp2p/src/behaviour.rs | 71 ++------- beacon_node/eth2-libp2p/src/config.rs | 1 + beacon_node/eth2-libp2p/src/discovery.rs | 182 +++++++++++++++++++++++ beacon_node/eth2-libp2p/src/lib.rs | 1 + 5 files changed, 199 insertions(+), 59 deletions(-) create mode 100644 beacon_node/eth2-libp2p/src/discovery.rs diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index 31c0a7f2d..aded78b02 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -8,7 +8,8 @@ edition = "2018" beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" # SigP repository until PR is merged -libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "fb852bcc2b9b3935555cc93930e913cbec2b0688" } +#libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "fb852bcc2b9b3935555cc93930e913cbec2b0688" } +libp2p = { path = "../../../sharding/rust-libp2p" } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index b80881853..e952d1f81 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -1,3 +1,4 @@ +use crate::discovery::Discovery; use crate::rpc::{RPCEvent, RPCMessage, Rpc}; use crate::NetworkConfig; use crate::{Topic, TopicHash}; @@ -9,7 +10,7 @@ use libp2p::{ }, gossipsub::{Gossipsub, GossipsubEvent}, identify::{protocol::IdentifyInfo, Identify, IdentifyEvent}, - kad::{Kademlia, KademliaOut}, + kad::KademliaOut, ping::{Ping, PingEvent}, tokio_io::{AsyncRead, AsyncWrite}, NetworkBehaviour, PeerId, @@ -18,12 +19,8 @@ use slog::{debug, o, trace, warn}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; use std::time::{Duration, Instant}; use tokio_timer::Delay; ->>>>>>> Adds Kademlia for peer discovery use types::{Attestation, BeaconBlock}; -//TODO: Make this dynamic -const TIME_BETWEEN_KAD_REQUESTS: Duration = Duration::from_secs(30); - /// Builds the network behaviour for the libp2p Swarm. /// Implements gossipsub message routing. #[derive(NetworkBehaviour)] @@ -38,13 +35,10 @@ pub struct Behaviour { /// Keep regular connection to peers and disconnect if absent. ping: Ping, /// Kademlia for peer discovery. - kad: Kademlia, + discovery: Discovery, /// Queue of behaviour events to be processed. #[behaviour(ignore)] events: Vec, - /// The delay until we next search for more peers. - #[behaviour(ignore)] - kad_delay: Delay, /// Logger for behaviour actions. #[behaviour(ignore)] log: slog::Logger, @@ -116,6 +110,12 @@ impl NetworkBehaviourEventProcess format!("{:?}", peer_id), "Addresses" => format!("{:?}", info.listen_addrs)); + // inject the found addresses into our discovery behaviour + for address in &info.listen_addrs { + self.discovery + .add_connected_address(&peer_id, address.clone()); + } } IdentifyEvent::Error { .. } => {} IdentifyEvent::SendBack { .. } => {} @@ -131,31 +131,12 @@ impl NetworkBehaviourEventProcess } } -// implement the kademlia behaviour +// implement the discovery behaviour (currently kademlia) impl NetworkBehaviourEventProcess for Behaviour { - fn inject_event(&mut self, out: KademliaOut) { - match out { - KademliaOut::Discovered { peer_id, .. } => { - debug!(self.log, "Kademlia peer discovered: {:?}", peer_id); - // send this to our topology behaviour - } - KademliaOut::KBucketAdded { .. } => { - // send this to our topology behaviour - } - KademliaOut::FindNodeResult { closer_peers, .. } => { - debug!( - self.log, - "Kademlia query found {} peers", - closer_peers.len() - ); - if closer_peers.is_empty() { - warn!(self.log, "Kademlia random query yielded empty results"); - } - } - KademliaOut::GetProvidersResult { .. } => (), - } + fn inject_event(&mut self, _out: KademliaOut) { + // not interested in kademlia results at the moment } } @@ -168,7 +149,7 @@ impl Behaviour { Behaviour { serenity_rpc: Rpc::new(log), gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), - kad: Kademlia::new(local_peer_id), + discovery: Discovery::new(local_peer_id, log), identify: Identify::new( identify_config.version, identify_config.user_agent, @@ -176,7 +157,6 @@ impl Behaviour { ), ping: Ping::new(), events: Vec::new(), - kad_delay: Delay::new(Instant::now()), log: behaviour_log, } } @@ -189,19 +169,6 @@ impl Behaviour { return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))); } - // check to see if it's time to search for me peers with kademlia - loop { - match self.kad_delay.poll() { - Ok(Async::Ready(_)) => { - self.get_kad_peers(); - } - Ok(Async::NotReady) => break, - Err(e) => { - warn!(self.log, "Error getting peers from Kademlia. Err: {:?}", e); - } - } - } - Async::NotReady } } @@ -225,18 +192,6 @@ impl Behaviour { self.gossipsub.publish(topic, message_bytes.clone()); } } - - /// Queries for more peers randomly using Kademlia. - pub fn get_kad_peers(&mut self) { - // pick a random PeerId - let random_peer = PeerId::random(); - debug!(self.log, "Running kademlia random peer query"); - self.kad.find_node(random_peer); - - // update the kademlia timeout - self.kad_delay - .reset(Instant::now() + TIME_BETWEEN_KAD_REQUESTS); - } } /// The types of events than can be obtained from polling the behaviour. diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index 1a3f3ad3d..b6857cd37 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -36,6 +36,7 @@ impl Default for Config { gs_config: GossipsubConfigBuilder::new() .max_gossip_size(4_000_000) .inactivity_timeout(Duration::from_secs(90)) + .heartbeat_interval(Duration::from_secs(20)) .build(), identify_config: IdentifyConfig::default(), boot_nodes: vec![], diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs new file mode 100644 index 000000000..232590c05 --- /dev/null +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -0,0 +1,182 @@ +/// This manages the discovery and management of peers. +/// +/// Currently using Kademlia for peer discovery. +/// +use futures::prelude::*; +use libp2p::core::swarm::{ + ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters, +}; +use libp2p::core::{Multiaddr, PeerId, ProtocolsHandler}; +use libp2p::kad::{Kademlia, KademliaOut}; +use slog::{debug, o, warn}; +use std::collections::HashMap; +use std::time::{Duration, Instant}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_timer::Delay; + +//TODO: Make this dynamic +const TIME_BETWEEN_KAD_REQUESTS: Duration = Duration::from_secs(30); + +/// Maintains a list of discovered peers and implements the discovery protocol to discover new +/// peers. +pub struct Discovery { + /// Queue of events to processed. + // TODO: Re-implement as discovery protocol grows + // events: Vec>, + /// The discovery behaviour used to discover new peers. + discovery: Kademlia, + /// The delay between peer discovery searches. + peer_discovery_delay: Delay, + /// Mapping of known addresses for peer ids. + known_peers: HashMap>, + /// Logger for the discovery behaviour. + log: slog::Logger, +} + +impl Discovery { + pub fn new(local_peer_id: PeerId, log: &slog::Logger) -> Self { + let log = log.new(o!("Service" => "Libp2p-Discovery")); + Self { + // events: Vec::new(), + discovery: Kademlia::new(local_peer_id), + peer_discovery_delay: Delay::new(Instant::now()), + known_peers: HashMap::new(), + log, + } + } + + /// Uses discovery to search for new peers. + pub fn find_peers(&mut self) { + // pick a random PeerId + let random_peer = PeerId::random(); + debug!(self.log, "Searching for peers..."); + self.discovery.find_node(random_peer); + + // update the kademlia timeout + self.peer_discovery_delay + .reset(Instant::now() + TIME_BETWEEN_KAD_REQUESTS); + } + + /// We have discovered an address for a peer, add it to known peers. + pub fn add_connected_address(&mut self, peer_id: &PeerId, address: Multiaddr) { + let known_peers = self + .known_peers + .entry(peer_id.clone()) + .or_insert_with(|| vec![]); + if !known_peers.contains(&address) { + known_peers.push(address.clone()); + } + // pass the address on to kademlia + self.discovery.add_connected_address(peer_id, address); + } +} + +// Redirect all behaviour event to underlying discovery behaviour. +impl NetworkBehaviour for Discovery +where + TSubstream: AsyncRead + AsyncWrite, +{ + type ProtocolsHandler = as NetworkBehaviour>::ProtocolsHandler; + type OutEvent = as NetworkBehaviour>::OutEvent; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + NetworkBehaviour::new_handler(&mut self.discovery) + } + + // TODO: we store all peers in known_peers, when upgrading to discv5 we will avoid duplication + // of peer storage. + fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + if let Some(addresses) = self.known_peers.get(peer_id) { + addresses.clone() + } else { + debug!( + self.log, + "Tried to dial: {:?} but no address stored", peer_id + ); + Vec::new() + } + } + + fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { + NetworkBehaviour::inject_connected(&mut self.discovery, peer_id, endpoint) + } + + fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { + NetworkBehaviour::inject_disconnected(&mut self.discovery, peer_id, endpoint) + } + + fn inject_replaced(&mut self, peer_id: PeerId, closed: ConnectedPoint, opened: ConnectedPoint) { + NetworkBehaviour::inject_replaced(&mut self.discovery, peer_id, closed, opened) + } + + fn inject_node_event( + &mut self, + peer_id: PeerId, + event: ::OutEvent, + ) { + // TODO: Upgrade to discv5 + NetworkBehaviour::inject_node_event(&mut self.discovery, peer_id, event) + } + + fn poll( + &mut self, + params: &mut PollParameters, + ) -> Async< + NetworkBehaviourAction< + ::InEvent, + Self::OutEvent, + >, + > { + // check to see if it's time to search for peers + loop { + match self.peer_discovery_delay.poll() { + Ok(Async::Ready(_)) => { + self.find_peers(); + } + Ok(Async::NotReady) => break, + Err(e) => { + warn!( + self.log, + "Error getting peers from discovery behaviour. Err: {:?}", e + ); + } + } + } + // Poll discovery + match self.discovery.poll(params) { + Async::Ready(action) => { + match &action { + NetworkBehaviourAction::GenerateEvent(disc_output) => match disc_output { + KademliaOut::Discovered { + peer_id, addresses, .. + } => { + debug!(self.log, "Kademlia peer discovered"; "Peer"=> format!("{:?}", peer_id), "Addresses" => format!("{:?}", addresses)); + (*self + .known_peers + .entry(peer_id.clone()) + .or_insert_with(|| vec![])) + .extend(addresses.clone()); + } + KademliaOut::FindNodeResult { closer_peers, .. } => { + debug!( + self.log, + "Kademlia query found {} peers", + closer_peers.len() + ); + if closer_peers.is_empty() { + debug!(self.log, "Kademlia random query yielded empty results"); + } + return Async::Ready(action); + } + _ => {} + }, + _ => {} + }; + return Async::Ready(action); + } + Async::NotReady => (), + } + + Async::NotReady + } +} diff --git a/beacon_node/eth2-libp2p/src/lib.rs b/beacon_node/eth2-libp2p/src/lib.rs index 5597f9107..197c074df 100644 --- a/beacon_node/eth2-libp2p/src/lib.rs +++ b/beacon_node/eth2-libp2p/src/lib.rs @@ -4,6 +4,7 @@ /// This crate builds and manages the libp2p services required by the beacon node. pub mod behaviour; mod config; +mod discovery; pub mod error; pub mod rpc; mod service; From cb7d5eba1c3457409601329ed4889f8c6b1531f5 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 18 Apr 2019 15:26:30 +1000 Subject: [PATCH 12/24] Discovery and gossip bug fixes --- beacon_node/Cargo.toml | 2 +- beacon_node/eth2-libp2p/src/discovery.rs | 20 ++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 43e75d0a6..783cdcda3 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -22,5 +22,5 @@ tokio-timer = "0.2.10" futures = "0.1.25" exit-future = "0.1.3" state_processing = { path = "../eth2/state_processing" } -slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] } +slog = "^2.2.3" env_logger = "0.6.1" diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index 232590c05..d6fd43ef4 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -83,18 +83,9 @@ where NetworkBehaviour::new_handler(&mut self.discovery) } - // TODO: we store all peers in known_peers, when upgrading to discv5 we will avoid duplication - // of peer storage. fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - if let Some(addresses) = self.known_peers.get(peer_id) { - addresses.clone() - } else { - debug!( - self.log, - "Tried to dial: {:?} but no address stored", peer_id - ); - Vec::new() - } + // Let discovery track possible known peers. + self.discovery.addresses_of_peer(peer_id) } fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { @@ -151,11 +142,6 @@ where peer_id, addresses, .. } => { debug!(self.log, "Kademlia peer discovered"; "Peer"=> format!("{:?}", peer_id), "Addresses" => format!("{:?}", addresses)); - (*self - .known_peers - .entry(peer_id.clone()) - .or_insert_with(|| vec![])) - .extend(addresses.clone()); } KademliaOut::FindNodeResult { closer_peers, .. } => { debug!( @@ -163,6 +149,8 @@ where "Kademlia query found {} peers", closer_peers.len() ); + debug!(self.log, "Kademlia peers discovered"; "Peer"=> format!("{:?}", closer_peers)); + if closer_peers.is_empty() { debug!(self.log, "Kademlia random query yielded empty results"); } From b33ce5dd104dbd0caea2c2fab05ffbd35be07ea5 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 30 Apr 2019 15:12:57 +1000 Subject: [PATCH 13/24] Initial core grouping of libp2p behaviours --- beacon_node/eth2-libp2p/Cargo.toml | 2 +- beacon_node/eth2-libp2p/src/behaviour.rs | 48 +-- beacon_node/eth2-libp2p/src/core-behaviour.rs | 279 ++++++++++++++++++ beacon_node/eth2-libp2p/src/discovery.rs | 13 +- beacon_node/network/Cargo.toml | 2 +- 5 files changed, 310 insertions(+), 34 deletions(-) create mode 100644 beacon_node/eth2-libp2p/src/core-behaviour.rs diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index aded78b02..ef404e8b8 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -15,7 +15,7 @@ serde = "1.0" serde_derive = "1.0" eth2_ssz = { path = "../../eth2/utils/ssz" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } -slog = "2.4.1" +slog = { version = "^2.4.1" , features = ["max_level_trace", "release_max_level_trace"] } version = { path = "../version" } tokio = "0.1.16" futures = "0.1.25" diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index e952d1f81..7ddbd95b7 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -3,6 +3,7 @@ use crate::rpc::{RPCEvent, RPCMessage, Rpc}; use crate::NetworkConfig; use crate::{Topic, TopicHash}; use futures::prelude::*; +use libp2p::Multiaddr; use libp2p::{ core::{ swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}, @@ -19,16 +20,18 @@ use slog::{debug, o, trace, warn}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; use std::time::{Duration, Instant}; use tokio_timer::Delay; +use std::collections::HashMap; use types::{Attestation, BeaconBlock}; -/// Builds the network behaviour for the libp2p Swarm. -/// Implements gossipsub message routing. +/// Builds the network behaviour that manages the core protocols of eth2. +/// This core behaviour is managed by `Behaviour` which adds peer management to all core +/// behaviours. #[derive(NetworkBehaviour)] -#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")] -pub struct Behaviour { +#[behaviour(out_event = "CoreBehaviourEvent", poll_method = "poll")] +pub struct CoreCoreBehaviourTSubstream: AsyncRead + AsyncWrite> { /// The routing pub-sub mechanism for eth2. gossipsub: Gossipsub, - /// The events generated by this behaviour to be consumed in the swarm poll. + /// The serenity RPC specified in the wire-0 protocol. serenity_rpc: Rpc, /// Allows discovery of IP addresses for peers on the network. identify: Identify, @@ -36,9 +39,9 @@ pub struct Behaviour { ping: Ping, /// Kademlia for peer discovery. discovery: Discovery, - /// Queue of behaviour events to be processed. #[behaviour(ignore)] - events: Vec, + /// The events generated by this behaviour to be consumed in the swarm poll. + events: Vec, /// Logger for behaviour actions. #[behaviour(ignore)] log: slog::Logger, @@ -46,7 +49,7 @@ pub struct Behaviour { // Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour impl NetworkBehaviourEventProcess - for Behaviour + for CoreBehaviourTSubstream> { fn inject_event(&mut self, event: GossipsubEvent) { match event { @@ -79,7 +82,7 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for Behaviour + for CoreBehaviourTSubstream> { fn inject_event(&mut self, event: RPCMessage) { match event { @@ -94,7 +97,7 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for Behaviour + for CoreBehaviourTSubstream> { fn inject_event(&mut self, event: IdentifyEvent) { match event { @@ -124,7 +127,7 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for Behaviour + for CoreBehaviourTSubstream> { fn inject_event(&mut self, _event: PingEvent) { // not interested in ping responses at the moment. @@ -133,14 +136,14 @@ impl NetworkBehaviourEventProcess // implement the discovery behaviour (currently kademlia) impl NetworkBehaviourEventProcess - for Behaviour + for CoreBehaviourTSubstream> { fn inject_event(&mut self, _out: KademliaOut) { // not interested in kademlia results at the moment } } -impl Behaviour { +impl CoreBehaviourTSubstream> { pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { let local_peer_id = local_public_key.clone().into_peer_id(); let identify_config = net_conf.identify_config.clone(); @@ -174,17 +177,14 @@ impl Behaviour { } /// Implements the combined behaviour for the libp2p service. -impl Behaviour { +impl CoreBehaviourTSubstream> { + /* Pubsub behaviour functions */ + /// Subscribes to a gossipsub topic. pub fn subscribe(&mut self, topic: Topic) -> bool { self.gossipsub.subscribe(topic) } - /// Sends an RPC Request/Response via the RPC protocol. - pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) { - self.serenity_rpc.send_rpc(peer_id, rpc_event); - } - /// Publishes a message on the pubsub (gossipsub) behaviour. pub fn publish(&mut self, topics: Vec, message: PubsubMessage) { let message_bytes = ssz_encode(&message); @@ -192,10 +192,18 @@ impl Behaviour { self.gossipsub.publish(topic, message_bytes.clone()); } } + + /* Eth2 RPC behaviour functions */ + + /// Sends an RPC Request/Response via the RPC protocol. + pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) { + self.serenity_rpc.send_rpc(peer_id, rpc_event); + } + } /// The types of events than can be obtained from polling the behaviour. -pub enum BehaviourEvent { +pub enum CoreBehaviourEvent { RPC(PeerId, RPCEvent), PeerDialed(PeerId), Identified(PeerId, Box), diff --git a/beacon_node/eth2-libp2p/src/core-behaviour.rs b/beacon_node/eth2-libp2p/src/core-behaviour.rs new file mode 100644 index 000000000..e59183b4c --- /dev/null +++ b/beacon_node/eth2-libp2p/src/core-behaviour.rs @@ -0,0 +1,279 @@ +use crate::discovery::Discovery; +use crate::rpc::{RPCEvent, RPCMessage, Rpc}; +use crate::NetworkConfig; +use crate::{Topic, TopicHash}; +use futures::prelude::*; +use libp2p::Multiaddr; +use libp2p::{ + core::{ + swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}, + PublicKey, + }, + gossipsub::{Gossipsub, GossipsubEvent}, + identify::{protocol::IdentifyInfo, Identify, IdentifyEvent}, + kad::KademliaOut, + ping::{Ping, PingEvent}, + tokio_io::{AsyncRead, AsyncWrite}, + NetworkBehaviour, PeerId, +}; +use slog::{debug, o, trace, warn}; +use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream}; +use std::collections::HashMap; +use types::{Attestation, BeaconBlock}; + +/// Builds the network behaviour that manages the core protocols of eth2. +/// This core behaviour is managed by `Behaviour` which adds peer management to all core +/// behaviours. +#[derive(NetworkBehaviour)] +#[behaviour(out_event = "CoreBehaviourEvent", poll_method = "poll")] +pub struct CoreBehaviour { + /// The routing pub-sub mechanism for eth2. + gossipsub: Gossipsub, + /// The serenity RPC specified in the wire-0 protocol. + serenity_rpc: Rpc, + /// Allows discovery of IP addresses for peers on the network. + identify: Identify, + /// Keep regular connection to peers and disconnect if absent. + ping: Ping, + /// Kademlia for peer discovery. + discovery: Discovery, + #[behaviour(ignore)] + /// The events generated by this behaviour to be consumed by the global behaviour. + events: Vec, + /// Logger for behaviour actions. + #[behaviour(ignore)] + log: slog::Logger, +} + +impl CoreBehaviour { + pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { + let local_peer_id = local_public_key.clone().into_peer_id(); + let identify_config = net_conf.identify_config.clone(); + let behaviour_log = log.new(o!()); + + CoreBehaviour { + serenity_rpc: Rpc::new(log), + gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), + discovery: Discovery::new(local_peer_id, log), + identify: Identify::new( + identify_config.version, + identify_config.user_agent, + local_public_key, + ), + ping: Ping::new(), + events: Vec::new(), + log: behaviour_log, + } + } + + /// Consumes the events list when polled. + fn poll( + &mut self, + ) -> Async> { + if !self.events.is_empty() { + return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))); + } + + Async::NotReady + } +} + +// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for CoreBehaviour +impl NetworkBehaviourEventProcess + for CoreBehaviour +{ + fn inject_event(&mut self, event: GossipsubEvent) { + match event { + GossipsubEvent::Message(gs_msg) => { + trace!(self.log, "Received GossipEvent"; "msg" => format!("{:?}", gs_msg)); + + let pubsub_message = match PubsubMessage::ssz_decode(&gs_msg.data, 0) { + //TODO: Punish peer on error + Err(e) => { + warn!( + self.log, + "Received undecodable message from Peer {:?} error", gs_msg.source; + "error" => format!("{:?}", e) + ); + return; + } + Ok((msg, _index)) => msg, + }; + + self.events.push(BehaviourEvent::GossipMessage { + source: gs_msg.source, + topics: gs_msg.topics, + message: pubsub_message, + }); + } + GossipsubEvent::Subscribed { + peer_id: _, + topic: _, + } + | GossipsubEvent::Unsubscribed { + peer_id: _, + topic: _, + } => {} + } + } +} + +impl NetworkBehaviourEventProcess + for CoreBehaviour +{ + fn inject_event(&mut self, event: RPCMessage) { + match event { + RPCMessage::PeerDialed(peer_id) => { + self.events.push(BehaviourEvent::PeerDialed(peer_id)) + } + RPCMessage::RPC(peer_id, rpc_event) => { + self.events.push(BehaviourEvent::RPC(peer_id, rpc_event)) + } + } + } +} + +impl NetworkBehaviourEventProcess + for CoreBehaviour +{ + fn inject_event(&mut self, event: IdentifyEvent) { + match event { + IdentifyEvent::Identified { + peer_id, mut info, .. + } => { + if info.listen_addrs.len() > 20 { + debug!( + self.log, + "More than 20 peers have been identified, truncating" + ); + info.listen_addrs.truncate(20); + } + trace!(self.log, "Found addresses"; "Peer Id" => format!("{:?}", peer_id), "Addresses" => format!("{:?}", info.listen_addrs)); + // inject the found addresses into our discovery behaviour + for address in &info.listen_addrs { + self.discovery + .add_connected_address(&peer_id, address.clone()); + } + self.events.push(BehaviourEvent::Identified(peer_id, info)); + } + IdentifyEvent::Error { .. } => {} + IdentifyEvent::SendBack { .. } => {} + } + } +} + +impl NetworkBehaviourEventProcess + for CoreBehaviour +{ + fn inject_event(&mut self, _event: PingEvent) { + // not interested in ping responses at the moment. + } +} + +// implement the discovery behaviour (currently kademlia) +impl NetworkBehaviourEventProcess + for CoreBehaviour +{ + fn inject_event(&mut self, _out: KademliaOut) { + // not interested in kademlia results at the moment + } +} + +/// Implements the combined behaviour for the libp2p service. +impl CoreBehaviour { + /* Pubsub behaviour functions */ + + /// Subscribes to a gossipsub topic. + pub fn subscribe(&mut self, topic: Topic) -> bool { + self.gossipsub.subscribe(topic) + } + + /// Publishes a message on the pubsub (gossipsub) behaviour. + pub fn publish(&mut self, topics: Vec, message: PubsubMessage) { + let message_bytes = ssz_encode(&message); + for topic in topics { + self.gossipsub.publish(topic, message_bytes.clone()); + } + } + + /* Eth2 RPC behaviour functions */ + + /// Sends an RPC Request/Response via the RPC protocol. + pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) { + self.serenity_rpc.send_rpc(peer_id, rpc_event); + } +} + +/// The types of events than can be obtained from polling the behaviour. +pub enum CoreBehaviourEvent { + RPC(PeerId, RPCEvent), + PeerDialed(PeerId), + Identified(PeerId, IdentifyInfo), + // TODO: This is a stub at the moment + GossipMessage { + source: PeerId, + topics: Vec, + message: PubsubMessage, + }, +} + +/// Messages that are passed to and from the pubsub (Gossipsub) behaviour. +#[derive(Debug, Clone, PartialEq)] +pub enum PubsubMessage { + /// Gossipsub message providing notification of a new block. + Block(BeaconBlock), + /// Gossipsub message providing notification of a new attestation. + Attestation(Attestation), +} + +//TODO: Correctly encode/decode enums. Prefixing with integer for now. +impl Encodable for PubsubMessage { + fn ssz_append(&self, s: &mut SszStream) { + match self { + PubsubMessage::Block(block_gossip) => { + 0u32.ssz_append(s); + block_gossip.ssz_append(s); + } + PubsubMessage::Attestation(attestation_gossip) => { + 1u32.ssz_append(s); + attestation_gossip.ssz_append(s); + } + } + } +} + +impl Decodable for PubsubMessage { + fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> { + let (id, index) = u32::ssz_decode(bytes, index)?; + match id { + 0 => { + let (block, index) = BeaconBlock::ssz_decode(bytes, index)?; + Ok((PubsubMessage::Block(block), index)) + } + 1 => { + let (attestation, index) = Attestation::ssz_decode(bytes, index)?; + Ok((PubsubMessage::Attestation(attestation), index)) + } + _ => Err(DecodeError::Invalid), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use types::*; + + #[test] + fn ssz_encoding() { + let original = PubsubMessage::Block(BeaconBlock::empty(&ChainSpec::foundation())); + + let encoded = ssz_encode(&original); + + println!("{:?}", encoded); + + let (decoded, _i) = PubsubMessage::ssz_decode(&encoded, 0).unwrap(); + + assert_eq!(original, decoded); + } +} diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index d6fd43ef4..dc91b487c 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -9,7 +9,6 @@ use libp2p::core::swarm::{ use libp2p::core::{Multiaddr, PeerId, ProtocolsHandler}; use libp2p::kad::{Kademlia, KademliaOut}; use slog::{debug, o, warn}; -use std::collections::HashMap; use std::time::{Duration, Instant}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_timer::Delay; @@ -27,8 +26,6 @@ pub struct Discovery { discovery: Kademlia, /// The delay between peer discovery searches. peer_discovery_delay: Delay, - /// Mapping of known addresses for peer ids. - known_peers: HashMap>, /// Logger for the discovery behaviour. log: slog::Logger, } @@ -37,10 +34,8 @@ impl Discovery { pub fn new(local_peer_id: PeerId, log: &slog::Logger) -> Self { let log = log.new(o!("Service" => "Libp2p-Discovery")); Self { - // events: Vec::new(), discovery: Kademlia::new(local_peer_id), peer_discovery_delay: Delay::new(Instant::now()), - known_peers: HashMap::new(), log, } } @@ -59,13 +54,6 @@ impl Discovery { /// We have discovered an address for a peer, add it to known peers. pub fn add_connected_address(&mut self, peer_id: &PeerId, address: Multiaddr) { - let known_peers = self - .known_peers - .entry(peer_id.clone()) - .or_insert_with(|| vec![]); - if !known_peers.contains(&address) { - known_peers.push(address.clone()); - } // pass the address on to kademlia self.discovery.add_connected_address(peer_id, address); } @@ -160,6 +148,7 @@ where }, _ => {} }; + // propagate result upwards return Async::Ready(action); } Async::NotReady => (), diff --git a/beacon_node/network/Cargo.toml b/beacon_node/network/Cargo.toml index 695adc9bd..23fbdd7d9 100644 --- a/beacon_node/network/Cargo.toml +++ b/beacon_node/network/Cargo.toml @@ -13,7 +13,7 @@ store = { path = "../store" } eth2-libp2p = { path = "../eth2-libp2p" } version = { path = "../version" } types = { path = "../../eth2/types" } -slog = { version = "^2.2.3" , features = ["max_level_trace", "release_max_level_debug"] } +slog = { version = "^2.2.3" } eth2_ssz = { path = "../../eth2/utils/ssz" } tree_hash = { path = "../../eth2/utils/tree_hash" } futures = "0.1.25" From 43135484ca7f3831ea9e7c63e22e330a931c4644 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 13 May 2019 17:50:11 +1000 Subject: [PATCH 14/24] Update to lastest libp2p --- beacon_node/client/src/client_config.rs | 2 +- beacon_node/eth2-libp2p/src/behaviour.rs | 42 +-- beacon_node/eth2-libp2p/src/core-behaviour.rs | 279 ------------------ beacon_node/eth2-libp2p/src/service.rs | 9 +- 4 files changed, 31 insertions(+), 301 deletions(-) delete mode 100644 beacon_node/eth2-libp2p/src/core-behaviour.rs diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index 4d0e286b0..74115c547 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -1,6 +1,5 @@ use clap::ArgMatches; use eth2_libp2p::multiaddr::Protocol; -use eth2_libp2p::multiaddr::ToMultiaddr; use eth2_libp2p::Multiaddr; use fork_choice::ForkChoiceAlgorithm; use http_server::HttpServerConfig; @@ -51,6 +50,7 @@ impl Default for ClientConfig { } impl ClientConfig { +<<<<<<< HEAD /// Returns the path to which the client may initialize an on-disk database. pub fn db_path(&self) -> Option { self.data_dir() diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index 7ddbd95b7..58f603276 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -3,7 +3,6 @@ use crate::rpc::{RPCEvent, RPCMessage, Rpc}; use crate::NetworkConfig; use crate::{Topic, TopicHash}; use futures::prelude::*; -use libp2p::Multiaddr; use libp2p::{ core::{ swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}, @@ -12,23 +11,24 @@ use libp2p::{ gossipsub::{Gossipsub, GossipsubEvent}, identify::{protocol::IdentifyInfo, Identify, IdentifyEvent}, kad::KademliaOut, - ping::{Ping, PingEvent}, + ping::{Ping, PingConfig, PingEvent}, tokio_io::{AsyncRead, AsyncWrite}, NetworkBehaviour, PeerId, }; use slog::{debug, o, trace, warn}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; +use std::collections::HashMap; +use std::num::NonZeroU32; use std::time::{Duration, Instant}; use tokio_timer::Delay; -use std::collections::HashMap; use types::{Attestation, BeaconBlock}; /// Builds the network behaviour that manages the core protocols of eth2. /// This core behaviour is managed by `Behaviour` which adds peer management to all core /// behaviours. #[derive(NetworkBehaviour)] -#[behaviour(out_event = "CoreBehaviourEvent", poll_method = "poll")] -pub struct CoreCoreBehaviourTSubstream: AsyncRead + AsyncWrite> { +#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")] +pub struct Behaviour { /// The routing pub-sub mechanism for eth2. gossipsub: Gossipsub, /// The serenity RPC specified in the wire-0 protocol. @@ -41,7 +41,7 @@ pub struct CoreCoreBehaviourTSubstream: AsyncRead + AsyncWrite> { discovery: Discovery, #[behaviour(ignore)] /// The events generated by this behaviour to be consumed in the swarm poll. - events: Vec, + events: Vec, /// Logger for behaviour actions. #[behaviour(ignore)] log: slog::Logger, @@ -49,7 +49,7 @@ pub struct CoreCoreBehaviourTSubstream: AsyncRead + AsyncWrite> { // Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour impl NetworkBehaviourEventProcess - for CoreBehaviourTSubstream> + for Behaviour { fn inject_event(&mut self, event: GossipsubEvent) { match event { @@ -82,7 +82,7 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for CoreBehaviourTSubstream> + for Behaviour { fn inject_event(&mut self, event: RPCMessage) { match event { @@ -97,7 +97,7 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for CoreBehaviourTSubstream> + for Behaviour { fn inject_event(&mut self, event: IdentifyEvent) { match event { @@ -127,7 +127,7 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for CoreBehaviourTSubstream> + for Behaviour { fn inject_event(&mut self, _event: PingEvent) { // not interested in ping responses at the moment. @@ -136,19 +136,28 @@ impl NetworkBehaviourEventProcess // implement the discovery behaviour (currently kademlia) impl NetworkBehaviourEventProcess - for CoreBehaviourTSubstream> + for Behaviour { fn inject_event(&mut self, _out: KademliaOut) { // not interested in kademlia results at the moment } } -impl CoreBehaviourTSubstream> { +impl Behaviour { pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { let local_peer_id = local_public_key.clone().into_peer_id(); - let identify_config = net_conf.identify_config.clone(); let behaviour_log = log.new(o!()); + // identify configuration + let identify_config = net_conf.identify_config.clone(); + + // ping configuration + let ping_config = PingConfig::new() + .with_timeout(Duration::from_secs(30)) + .with_interval(Duration::from_secs(20)) + .with_max_failures(NonZeroU32::new(2).expect("2 != 0")) + .with_keep_alive(false); + Behaviour { serenity_rpc: Rpc::new(log), gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), @@ -158,7 +167,7 @@ impl CoreBehaviourTSubstream> { identify_config.user_agent, local_public_key, ), - ping: Ping::new(), + ping: Ping::new(ping_config), events: Vec::new(), log: behaviour_log, } @@ -177,7 +186,7 @@ impl CoreBehaviourTSubstream> { } /// Implements the combined behaviour for the libp2p service. -impl CoreBehaviourTSubstream> { +impl Behaviour { /* Pubsub behaviour functions */ /// Subscribes to a gossipsub topic. @@ -199,11 +208,10 @@ impl CoreBehaviourTSubstream> { pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) { self.serenity_rpc.send_rpc(peer_id, rpc_event); } - } /// The types of events than can be obtained from polling the behaviour. -pub enum CoreBehaviourEvent { +pub enum BehaviourEvent { RPC(PeerId, RPCEvent), PeerDialed(PeerId), Identified(PeerId, Box), diff --git a/beacon_node/eth2-libp2p/src/core-behaviour.rs b/beacon_node/eth2-libp2p/src/core-behaviour.rs deleted file mode 100644 index e59183b4c..000000000 --- a/beacon_node/eth2-libp2p/src/core-behaviour.rs +++ /dev/null @@ -1,279 +0,0 @@ -use crate::discovery::Discovery; -use crate::rpc::{RPCEvent, RPCMessage, Rpc}; -use crate::NetworkConfig; -use crate::{Topic, TopicHash}; -use futures::prelude::*; -use libp2p::Multiaddr; -use libp2p::{ - core::{ - swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}, - PublicKey, - }, - gossipsub::{Gossipsub, GossipsubEvent}, - identify::{protocol::IdentifyInfo, Identify, IdentifyEvent}, - kad::KademliaOut, - ping::{Ping, PingEvent}, - tokio_io::{AsyncRead, AsyncWrite}, - NetworkBehaviour, PeerId, -}; -use slog::{debug, o, trace, warn}; -use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream}; -use std::collections::HashMap; -use types::{Attestation, BeaconBlock}; - -/// Builds the network behaviour that manages the core protocols of eth2. -/// This core behaviour is managed by `Behaviour` which adds peer management to all core -/// behaviours. -#[derive(NetworkBehaviour)] -#[behaviour(out_event = "CoreBehaviourEvent", poll_method = "poll")] -pub struct CoreBehaviour { - /// The routing pub-sub mechanism for eth2. - gossipsub: Gossipsub, - /// The serenity RPC specified in the wire-0 protocol. - serenity_rpc: Rpc, - /// Allows discovery of IP addresses for peers on the network. - identify: Identify, - /// Keep regular connection to peers and disconnect if absent. - ping: Ping, - /// Kademlia for peer discovery. - discovery: Discovery, - #[behaviour(ignore)] - /// The events generated by this behaviour to be consumed by the global behaviour. - events: Vec, - /// Logger for behaviour actions. - #[behaviour(ignore)] - log: slog::Logger, -} - -impl CoreBehaviour { - pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { - let local_peer_id = local_public_key.clone().into_peer_id(); - let identify_config = net_conf.identify_config.clone(); - let behaviour_log = log.new(o!()); - - CoreBehaviour { - serenity_rpc: Rpc::new(log), - gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), - discovery: Discovery::new(local_peer_id, log), - identify: Identify::new( - identify_config.version, - identify_config.user_agent, - local_public_key, - ), - ping: Ping::new(), - events: Vec::new(), - log: behaviour_log, - } - } - - /// Consumes the events list when polled. - fn poll( - &mut self, - ) -> Async> { - if !self.events.is_empty() { - return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))); - } - - Async::NotReady - } -} - -// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for CoreBehaviour -impl NetworkBehaviourEventProcess - for CoreBehaviour -{ - fn inject_event(&mut self, event: GossipsubEvent) { - match event { - GossipsubEvent::Message(gs_msg) => { - trace!(self.log, "Received GossipEvent"; "msg" => format!("{:?}", gs_msg)); - - let pubsub_message = match PubsubMessage::ssz_decode(&gs_msg.data, 0) { - //TODO: Punish peer on error - Err(e) => { - warn!( - self.log, - "Received undecodable message from Peer {:?} error", gs_msg.source; - "error" => format!("{:?}", e) - ); - return; - } - Ok((msg, _index)) => msg, - }; - - self.events.push(BehaviourEvent::GossipMessage { - source: gs_msg.source, - topics: gs_msg.topics, - message: pubsub_message, - }); - } - GossipsubEvent::Subscribed { - peer_id: _, - topic: _, - } - | GossipsubEvent::Unsubscribed { - peer_id: _, - topic: _, - } => {} - } - } -} - -impl NetworkBehaviourEventProcess - for CoreBehaviour -{ - fn inject_event(&mut self, event: RPCMessage) { - match event { - RPCMessage::PeerDialed(peer_id) => { - self.events.push(BehaviourEvent::PeerDialed(peer_id)) - } - RPCMessage::RPC(peer_id, rpc_event) => { - self.events.push(BehaviourEvent::RPC(peer_id, rpc_event)) - } - } - } -} - -impl NetworkBehaviourEventProcess - for CoreBehaviour -{ - fn inject_event(&mut self, event: IdentifyEvent) { - match event { - IdentifyEvent::Identified { - peer_id, mut info, .. - } => { - if info.listen_addrs.len() > 20 { - debug!( - self.log, - "More than 20 peers have been identified, truncating" - ); - info.listen_addrs.truncate(20); - } - trace!(self.log, "Found addresses"; "Peer Id" => format!("{:?}", peer_id), "Addresses" => format!("{:?}", info.listen_addrs)); - // inject the found addresses into our discovery behaviour - for address in &info.listen_addrs { - self.discovery - .add_connected_address(&peer_id, address.clone()); - } - self.events.push(BehaviourEvent::Identified(peer_id, info)); - } - IdentifyEvent::Error { .. } => {} - IdentifyEvent::SendBack { .. } => {} - } - } -} - -impl NetworkBehaviourEventProcess - for CoreBehaviour -{ - fn inject_event(&mut self, _event: PingEvent) { - // not interested in ping responses at the moment. - } -} - -// implement the discovery behaviour (currently kademlia) -impl NetworkBehaviourEventProcess - for CoreBehaviour -{ - fn inject_event(&mut self, _out: KademliaOut) { - // not interested in kademlia results at the moment - } -} - -/// Implements the combined behaviour for the libp2p service. -impl CoreBehaviour { - /* Pubsub behaviour functions */ - - /// Subscribes to a gossipsub topic. - pub fn subscribe(&mut self, topic: Topic) -> bool { - self.gossipsub.subscribe(topic) - } - - /// Publishes a message on the pubsub (gossipsub) behaviour. - pub fn publish(&mut self, topics: Vec, message: PubsubMessage) { - let message_bytes = ssz_encode(&message); - for topic in topics { - self.gossipsub.publish(topic, message_bytes.clone()); - } - } - - /* Eth2 RPC behaviour functions */ - - /// Sends an RPC Request/Response via the RPC protocol. - pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) { - self.serenity_rpc.send_rpc(peer_id, rpc_event); - } -} - -/// The types of events than can be obtained from polling the behaviour. -pub enum CoreBehaviourEvent { - RPC(PeerId, RPCEvent), - PeerDialed(PeerId), - Identified(PeerId, IdentifyInfo), - // TODO: This is a stub at the moment - GossipMessage { - source: PeerId, - topics: Vec, - message: PubsubMessage, - }, -} - -/// Messages that are passed to and from the pubsub (Gossipsub) behaviour. -#[derive(Debug, Clone, PartialEq)] -pub enum PubsubMessage { - /// Gossipsub message providing notification of a new block. - Block(BeaconBlock), - /// Gossipsub message providing notification of a new attestation. - Attestation(Attestation), -} - -//TODO: Correctly encode/decode enums. Prefixing with integer for now. -impl Encodable for PubsubMessage { - fn ssz_append(&self, s: &mut SszStream) { - match self { - PubsubMessage::Block(block_gossip) => { - 0u32.ssz_append(s); - block_gossip.ssz_append(s); - } - PubsubMessage::Attestation(attestation_gossip) => { - 1u32.ssz_append(s); - attestation_gossip.ssz_append(s); - } - } - } -} - -impl Decodable for PubsubMessage { - fn ssz_decode(bytes: &[u8], index: usize) -> Result<(Self, usize), DecodeError> { - let (id, index) = u32::ssz_decode(bytes, index)?; - match id { - 0 => { - let (block, index) = BeaconBlock::ssz_decode(bytes, index)?; - Ok((PubsubMessage::Block(block), index)) - } - 1 => { - let (attestation, index) = Attestation::ssz_decode(bytes, index)?; - Ok((PubsubMessage::Attestation(attestation), index)) - } - _ => Err(DecodeError::Invalid), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use types::*; - - #[test] - fn ssz_encoding() { - let original = PubsubMessage::Block(BeaconBlock::empty(&ChainSpec::foundation())); - - let encoded = ssz_encode(&original); - - println!("{:?}", encoded); - - let (decoded, _i) = PubsubMessage::ssz_decode(&encoded, 0).unwrap(); - - assert_eq!(original, decoded); - } -} diff --git a/beacon_node/eth2-libp2p/src/service.rs b/beacon_node/eth2-libp2p/src/service.rs index 9cbceda8d..68ca72620 100644 --- a/beacon_node/eth2-libp2p/src/service.rs +++ b/beacon_node/eth2-libp2p/src/service.rs @@ -63,11 +63,12 @@ impl Service { .map_err(|e| format!("Invalid listen multiaddr: {}", e))? { match Swarm::listen_on(&mut swarm, address.clone()) { - Ok(mut listen_addr) => { - listen_addr.append(Protocol::P2p(local_peer_id.clone().into())); - info!(log, "Listening on: {}", listen_addr); + Ok(_) => { + let mut log_address = address.clone(); + log_address.push(Protocol::P2p(local_peer_id.clone().into())); + info!(log, "Listening on: {}", log_address); } - Err(err) => warn!(log, "Cannot listen on: {} : {:?}", address, err), + Err(err) => warn!(log, "Cannot listen on: {} because: {:?}", address, err), }; } // connect to boot nodes - these are currently stored as multiaddrs From c7e17c86414b56457ca94cdd52bc81735e91e781 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Thu, 20 Jun 2019 22:43:50 +1000 Subject: [PATCH 15/24] Updates for latest master --- beacon_node/client/src/client_config.rs | 23 ++++++++--------------- beacon_node/eth2-libp2p/Cargo.toml | 5 ++--- beacon_node/eth2-libp2p/src/behaviour.rs | 4 ---- eth2/types/src/chain_spec.rs | 4 +++- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index 74115c547..93ff5f7eb 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -23,19 +23,6 @@ pub struct ClientConfig { impl Default for ClientConfig { fn default() -> Self { - let data_dir = { - let home = dirs::home_dir().expect("Unable to determine home dir."); - home.join(".lighthouse/") - }; - fs::create_dir_all(&data_dir) - .unwrap_or_else(|_| panic!("Unable to create {:?}", &data_dir)); - - // currently lighthouse spec - let default_spec = ChainSpec::lighthouse_testnet(); - let chain_type = ChainType::from(default_spec.chain_id); - // builds a chain-specific network config - let net_conf = NetworkConfig::from(chain_type); - Self { data_dir: PathBuf::from(".lighthouse"), db_type: "disk".to_string(), @@ -50,13 +37,19 @@ impl Default for ClientConfig { } impl ClientConfig { -<<<<<<< HEAD /// Returns the path to which the client may initialize an on-disk database. pub fn db_path(&self) -> Option { self.data_dir() .and_then(|path| Some(path.join(&self.db_name))) } + /// Returns the core path for the client. + pub fn data_dir(&self) -> Option { + let path = dirs::home_dir()?.join(&self.data_dir); + fs::create_dir_all(&path).ok()?; + Some(path) + } + /// Apply the following arguments to `self`, replacing values if they are specified in `args`. /// /// Returns an error if arguments are obviously invalid. May succeed even if some values are @@ -74,6 +67,6 @@ impl ClientConfig { self.rpc.apply_cli_args(args)?; self.http.apply_cli_args(args)?; - Ok(log) + Ok(()) } } diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index ef404e8b8..3fc327e99 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -7,9 +7,8 @@ edition = "2018" [dependencies] beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" -# SigP repository until PR is merged -#libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "fb852bcc2b9b3935555cc93930e913cbec2b0688" } -libp2p = { path = "../../../sharding/rust-libp2p" } +# SigP repository +libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "8d5e5bbbe32d07ad271d6a2e15fde0347894061a" } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index 58f603276..ed466bb75 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -147,11 +147,7 @@ impl Behaviour { pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { let local_peer_id = local_public_key.clone().into_peer_id(); let behaviour_log = log.new(o!()); - - // identify configuration let identify_config = net_conf.identify_config.clone(); - - // ping configuration let ping_config = PingConfig::new() .with_timeout(Duration::from_secs(30)) .with_interval(Duration::from_secs(20)) diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 8e4bd9c9c..d35f696b9 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -107,6 +107,7 @@ pub struct ChainSpec { /* * Network specific parameters */ + pub boot_nodes: Vec, pub chain_id: u8, } @@ -216,7 +217,8 @@ impl ChainSpec { /* * Network specific */ - chain_id: 1, // foundation chain id + boot_nodes: vec![], + chain_id: 1, // mainnet chain id } } From 6ee2b4df34bf84a2c9701279d4b196043a446312 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Sun, 23 Jun 2019 12:34:00 +1000 Subject: [PATCH 16/24] Complete merging of network addition branch --- beacon_node/Cargo.toml | 1 - beacon_node/client/src/client_config.rs | 3 +-- beacon_node/eth2-libp2p/Cargo.toml | 2 +- beacon_node/eth2-libp2p/src/behaviour.rs | 6 ++++-- beacon_node/eth2-libp2p/src/config.rs | 2 +- beacon_node/eth2-libp2p/src/discovery.rs | 2 +- beacon_node/network/src/sync/simple_sync.rs | 1 - beacon_node/rpc/src/lib.rs | 1 - beacon_node/src/main.rs | 2 +- eth2/types/Cargo.toml | 1 - eth2/types/src/chain_spec.rs | 13 +++---------- 11 files changed, 12 insertions(+), 22 deletions(-) diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 783cdcda3..7e43a13df 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -22,5 +22,4 @@ tokio-timer = "0.2.10" futures = "0.1.25" exit-future = "0.1.3" state_processing = { path = "../eth2/state_processing" } -slog = "^2.2.3" env_logger = "0.6.1" diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs index 93ff5f7eb..f2f356daf 100644 --- a/beacon_node/client/src/client_config.rs +++ b/beacon_node/client/src/client_config.rs @@ -3,7 +3,6 @@ use eth2_libp2p::multiaddr::Protocol; use eth2_libp2p::Multiaddr; use fork_choice::ForkChoiceAlgorithm; use http_server::HttpServerConfig; -use network::NetworkConfig; use network::{ChainType, NetworkConfig}; use serde_derive::{Deserialize, Serialize}; use slog::{error, o, Drain, Level}; @@ -29,7 +28,7 @@ impl Default for ClientConfig { db_name: "chain_db".to_string(), // Note: there are no default bootnodes specified. // Once bootnodes are established, add them here. - network: NetworkConfig::new(vec![]), + network: NetworkConfig::new(), rpc: rpc::RPCConfig::default(), http: HttpServerConfig::default(), } diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index 3fc327e99..6fd141028 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" # SigP repository -libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "8d5e5bbbe32d07ad271d6a2e15fde0347894061a" } +libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "71744d4090ebd93a993d1b390787919add4098fd" } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index ed466bb75..c711a2134 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -111,14 +111,16 @@ impl NetworkBehaviourEventProcess format!("{:?}", peer_id), "Addresses" => format!("{:?}", info.listen_addrs)); // inject the found addresses into our discovery behaviour + for address in &info.listen_addrs { self.discovery .add_connected_address(&peer_id, address.clone()); } + + self.events + .push(BehaviourEvent::Identified(peer_id, Box::new(info))); } IdentifyEvent::Error { .. } => {} IdentifyEvent::SendBack { .. } => {} diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index b6857cd37..baa1bf47f 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -1,8 +1,8 @@ use clap::ArgMatches; use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; +use libp2p::multiaddr::{Error as MultiaddrError, Multiaddr}; use serde_derive::{Deserialize, Serialize}; use std::time::Duration; -use types::multiaddr::{Error as MultiaddrError, Multiaddr}; /// The beacon node topic string to subscribe to. pub const BEACON_PUBSUB_TOPIC: &str = "beacon_node"; diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index dc91b487c..c3a02b16f 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -55,7 +55,7 @@ impl Discovery { /// We have discovered an address for a peer, add it to known peers. pub fn add_connected_address(&mut self, peer_id: &PeerId, address: Multiaddr) { // pass the address on to kademlia - self.discovery.add_connected_address(peer_id, address); + self.discovery.add_address(peer_id, address); } } diff --git a/beacon_node/network/src/sync/simple_sync.rs b/beacon_node/network/src/sync/simple_sync.rs index 99e427c8c..5899e5aea 100644 --- a/beacon_node/network/src/sync/simple_sync.rs +++ b/beacon_node/network/src/sync/simple_sync.rs @@ -5,7 +5,6 @@ use eth2_libp2p::rpc::methods::*; use eth2_libp2p::rpc::{RPCRequest, RPCResponse, RequestId}; use eth2_libp2p::PeerId; use slog::{debug, error, info, o, trace, warn}; -use ssz::TreeHash; use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; diff --git a/beacon_node/rpc/src/lib.rs b/beacon_node/rpc/src/lib.rs index 4506d90fc..3e6fd3e73 100644 --- a/beacon_node/rpc/src/lib.rs +++ b/beacon_node/rpc/src/lib.rs @@ -62,7 +62,6 @@ pub fn start_server( let instance = AttestationServiceInstance { network_chan, chain: beacon_chain.clone(), - network_chan, log: log.clone(), }; create_attestation_service(instance) diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 96353ddf9..07215119f 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -4,7 +4,7 @@ use clap::{App, Arg}; use client::{ClientConfig, Eth2Config}; use env_logger::{Builder, Env}; use eth2_config::{get_data_dir, read_from_file, write_to_file}; -use slog::{crit, o, Drain}; +use slog::{crit, o, Drain, Level}; use std::path::PathBuf; pub const DEFAULT_DATA_DIR: &str = ".lighthouse"; diff --git a/eth2/types/Cargo.toml b/eth2/types/Cargo.toml index ed6307684..fd6578340 100644 --- a/eth2/types/Cargo.toml +++ b/eth2/types/Cargo.toml @@ -32,7 +32,6 @@ swap_or_not_shuffle = { path = "../utils/swap_or_not_shuffle" } test_random_derive = { path = "../utils/test_random_derive" } tree_hash = { path = "../utils/tree_hash" } tree_hash_derive = { path = "../utils/tree_hash_derive" } -libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "b3c32d9a821ae6cc89079499cc6e8a6bab0bffc3" } [dev-dependencies] env_logger = "0.6.0" diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index d35f696b9..6073fb32e 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -104,10 +104,7 @@ pub struct ChainSpec { domain_voluntary_exit: u32, domain_transfer: u32, - /* - * Network specific parameters - */ - pub boot_nodes: Vec, + pub boot_nodes: Vec, pub chain_id: u8, } @@ -230,12 +227,8 @@ impl ChainSpec { pub fn minimal() -> Self { let genesis_slot = Slot::new(0); - // Note: these bootnodes are placeholders. - // - // Should be updated once static bootnodes exist. - let boot_nodes = vec!["/ip4/127.0.0.1/tcp/9000" - .parse() - .expect("correct multiaddr")]; + // Note: bootnodes to be updated when static nodes exist. + let boot_nodes = vec![]; Self { target_committee_size: 4, From 44c9058477792d1fc15e6e0d68b44464d4a55d81 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 25 Jun 2019 14:51:45 +1000 Subject: [PATCH 17/24] Integrate discv5 into lighthouse --- beacon_node/client/Cargo.toml | 2 +- beacon_node/client/src/client_config.rs | 71 ------- beacon_node/client/src/lib.rs | 4 +- beacon_node/eth2-libp2p/Cargo.toml | 3 +- beacon_node/eth2-libp2p/src/behaviour.rs | 116 ++++------- beacon_node/eth2-libp2p/src/config.rs | 143 ++++++------- beacon_node/eth2-libp2p/src/discovery.rs | 246 ++++++++++++++++------- beacon_node/eth2-libp2p/src/lib.rs | 4 +- beacon_node/eth2-libp2p/src/service.rs | 46 +---- beacon_node/network/src/lib.rs | 2 +- beacon_node/src/main.rs | 24 ++- beacon_node/src/run.rs | 2 +- 12 files changed, 312 insertions(+), 351 deletions(-) delete mode 100644 beacon_node/client/src/client_config.rs diff --git a/beacon_node/client/Cargo.toml b/beacon_node/client/Cargo.toml index 7c5a67b89..94a529ea7 100644 --- a/beacon_node/client/Cargo.toml +++ b/beacon_node/client/Cargo.toml @@ -16,7 +16,7 @@ types = { path = "../../eth2/types" } tree_hash = { path = "../../eth2/utils/tree_hash" } eth2_config = { path = "../../eth2/utils/eth2_config" } slot_clock = { path = "../../eth2/utils/slot_clock" } -serde = "1.0" +serde = "1.0.93" serde_derive = "1.0" error-chain = "0.12.0" eth2_ssz = { path = "../../eth2/utils/ssz" } diff --git a/beacon_node/client/src/client_config.rs b/beacon_node/client/src/client_config.rs deleted file mode 100644 index f2f356daf..000000000 --- a/beacon_node/client/src/client_config.rs +++ /dev/null @@ -1,71 +0,0 @@ -use clap::ArgMatches; -use eth2_libp2p::multiaddr::Protocol; -use eth2_libp2p::Multiaddr; -use fork_choice::ForkChoiceAlgorithm; -use http_server::HttpServerConfig; -use network::{ChainType, NetworkConfig}; -use serde_derive::{Deserialize, Serialize}; -use slog::{error, o, Drain, Level}; -use std::fs; -use std::path::PathBuf; - -/// The core configuration of a Lighthouse beacon node. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ClientConfig { - pub data_dir: PathBuf, - pub db_type: String, - db_name: String, - pub network: network::NetworkConfig, - pub rpc: rpc::RPCConfig, - pub http: HttpServerConfig, -} - -impl Default for ClientConfig { - fn default() -> Self { - Self { - data_dir: PathBuf::from(".lighthouse"), - db_type: "disk".to_string(), - db_name: "chain_db".to_string(), - // Note: there are no default bootnodes specified. - // Once bootnodes are established, add them here. - network: NetworkConfig::new(), - rpc: rpc::RPCConfig::default(), - http: HttpServerConfig::default(), - } - } -} - -impl ClientConfig { - /// Returns the path to which the client may initialize an on-disk database. - pub fn db_path(&self) -> Option { - self.data_dir() - .and_then(|path| Some(path.join(&self.db_name))) - } - - /// Returns the core path for the client. - pub fn data_dir(&self) -> Option { - let path = dirs::home_dir()?.join(&self.data_dir); - fs::create_dir_all(&path).ok()?; - Some(path) - } - - /// Apply the following arguments to `self`, replacing values if they are specified in `args`. - /// - /// Returns an error if arguments are obviously invalid. May succeed even if some values are - /// invalid. - pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> { - if let Some(dir) = args.value_of("datadir") { - self.data_dir = PathBuf::from(dir); - }; - - if let Some(dir) = args.value_of("db") { - self.db_type = dir.to_string(); - } - - self.network.apply_cli_args(args)?; - self.rpc.apply_cli_args(args)?; - self.http.apply_cli_args(args)?; - - Ok(()) - } -} diff --git a/beacon_node/client/src/lib.rs b/beacon_node/client/src/lib.rs index 18ddef7bb..7eee8ac0a 100644 --- a/beacon_node/client/src/lib.rs +++ b/beacon_node/client/src/lib.rs @@ -1,7 +1,7 @@ extern crate slog; mod beacon_chain_types; -mod client_config; +mod config; pub mod error; pub mod notifier; @@ -21,7 +21,7 @@ use tokio::timer::Interval; pub use beacon_chain::BeaconChainTypes; pub use beacon_chain_types::ClientType; pub use beacon_chain_types::InitialiseBeaconChain; -pub use client_config::ClientConfig; +pub use config::Config as ClientConfig; pub use eth2_config::Eth2Config; /// Main beacon node client service. This provides the connection and initialisation of the clients diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index 6fd141028..fd7162767 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -8,7 +8,8 @@ edition = "2018" beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" # SigP repository -libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "71744d4090ebd93a993d1b390787919add4098fd" } +libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "f018f5c443ed5a93de890048dbc6755393373e72" } +enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "f018f5c443ed5a93de890048dbc6755393373e72", features = ["serde"] } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/eth2-libp2p/src/behaviour.rs b/beacon_node/eth2-libp2p/src/behaviour.rs index c711a2134..4e4cf24f3 100644 --- a/beacon_node/eth2-libp2p/src/behaviour.rs +++ b/beacon_node/eth2-libp2p/src/behaviour.rs @@ -1,26 +1,23 @@ use crate::discovery::Discovery; use crate::rpc::{RPCEvent, RPCMessage, Rpc}; -use crate::NetworkConfig; +use crate::{error, NetworkConfig}; use crate::{Topic, TopicHash}; use futures::prelude::*; use libp2p::{ core::{ + identity::Keypair, swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess}, - PublicKey, }, + discv5::Discv5Event, gossipsub::{Gossipsub, GossipsubEvent}, - identify::{protocol::IdentifyInfo, Identify, IdentifyEvent}, - kad::KademliaOut, ping::{Ping, PingConfig, PingEvent}, tokio_io::{AsyncRead, AsyncWrite}, NetworkBehaviour, PeerId, }; -use slog::{debug, o, trace, warn}; +use slog::{o, trace, warn}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; -use std::collections::HashMap; use std::num::NonZeroU32; -use std::time::{Duration, Instant}; -use tokio_timer::Delay; +use std::time::Duration; use types::{Attestation, BeaconBlock}; /// Builds the network behaviour that manages the core protocols of eth2. @@ -33,8 +30,6 @@ pub struct Behaviour { gossipsub: Gossipsub, /// The serenity RPC specified in the wire-0 protocol. serenity_rpc: Rpc, - /// Allows discovery of IP addresses for peers on the network. - identify: Identify, /// Keep regular connection to peers and disconnect if absent. ping: Ping, /// Kademlia for peer discovery. @@ -47,6 +42,31 @@ pub struct Behaviour { log: slog::Logger, } +impl Behaviour { + pub fn new( + local_key: &Keypair, + net_conf: &NetworkConfig, + log: &slog::Logger, + ) -> error::Result { + let local_peer_id = local_key.public().clone().into_peer_id(); + let behaviour_log = log.new(o!()); + let ping_config = PingConfig::new() + .with_timeout(Duration::from_secs(30)) + .with_interval(Duration::from_secs(20)) + .with_max_failures(NonZeroU32::new(2).expect("2 != 0")) + .with_keep_alive(false); + + Ok(Behaviour { + serenity_rpc: Rpc::new(log), + gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), + discovery: Discovery::new(local_key, net_conf, log)?, + ping: Ping::new(ping_config), + events: Vec::new(), + log: behaviour_log, + }) + } +} + // Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour impl NetworkBehaviourEventProcess for Behaviour @@ -96,38 +116,6 @@ impl NetworkBehaviourEventProcess NetworkBehaviourEventProcess - for Behaviour -{ - fn inject_event(&mut self, event: IdentifyEvent) { - match event { - IdentifyEvent::Identified { - peer_id, mut info, .. - } => { - if info.listen_addrs.len() > 20 { - debug!( - self.log, - "More than 20 peers have been identified, truncating" - ); - info.listen_addrs.truncate(20); - } - trace!(self.log, "Found addresses"; "Peer Id" => format!("{:?}", peer_id), "Addresses" => format!("{:?}", info.listen_addrs)); - // inject the found addresses into our discovery behaviour - - for address in &info.listen_addrs { - self.discovery - .add_connected_address(&peer_id, address.clone()); - } - - self.events - .push(BehaviourEvent::Identified(peer_id, Box::new(info))); - } - IdentifyEvent::Error { .. } => {} - IdentifyEvent::SendBack { .. } => {} - } - } -} - impl NetworkBehaviourEventProcess for Behaviour { @@ -136,41 +124,7 @@ impl NetworkBehaviourEventProcess } } -// implement the discovery behaviour (currently kademlia) -impl NetworkBehaviourEventProcess - for Behaviour -{ - fn inject_event(&mut self, _out: KademliaOut) { - // not interested in kademlia results at the moment - } -} - impl Behaviour { - pub fn new(local_public_key: PublicKey, net_conf: &NetworkConfig, log: &slog::Logger) -> Self { - let local_peer_id = local_public_key.clone().into_peer_id(); - let behaviour_log = log.new(o!()); - let identify_config = net_conf.identify_config.clone(); - let ping_config = PingConfig::new() - .with_timeout(Duration::from_secs(30)) - .with_interval(Duration::from_secs(20)) - .with_max_failures(NonZeroU32::new(2).expect("2 != 0")) - .with_keep_alive(false); - - Behaviour { - serenity_rpc: Rpc::new(log), - gossipsub: Gossipsub::new(local_peer_id.clone(), net_conf.gs_config.clone()), - discovery: Discovery::new(local_peer_id, log), - identify: Identify::new( - identify_config.version, - identify_config.user_agent, - local_public_key, - ), - ping: Ping::new(ping_config), - events: Vec::new(), - log: behaviour_log, - } - } - /// Consumes the events list when polled. fn poll( &mut self, @@ -183,6 +137,14 @@ impl Behaviour { } } +impl NetworkBehaviourEventProcess + for Behaviour +{ + fn inject_event(&mut self, _event: Discv5Event) { + // discv5 has no events to inject + } +} + /// Implements the combined behaviour for the libp2p service. impl Behaviour { /* Pubsub behaviour functions */ @@ -212,8 +174,6 @@ impl Behaviour { pub enum BehaviourEvent { RPC(PeerId, RPCEvent), PeerDialed(PeerId), - Identified(PeerId, Box), - // TODO: This is a stub at the moment GossipMessage { source: PeerId, topics: Vec, diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index baa1bf47f..00a8ed51e 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -1,29 +1,44 @@ use clap::ArgMatches; -use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; -use libp2p::multiaddr::{Error as MultiaddrError, Multiaddr}; +use enr::Enr; +use libp2p::{ + gossipsub::{GossipsubConfig, GossipsubConfigBuilder}, + multiaddr::Multiaddr, +}; use serde_derive::{Deserialize, Serialize}; use std::time::Duration; /// The beacon node topic string to subscribe to. -pub const BEACON_PUBSUB_TOPIC: &str = "beacon_node"; -pub const SHARD_TOPIC_PREFIX: &str = "attestations"; // single topic for all attestation for the moment. +pub const BEACON_PUBSUB_TOPIC: &str = "beacon_block"; +pub const BEACON_ATTESTATION_TOPIC: &str = "beacon_attestation"; +//TODO: Implement shard subnets +pub const SHARD_TOPIC_PREFIX: &str = "shard"; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] /// Network configuration for lighthouse. pub struct Config { /// IP address to listen on. - listen_addresses: Vec, + pub listen_addresses: Vec, + + /// Specifies the IP address that the discovery protocol will listen on. + pub discovery_address: std::net::IpAddr, + + /// UDP port that discovery listens on. + pub discovery_port: u16, + + /// Target number of connected peers. + pub max_peers: usize, + /// Gossipsub configuration parameters. #[serde(skip)] pub gs_config: GossipsubConfig, - /// Configuration parameters for node identification protocol. - #[serde(skip)] - pub identify_config: IdentifyConfig, + /// List of nodes to initially connect to. - boot_nodes: Vec, + pub boot_nodes: Vec, + /// Client version pub client_version: String, + /// List of extra topics to initially subscribe to as strings. pub topics: Vec, } @@ -32,13 +47,16 @@ impl Default for Config { /// Generate a default network configuration. fn default() -> Self { Config { - listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000".to_string()], + listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000".parse().expect("vaild multiaddr")], + discovery_address: "0.0.0.0".parse().expect("valid ip address"), + discovery_port: 9000, + max_peers: 10, + //TODO: Set realistic values for production gs_config: GossipsubConfigBuilder::new() .max_gossip_size(4_000_000) .inactivity_timeout(Duration::from_secs(90)) .heartbeat_interval(Duration::from_secs(20)) .build(), - identify_config: IdentifyConfig::default(), boot_nodes: vec![], client_version: version::version(), topics: Vec::new(), @@ -52,83 +70,42 @@ impl Config { Config::default() } - pub fn listen_addresses(&self) -> Result, MultiaddrError> { - self.listen_addresses.iter().map(|s| s.parse()).collect() - } - - pub fn boot_nodes(&self) -> Result, MultiaddrError> { - self.boot_nodes.iter().map(|s| s.parse()).collect() - } - - pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), &'static str> { + pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), String> { if let Some(listen_address_str) = args.value_of("listen-address") { - let listen_addresses = listen_address_str.split(',').map(Into::into).collect(); - self.listen_addresses = listen_addresses; + self.listen_addresses = listen_address_str + .split(',') + .map(|a| { + a.parse::() + .map_err(|_| format!("Invalid Listen address: {:?}", a)) + }) + .collect::, _>>()?; } - if let Some(boot_addresses_str) = args.value_of("boot-nodes") { - let boot_addresses = boot_addresses_str.split(',').map(Into::into).collect(); - self.boot_nodes = boot_addresses; + if let Some(max_peers_str) = args.value_of("maxpeers") { + self.max_peers = max_peers_str + .parse::() + .map_err(|_| format!("Invalid number of max peers: {}", max_peers_str))?; + } + + if let Some(discovery_address_str) = args.value_of("disc-listen-address") { + self.discovery_address = discovery_address_str + .parse::() + .map_err(|_| format!("Invalid discovery address: {:?}", discovery_address_str))?; + } + + if let Some(boot_enr_str) = args.value_of("boot-nodes") { + self.boot_nodes = boot_enr_str + .split(',') + .map(|enr| enr.parse().map_err(|_| format!("Invalid ENR: {}", enr))) + .collect::, _>>()?; + } + + if let Some(disc_port_str) = args.value_of("disc-port") { + self.discovery_port = disc_port_str + .parse::() + .map_err(|_| format!("Invalid discovery port: {}", disc_port_str))?; } Ok(()) } } - -/// The configuration parameters for the Identify protocol -#[derive(Debug, Clone)] -pub struct IdentifyConfig { - /// The protocol version to listen on. - pub version: String, - /// The client's name and version for identification. - pub user_agent: String, -} - -impl Default for IdentifyConfig { - fn default() -> Self { - Self { - version: "/eth/serenity/1.0".to_string(), - user_agent: version::version(), - } - } -} - -/// Creates a standard network config from a chain_id. -/// -/// This creates specified network parameters for each chain type. -impl From for Config { - fn from(chain_type: ChainType) -> Self { - match chain_type { - ChainType::Foundation => Config::default(), - - ChainType::LighthouseTestnet => { - let boot_nodes = vec!["/ip4/127.0.0.1/tcp/9000" - .parse() - .expect("correct multiaddr")]; - Self { - boot_nodes, - ..Config::default() - } - } - - ChainType::Other => Config::default(), - } - } -} - -pub enum ChainType { - Foundation, - LighthouseTestnet, - Other, -} - -/// Maps a chain id to a ChainType. -impl From for ChainType { - fn from(chain_id: u8) -> Self { - match chain_id { - 1 => ChainType::Foundation, - 2 => ChainType::LighthouseTestnet, - _ => ChainType::Other, - } - } -} diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index c3a02b16f..9a1e75691 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -1,3 +1,4 @@ +use crate::{error, NetworkConfig}; /// This manages the discovery and management of peers. /// /// Currently using Kademlia for peer discovery. @@ -6,66 +7,154 @@ use futures::prelude::*; use libp2p::core::swarm::{ ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters, }; -use libp2p::core::{Multiaddr, PeerId, ProtocolsHandler}; -use libp2p::kad::{Kademlia, KademliaOut}; -use slog::{debug, o, warn}; +use libp2p::core::{identity::Keypair, Multiaddr, PeerId, ProtocolsHandler}; +use libp2p::discv5::{Discv5, Discv5Event}; +use libp2p::enr::{Enr, EnrBuilder, NodeId}; +use libp2p::multiaddr::Protocol; +use slog::{debug, error, info, o, warn}; +use std::collections::HashSet; use std::time::{Duration, Instant}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_timer::Delay; -//TODO: Make this dynamic -const TIME_BETWEEN_KAD_REQUESTS: Duration = Duration::from_secs(30); +/// Maximum seconds before searching for extra peers. +const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 60; -/// Maintains a list of discovered peers and implements the discovery protocol to discover new -/// peers. +/// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5 +/// libp2p protocol. pub struct Discovery { - /// Queue of events to processed. - // TODO: Re-implement as discovery protocol grows - // events: Vec>, - /// The discovery behaviour used to discover new peers. - discovery: Kademlia, + /// The peers currently connected to libp2p streams. + connected_peers: HashSet, + + /// The target number of connected peers on the libp2p interface. + max_peers: usize, + /// The delay between peer discovery searches. peer_discovery_delay: Delay, + + /// Tracks the last discovery delay. The delay is doubled each round until the max + /// time is reached. + past_discovery_delay: u64, + + /// The TCP port for libp2p. Used to convert an updated IP address to a multiaddr. Note: This + /// assumes that the external TCP port is the same as the internal TCP port if behind a NAT. + //TODO: Improve NAT handling limit the above restriction + tcp_port: u16, + + /// The discovery behaviour used to discover new peers. + discovery: Discv5, + /// Logger for the discovery behaviour. log: slog::Logger, } impl Discovery { - pub fn new(local_peer_id: PeerId, log: &slog::Logger) -> Self { + pub fn new( + local_key: &Keypair, + net_conf: &NetworkConfig, + log: &slog::Logger, + ) -> error::Result { let log = log.new(o!("Service" => "Libp2p-Discovery")); - Self { - discovery: Kademlia::new(local_peer_id), - peer_discovery_delay: Delay::new(Instant::now()), - log, + + // Build the local ENR. + // The first TCP listening address is used for the ENR record. This will inform our peers to + // connect to this TCP port and establish libp2p streams. + // Note: Discovery should update the ENR record's IP to the external IP as seen by the + // majority of our peers. + let tcp_multiaddr = net_conf + .listen_addresses + .iter() + .filter(|a| { + if let Some(Protocol::Tcp(_)) = a.iter().last() { + true + } else { + false + } + }) + .next() + .ok_or_else(|| "No valid TCP addresses")?; + + let ip: std::net::IpAddr = match tcp_multiaddr.iter().next() { + Some(Protocol::Ip4(ip)) => ip.into(), + Some(Protocol::Ip6(ip)) => ip.into(), + _ => { + error!(log, "Multiaddr has an invalid IP address"); + return Err(format!("Invalid IP Address: {}", tcp_multiaddr).into()); + } + }; + + let tcp_port = match tcp_multiaddr.iter().last() { + Some(Protocol::Tcp(tcp)) => tcp, + _ => unreachable!(), + }; + + let local_enr = EnrBuilder::new() + .ip(ip.into()) + .tcp(tcp_port) + .udp(net_conf.discovery_port) + .build(&local_key) + .map_err(|e| format!("Could not build Local ENR: {:?}", e))?; + info!(log, "Local ENR: {}", local_enr.to_base64()); + + let mut discovery = Discv5::new(local_enr, local_key.clone(), net_conf.discovery_address) + .map_err(|e| format!("Discv5 service failed: {:?}", e))?; + + // Add bootnodes to routing table + for bootnode_enr in net_conf.boot_nodes.clone() { + discovery.add_enr(bootnode_enr); } + + Ok(Self { + connected_peers: HashSet::new(), + max_peers: net_conf.max_peers, + peer_discovery_delay: Delay::new(Instant::now()), + past_discovery_delay: 1, + tcp_port, + discovery, + log, + }) } - /// Uses discovery to search for new peers. - pub fn find_peers(&mut self) { - // pick a random PeerId - let random_peer = PeerId::random(); + /// Manually search for peers. This restarts the discovery round, sparking multiple rapid + /// queries. + pub fn discover_peers(&mut self) { + self.past_discovery_delay = 1; + self.find_peers(); + } + + /// Add an Enr to the routing table of the discovery mechanism. + pub fn add_enr(&mut self, enr: Enr) { + self.discovery.add_enr(enr); + } + + /// Search for new peers using the underlying discovery mechanism. + fn find_peers(&mut self) { + // pick a random NodeId + let random_node = NodeId::random(); debug!(self.log, "Searching for peers..."); - self.discovery.find_node(random_peer); + self.discovery.find_node(random_node); - // update the kademlia timeout + // update the time until next discovery + let delay = { + if self.past_discovery_delay < MAX_TIME_BETWEEN_PEER_SEARCHES { + self.past_discovery_delay *= 2; + self.past_discovery_delay + } else { + MAX_TIME_BETWEEN_PEER_SEARCHES + } + }; self.peer_discovery_delay - .reset(Instant::now() + TIME_BETWEEN_KAD_REQUESTS); - } - - /// We have discovered an address for a peer, add it to known peers. - pub fn add_connected_address(&mut self, peer_id: &PeerId, address: Multiaddr) { - // pass the address on to kademlia - self.discovery.add_address(peer_id, address); + .reset(Instant::now() + Duration::from_secs(delay)); } } -// Redirect all behaviour event to underlying discovery behaviour. +// Redirect all behaviour events to underlying discovery behaviour. impl NetworkBehaviour for Discovery where TSubstream: AsyncRead + AsyncWrite, { - type ProtocolsHandler = as NetworkBehaviour>::ProtocolsHandler; - type OutEvent = as NetworkBehaviour>::OutEvent; + type ProtocolsHandler = as NetworkBehaviour>::ProtocolsHandler; + type OutEvent = as NetworkBehaviour>::OutEvent; fn new_handler(&mut self) -> Self::ProtocolsHandler { NetworkBehaviour::new_handler(&mut self.discovery) @@ -76,25 +165,29 @@ where self.discovery.addresses_of_peer(peer_id) } - fn inject_connected(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) { - NetworkBehaviour::inject_connected(&mut self.discovery, peer_id, endpoint) + fn inject_connected(&mut self, peer_id: PeerId, _endpoint: ConnectedPoint) { + self.connected_peers.insert(peer_id); } - fn inject_disconnected(&mut self, peer_id: &PeerId, endpoint: ConnectedPoint) { - NetworkBehaviour::inject_disconnected(&mut self.discovery, peer_id, endpoint) + fn inject_disconnected(&mut self, peer_id: &PeerId, _endpoint: ConnectedPoint) { + self.connected_peers.remove(peer_id); } - fn inject_replaced(&mut self, peer_id: PeerId, closed: ConnectedPoint, opened: ConnectedPoint) { - NetworkBehaviour::inject_replaced(&mut self.discovery, peer_id, closed, opened) + fn inject_replaced( + &mut self, + _peer_id: PeerId, + _closed: ConnectedPoint, + _opened: ConnectedPoint, + ) { + // discv5 doesn't implement } fn inject_node_event( &mut self, - peer_id: PeerId, - event: ::OutEvent, + _peer_id: PeerId, + _event: ::OutEvent, ) { - // TODO: Upgrade to discv5 - NetworkBehaviour::inject_node_event(&mut self.discovery, peer_id, event) + // discv5 doesn't implement } fn poll( @@ -106,7 +199,7 @@ where Self::OutEvent, >, > { - // check to see if it's time to search for peers + // search of peers if it is time loop { match self.peer_discovery_delay.poll() { Ok(Async::Ready(_)) => { @@ -114,46 +207,49 @@ where } Ok(Async::NotReady) => break, Err(e) => { - warn!( - self.log, - "Error getting peers from discovery behaviour. Err: {:?}", e - ); + warn!(self.log, "Discovery peer search failed: {:?}", e); } } } - // Poll discovery - match self.discovery.poll(params) { - Async::Ready(action) => { - match &action { - NetworkBehaviourAction::GenerateEvent(disc_output) => match disc_output { - KademliaOut::Discovered { - peer_id, addresses, .. - } => { - debug!(self.log, "Kademlia peer discovered"; "Peer"=> format!("{:?}", peer_id), "Addresses" => format!("{:?}", addresses)); - } - KademliaOut::FindNodeResult { closer_peers, .. } => { - debug!( - self.log, - "Kademlia query found {} peers", - closer_peers.len() - ); - debug!(self.log, "Kademlia peers discovered"; "Peer"=> format!("{:?}", closer_peers)); - if closer_peers.is_empty() { - debug!(self.log, "Kademlia random query yielded empty results"); + // Poll discovery + loop { + match self.discovery.poll(params) { + Async::Ready(NetworkBehaviourAction::GenerateEvent(event)) => { + match event { + Discv5Event::Discovered(enr) => { + debug!(self.log, "Discv5: Peer discovered"; "Peer"=> format!("{:?}", enr.peer_id()), "Addresses" => format!("{:?}", enr.multiaddr())); + + let peer_id = enr.peer_id(); + // if we need more peers, attempt a connection + if self.connected_peers.len() < self.max_peers + && self.connected_peers.get(&peer_id).is_none() + { + return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }); + } + } + Discv5Event::SocketUpdated(socket) => { + info!(self.log, "Address updated"; "IP" => format!("{}",socket.ip())); + let mut address = Multiaddr::from(socket.ip()); + address.push(Protocol::Tcp(self.tcp_port)); + return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { + address, + }); + } + Discv5Event::FindNodeResult { closer_peers, .. } => { + debug!(self.log, "Discv5 query found {} peers", closer_peers.len()); + if closer_peers.is_empty() { + debug!(self.log, "Discv5 random query yielded empty results"); } - return Async::Ready(action); } _ => {} - }, - _ => {} - }; - // propagate result upwards - return Async::Ready(action); + } + } + // discv5 does not output any other NetworkBehaviourAction + Async::Ready(_) => {} + Async::NotReady => break, } - Async::NotReady => (), } - Async::NotReady } } diff --git a/beacon_node/eth2-libp2p/src/lib.rs b/beacon_node/eth2-libp2p/src/lib.rs index 197c074df..7a3b2e632 100644 --- a/beacon_node/eth2-libp2p/src/lib.rs +++ b/beacon_node/eth2-libp2p/src/lib.rs @@ -10,7 +10,9 @@ pub mod rpc; mod service; pub use behaviour::PubsubMessage; -pub use config::{ChainType, Config as NetworkConfig, BEACON_PUBSUB_TOPIC, SHARD_TOPIC_PREFIX}; +pub use config::{ + Config as NetworkConfig, BEACON_ATTESTATION_TOPIC, BEACON_PUBSUB_TOPIC, SHARD_TOPIC_PREFIX, +}; pub use libp2p::floodsub::{Topic, TopicBuilder, TopicHash}; pub use libp2p::multiaddr; pub use libp2p::Multiaddr; diff --git a/beacon_node/eth2-libp2p/src/service.rs b/beacon_node/eth2-libp2p/src/service.rs index 68ca72620..780b1453f 100644 --- a/beacon_node/eth2-libp2p/src/service.rs +++ b/beacon_node/eth2-libp2p/src/service.rs @@ -4,7 +4,7 @@ use crate::multiaddr::Protocol; use crate::rpc::RPCEvent; use crate::NetworkConfig; use crate::{TopicBuilder, TopicHash}; -use crate::{BEACON_PUBSUB_TOPIC, SHARD_TOPIC_PREFIX}; +use crate::{BEACON_ATTESTATION_TOPIC, BEACON_PUBSUB_TOPIC}; use futures::prelude::*; use futures::Stream; use libp2p::core::{ @@ -36,32 +36,24 @@ pub struct Service { impl Service { pub fn new(config: NetworkConfig, log: slog::Logger) -> error::Result { - debug!(log, "Libp2p Service starting"); + debug!(log, "Network-libp2p Service starting"); - // TODO: Currently using secp256k1 key pairs. Wire protocol specifies RSA. Waiting for this - // PR to be merged to generate RSA keys: https://github.com/briansmith/ring/pull/733 // TODO: Save and recover node key from disk + // TODO: Currently using secp256k1 keypairs - currently required for discv5 let local_private_key = identity::Keypair::generate_secp256k1(); - - let local_public_key = local_private_key.public(); let local_peer_id = PeerId::from(local_private_key.public()); info!(log, "Local peer id: {:?}", local_peer_id); let mut swarm = { - // Set up the transport - let transport = build_transport(local_private_key); - // Set up gossipsub routing - let behaviour = Behaviour::new(local_public_key.clone(), &config, &log); - // Set up Topology - let topology = local_peer_id.clone(); - Swarm::new(transport, behaviour, topology) + // Set up the transport - tcp/ws with secio and mplex/yamux + let transport = build_transport(local_private_key.clone()); + // Lighthouse network behaviour + let behaviour = Behaviour::new(&local_private_key, &config, &log)?; + Swarm::new(transport, behaviour, local_peer_id.clone()) }; // listen on all addresses - for address in config - .listen_addresses() - .map_err(|e| format!("Invalid listen multiaddr: {}", e))? - { + for address in config.listen_addresses { match Swarm::listen_on(&mut swarm, address.clone()) { Ok(_) => { let mut log_address = address.clone(); @@ -71,28 +63,13 @@ impl Service { Err(err) => warn!(log, "Cannot listen on: {} because: {:?}", address, err), }; } - // connect to boot nodes - these are currently stored as multiaddrs - // Once we have discovery, can set to peerId - for bootnode in config - .boot_nodes() - .map_err(|e| format!("Invalid boot node multiaddr: {:?}", e))? - { - match Swarm::dial_addr(&mut swarm, bootnode.clone()) { - Ok(()) => debug!(log, "Dialing bootnode: {}", bootnode), - Err(err) => debug!( - log, - "Could not connect to bootnode: {} error: {:?}", bootnode, err - ), - }; - } // subscribe to default gossipsub topics let mut topics = vec![]; //TODO: Handle multiple shard attestations. For now we simply use a separate topic for //attestations - topics.push(SHARD_TOPIC_PREFIX.to_string()); + topics.push(BEACON_ATTESTATION_TOPIC.to_string()); topics.push(BEACON_PUBSUB_TOPIC.to_string()); - topics.append(&mut config.topics.clone()); let mut subscribed_topics = vec![]; @@ -145,9 +122,6 @@ impl Stream for Service { BehaviourEvent::PeerDialed(peer_id) => { return Ok(Async::Ready(Some(Libp2pEvent::PeerDialed(peer_id)))); } - BehaviourEvent::Identified(peer_id, info) => { - return Ok(Async::Ready(Some(Libp2pEvent::Identified(peer_id, info)))); - } }, Ok(Async::Ready(None)) => unreachable!("Swarm stream shouldn't end"), Ok(Async::NotReady) => break, diff --git a/beacon_node/network/src/lib.rs b/beacon_node/network/src/lib.rs index d00c16292..b805c1d75 100644 --- a/beacon_node/network/src/lib.rs +++ b/beacon_node/network/src/lib.rs @@ -4,6 +4,6 @@ pub mod message_handler; pub mod service; pub mod sync; -pub use eth2_libp2p::{ChainType, NetworkConfig}; +pub use eth2_libp2p::NetworkConfig; pub use service::NetworkMessage; pub use service::Service; diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 07215119f..51d3a58f9 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -37,11 +37,33 @@ fn main() { .help("One or more comma-delimited multi-addresses to listen for p2p connections.") .takes_value(true), ) + .arg( + Arg::with_name("maxpeers") + .long("maxpeers") + .value_name("Max Peers") + .help("The maximum number of peers (default 10)") + .takes_value(true), + ) .arg( Arg::with_name("boot-nodes") .long("boot-nodes") + .allow_hyphen_values(true) .value_name("BOOTNODES") - .help("One or more comma-delimited multi-addresses to bootstrap the p2p network.") + .help("One or more comma-delimited base64-encoded ENR's to bootstrap the p2p network.") + .takes_value(true), + ) + .arg( + Arg::with_name("disc-listen-address") + .long("disc-listen_address") + .value_name("DISCPORT") + .help("The IP address that the discovery protocol will listen on. Defaults to 0.0.0.0") + .takes_value(true), + ) + .arg( + Arg::with_name("discovery-port") + .long("disc-port") + .value_name("DISCPORT") + .help("Listen UDP port for the discovery process") .takes_value(true), ) // rpc related arguments diff --git a/beacon_node/src/run.rs b/beacon_node/src/run.rs index 834f9a428..15883d974 100644 --- a/beacon_node/src/run.rs +++ b/beacon_node/src/run.rs @@ -84,7 +84,7 @@ pub fn run_beacon_node( info!( log, "Started beacon node"; - "p2p_listen_addresses" => format!("{:?}", &other_client_config.network.listen_addresses()), + "p2p_listen_addresses" => format!("{:?}", &other_client_config.network.listen_addresses), "data_dir" => format!("{:?}", other_client_config.data_dir()), "spec_constants" => &spec_constants, "db_type" => &other_client_config.db_type, From 0952a36a21f270f895c743668baecba269527293 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 25 Jun 2019 18:02:11 +1000 Subject: [PATCH 18/24] Initial integration of discovery v5 --- beacon_node/client/src/config.rs | 67 +++++++++++++++++++++ beacon_node/eth2-libp2p/Cargo.toml | 4 +- beacon_node/eth2-libp2p/src/config.rs | 45 ++++++++------ beacon_node/eth2-libp2p/src/discovery.rs | 77 ++++++++++-------------- beacon_node/eth2-libp2p/src/service.rs | 30 +++++---- beacon_node/src/main.rs | 33 +++++----- beacon_node/src/run.rs | 20 +++--- 7 files changed, 172 insertions(+), 104 deletions(-) create mode 100644 beacon_node/client/src/config.rs diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs new file mode 100644 index 000000000..415ef0ec9 --- /dev/null +++ b/beacon_node/client/src/config.rs @@ -0,0 +1,67 @@ +use clap::ArgMatches; +use http_server::HttpServerConfig; +use network::NetworkConfig; +use serde_derive::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +/// The core configuration of a Lighthouse beacon node. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + pub data_dir: PathBuf, + pub db_type: String, + db_name: String, + pub network: network::NetworkConfig, + pub rpc: rpc::RPCConfig, + pub http: HttpServerConfig, +} + +impl Default for Config { + fn default() -> Self { + Self { + data_dir: PathBuf::from(".lighthouse"), + db_type: "disk".to_string(), + db_name: "chain_db".to_string(), + // Note: there are no default bootnodes specified. + // Once bootnodes are established, add them here. + network: NetworkConfig::new(), + rpc: rpc::RPCConfig::default(), + http: HttpServerConfig::default(), + } + } +} + +impl Config { + /// Returns the path to which the client may initialize an on-disk database. + pub fn db_path(&self) -> Option { + self.data_dir() + .and_then(|path| Some(path.join(&self.db_name))) + } + + /// Returns the core path for the client. + pub fn data_dir(&self) -> Option { + let path = dirs::home_dir()?.join(&self.data_dir); + fs::create_dir_all(&path).ok()?; + Some(path) + } + + /// Apply the following arguments to `self`, replacing values if they are specified in `args`. + /// + /// Returns an error if arguments are obviously invalid. May succeed even if some values are + /// invalid. + pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), String> { + if let Some(dir) = args.value_of("datadir") { + self.data_dir = PathBuf::from(dir); + }; + + if let Some(dir) = args.value_of("db") { + self.db_type = dir.to_string(); + } + + self.network.apply_cli_args(args)?; + self.rpc.apply_cli_args(args)?; + self.http.apply_cli_args(args)?; + + Ok(()) + } +} diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index fd7162767..d66127df9 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" # SigP repository -libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "f018f5c443ed5a93de890048dbc6755393373e72" } -enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "f018f5c443ed5a93de890048dbc6755393373e72", features = ["serde"] } + libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "1c2aa97a338fc9dfd8e804cc47497fb9b8e7ad04" } +enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "1c2aa97a338fc9dfd8e804cc47497fb9b8e7ad04", features = ["serde"] } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index 00a8ed51e..cf9422520 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -1,9 +1,6 @@ use clap::ArgMatches; use enr::Enr; -use libp2p::{ - gossipsub::{GossipsubConfig, GossipsubConfigBuilder}, - multiaddr::Multiaddr, -}; +use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; use serde_derive::{Deserialize, Serialize}; use std::time::Duration; @@ -18,9 +15,12 @@ pub const SHARD_TOPIC_PREFIX: &str = "shard"; /// Network configuration for lighthouse. pub struct Config { /// IP address to listen on. - pub listen_addresses: Vec, + pub listen_address: std::net::IpAddr, - /// Specifies the IP address that the discovery protocol will listen on. + /// The TCP port that libp2p listens on. + pub libp2p_port: u16, + + /// The address to broadcast to peers about which address we are listening on. pub discovery_address: std::net::IpAddr, /// UDP port that discovery listens on. @@ -47,8 +47,9 @@ impl Default for Config { /// Generate a default network configuration. fn default() -> Self { Config { - listen_addresses: vec!["/ip4/127.0.0.1/tcp/9000".parse().expect("vaild multiaddr")], - discovery_address: "0.0.0.0".parse().expect("valid ip address"), + listen_address: "127.0.0.1".parse().expect("vaild ip address"), + libp2p_port: 9000, + discovery_address: "127.0.0.1".parse().expect("valid ip address"), discovery_port: 9000, max_peers: 10, //TODO: Set realistic values for production @@ -72,13 +73,11 @@ impl Config { pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), String> { if let Some(listen_address_str) = args.value_of("listen-address") { - self.listen_addresses = listen_address_str - .split(',') - .map(|a| { - a.parse::() - .map_err(|_| format!("Invalid Listen address: {:?}", a)) - }) - .collect::, _>>()?; + let listen_address = listen_address_str + .parse() + .map_err(|_| format!("Invalid listen address: {:?}", listen_address_str))?; + self.listen_address = listen_address; + self.discovery_address = listen_address; } if let Some(max_peers_str) = args.value_of("maxpeers") { @@ -87,10 +86,12 @@ impl Config { .map_err(|_| format!("Invalid number of max peers: {}", max_peers_str))?; } - if let Some(discovery_address_str) = args.value_of("disc-listen-address") { - self.discovery_address = discovery_address_str - .parse::() - .map_err(|_| format!("Invalid discovery address: {:?}", discovery_address_str))?; + if let Some(port_str) = args.value_of("port") { + let port = port_str + .parse::() + .map_err(|_| format!("Invalid port: {}", port_str))?; + self.libp2p_port = port; + self.discovery_port = port; } if let Some(boot_enr_str) = args.value_of("boot-nodes") { @@ -100,6 +101,12 @@ impl Config { .collect::, _>>()?; } + if let Some(discovery_address_str) = args.value_of("discovery-address") { + self.discovery_address = discovery_address_str + .parse() + .map_err(|_| format!("Invalid discovery address: {:?}", discovery_address_str))? + } + if let Some(disc_port_str) = args.value_of("disc-port") { self.discovery_port = disc_port_str .parse::() diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index 9a1e75691..1d4563552 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -11,7 +11,7 @@ use libp2p::core::{identity::Keypair, Multiaddr, PeerId, ProtocolsHandler}; use libp2p::discv5::{Discv5, Discv5Event}; use libp2p::enr::{Enr, EnrBuilder, NodeId}; use libp2p::multiaddr::Protocol; -use slog::{debug, error, info, o, warn}; +use slog::{debug, info, o, warn}; use std::collections::HashSet; use std::time::{Duration, Instant}; use tokio::io::{AsyncRead, AsyncWrite}; @@ -19,6 +19,8 @@ use tokio_timer::Delay; /// Maximum seconds before searching for extra peers. const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 60; +/// Initial delay between peer searches. +const INITIAL_SEARCH_DELAY: u64 = 5; /// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5 /// libp2p protocol. @@ -57,50 +59,27 @@ impl Discovery { let log = log.new(o!("Service" => "Libp2p-Discovery")); // Build the local ENR. - // The first TCP listening address is used for the ENR record. This will inform our peers to - // connect to this TCP port and establish libp2p streams. // Note: Discovery should update the ENR record's IP to the external IP as seen by the // majority of our peers. - let tcp_multiaddr = net_conf - .listen_addresses - .iter() - .filter(|a| { - if let Some(Protocol::Tcp(_)) = a.iter().last() { - true - } else { - false - } - }) - .next() - .ok_or_else(|| "No valid TCP addresses")?; - - let ip: std::net::IpAddr = match tcp_multiaddr.iter().next() { - Some(Protocol::Ip4(ip)) => ip.into(), - Some(Protocol::Ip6(ip)) => ip.into(), - _ => { - error!(log, "Multiaddr has an invalid IP address"); - return Err(format!("Invalid IP Address: {}", tcp_multiaddr).into()); - } - }; - - let tcp_port = match tcp_multiaddr.iter().last() { - Some(Protocol::Tcp(tcp)) => tcp, - _ => unreachable!(), - }; let local_enr = EnrBuilder::new() - .ip(ip.into()) - .tcp(tcp_port) + .ip(net_conf.discovery_address.into()) + .tcp(net_conf.libp2p_port) .udp(net_conf.discovery_port) .build(&local_key) .map_err(|e| format!("Could not build Local ENR: {:?}", e))?; info!(log, "Local ENR: {}", local_enr.to_base64()); - let mut discovery = Discv5::new(local_enr, local_key.clone(), net_conf.discovery_address) + let mut discovery = Discv5::new(local_enr, local_key.clone(), net_conf.listen_address) .map_err(|e| format!("Discv5 service failed: {:?}", e))?; // Add bootnodes to routing table for bootnode_enr in net_conf.boot_nodes.clone() { + debug!( + log, + "Adding node to routing table: {}", + bootnode_enr.node_id() + ); discovery.add_enr(bootnode_enr); } @@ -108,8 +87,8 @@ impl Discovery { connected_peers: HashSet::new(), max_peers: net_conf.max_peers, peer_discovery_delay: Delay::new(Instant::now()), - past_discovery_delay: 1, - tcp_port, + past_discovery_delay: INITIAL_SEARCH_DELAY, + tcp_port: net_conf.libp2p_port, discovery, log, }) @@ -118,7 +97,7 @@ impl Discovery { /// Manually search for peers. This restarts the discovery round, sparking multiple rapid /// queries. pub fn discover_peers(&mut self) { - self.past_discovery_delay = 1; + self.past_discovery_delay = INITIAL_SEARCH_DELAY; self.find_peers(); } @@ -203,7 +182,9 @@ where loop { match self.peer_discovery_delay.poll() { Ok(Async::Ready(_)) => { - self.find_peers(); + if self.connected_peers.len() < self.max_peers { + self.find_peers(); + } } Ok(Async::NotReady) => break, Err(e) => { @@ -217,16 +198,9 @@ where match self.discovery.poll(params) { Async::Ready(NetworkBehaviourAction::GenerateEvent(event)) => { match event { - Discv5Event::Discovered(enr) => { - debug!(self.log, "Discv5: Peer discovered"; "Peer"=> format!("{:?}", enr.peer_id()), "Addresses" => format!("{:?}", enr.multiaddr())); - - let peer_id = enr.peer_id(); - // if we need more peers, attempt a connection - if self.connected_peers.len() < self.max_peers - && self.connected_peers.get(&peer_id).is_none() - { - return Async::Ready(NetworkBehaviourAction::DialPeer { peer_id }); - } + Discv5Event::Discovered(_enr) => { + // not concerned about FINDNODE results, rather the result of an entire + // query. } Discv5Event::SocketUpdated(socket) => { info!(self.log, "Address updated"; "IP" => format!("{}",socket.ip())); @@ -241,6 +215,17 @@ where if closer_peers.is_empty() { debug!(self.log, "Discv5 random query yielded empty results"); } + for peer_id in closer_peers { + // if we need more peers, attempt a connection + if self.connected_peers.len() < self.max_peers + && self.connected_peers.get(&peer_id).is_none() + { + debug!(self.log, "Discv5: Peer discovered"; "Peer"=> format!("{:?}", peer_id)); + return Async::Ready(NetworkBehaviourAction::DialPeer { + peer_id, + }); + } + } } _ => {} } diff --git a/beacon_node/eth2-libp2p/src/service.rs b/beacon_node/eth2-libp2p/src/service.rs index 780b1453f..1db855cd4 100644 --- a/beacon_node/eth2-libp2p/src/service.rs +++ b/beacon_node/eth2-libp2p/src/service.rs @@ -9,6 +9,7 @@ use futures::prelude::*; use futures::Stream; use libp2p::core::{ identity, + multiaddr::Multiaddr, muxing::StreamMuxerBox, nodes::Substream, transport::boxed::Boxed, @@ -52,17 +53,24 @@ impl Service { Swarm::new(transport, behaviour, local_peer_id.clone()) }; - // listen on all addresses - for address in config.listen_addresses { - match Swarm::listen_on(&mut swarm, address.clone()) { - Ok(_) => { - let mut log_address = address.clone(); - log_address.push(Protocol::P2p(local_peer_id.clone().into())); - info!(log, "Listening on: {}", log_address); - } - Err(err) => warn!(log, "Cannot listen on: {} because: {:?}", address, err), - }; - } + // listen on the specified address + let listen_multiaddr = { + let mut m = Multiaddr::from(config.listen_address); + m.push(Protocol::Tcp(config.libp2p_port)); + m + }; + + match Swarm::listen_on(&mut swarm, listen_multiaddr.clone()) { + Ok(_) => { + let mut log_address = listen_multiaddr; + log_address.push(Protocol::P2p(local_peer_id.clone().into())); + info!(log, "Listening on: {}", log_address); + } + Err(err) => warn!( + log, + "Cannot listen on: {} because: {:?}", listen_multiaddr, err + ), + }; // subscribe to default gossipsub topics let mut topics = vec![]; diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 51d3a58f9..f7b92275a 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -33,15 +33,14 @@ fn main() { .arg( Arg::with_name("listen-address") .long("listen-address") - .value_name("Listen Address") - .help("One or more comma-delimited multi-addresses to listen for p2p connections.") + .value_name("Address") + .help("The address lighthouse will listen for UDP and TCP connections. (default 127.0.0.1).") .takes_value(true), ) .arg( Arg::with_name("maxpeers") .long("maxpeers") - .value_name("Max Peers") - .help("The maximum number of peers (default 10)") + .help("The maximum number of peers (default 10).") .takes_value(true), ) .arg( @@ -53,17 +52,24 @@ fn main() { .takes_value(true), ) .arg( - Arg::with_name("disc-listen-address") - .long("disc-listen_address") - .value_name("DISCPORT") - .help("The IP address that the discovery protocol will listen on. Defaults to 0.0.0.0") + Arg::with_name("port") + .long("port") + .value_name("Lighthouse Port") + .help("The TCP/UDP port to listen on. The UDP port can be modified by the --discovery-port flag.") .takes_value(true), ) .arg( Arg::with_name("discovery-port") .long("disc-port") - .value_name("DISCPORT") - .help("Listen UDP port for the discovery process") + .value_name("DiscoveryPort") + .help("The discovery UDP port.") + .takes_value(true), + ) + .arg( + Arg::with_name("discovery-address") + .long("discovery-address") + .value_name("Address") + .help("The address to broadcast to other peers on how to reach this node.") .takes_value(true), ) // rpc related arguments @@ -77,14 +83,13 @@ fn main() { .arg( Arg::with_name("rpc-address") .long("rpc-address") - .value_name("RPCADDRESS") + .value_name("Address") .help("Listen address for RPC endpoint.") .takes_value(true), ) .arg( Arg::with_name("rpc-port") .long("rpc-port") - .value_name("RPCPORT") .help("Listen port for RPC endpoint.") .takes_value(true), ) @@ -92,21 +97,19 @@ fn main() { .arg( Arg::with_name("http") .long("http") - .value_name("HTTP") .help("Enable the HTTP server.") .takes_value(false), ) .arg( Arg::with_name("http-address") .long("http-address") - .value_name("HTTPADDRESS") + .value_name("Address") .help("Listen address for the HTTP server.") .takes_value(true), ) .arg( Arg::with_name("http-port") .long("http-port") - .value_name("HTTPPORT") .help("Listen port for the HTTP server.") .takes_value(true), ) diff --git a/beacon_node/src/run.rs b/beacon_node/src/run.rs index 15883d974..51fa16154 100644 --- a/beacon_node/src/run.rs +++ b/beacon_node/src/run.rs @@ -41,6 +41,15 @@ pub fn run_beacon_node( "This software is EXPERIMENTAL and provides no guarantees or warranties." ); + info!( + log, + "Starting beacon node"; + "p2p_listen_address" => format!("{:?}", &other_client_config.network.listen_address), + "data_dir" => format!("{:?}", other_client_config.data_dir()), + "spec_constants" => &spec_constants, + "db_type" => &other_client_config.db_type, + ); + let result = match (db_type.as_str(), spec_constants.as_str()) { ("disk", "minimal") => run::>( &db_path, @@ -80,17 +89,6 @@ pub fn run_beacon_node( } }; - if result.is_ok() { - info!( - log, - "Started beacon node"; - "p2p_listen_addresses" => format!("{:?}", &other_client_config.network.listen_addresses), - "data_dir" => format!("{:?}", other_client_config.data_dir()), - "spec_constants" => &spec_constants, - "db_type" => &other_client_config.db_type, - ); - } - result } From 7dc5e2f959cb1e7b6a2725b46b60ca05b1144be4 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 25 Jun 2019 18:57:11 +1000 Subject: [PATCH 19/24] Update to latest libp2p --- beacon_node/eth2-libp2p/Cargo.toml | 4 ++-- beacon_node/eth2-libp2p/src/discovery.rs | 2 +- beacon_node/eth2-libp2p/src/rpc/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index d66127df9..550486e98 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" # SigP repository - libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "1c2aa97a338fc9dfd8e804cc47497fb9b8e7ad04" } -enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "1c2aa97a338fc9dfd8e804cc47497fb9b8e7ad04", features = ["serde"] } + libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "8ff9f2001de0aea1175c1442f22bfbf382dc0e8b" } +enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "8ff9f2001de0aea1175c1442f22bfbf382dc0e8b", features = ["serde"] } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index 1d4563552..b69f45be7 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -171,7 +171,7 @@ where fn poll( &mut self, - params: &mut PollParameters, + params: &mut impl PollParameters, ) -> Async< NetworkBehaviourAction< ::InEvent, diff --git a/beacon_node/eth2-libp2p/src/rpc/mod.rs b/beacon_node/eth2-libp2p/src/rpc/mod.rs index 57d7dadbe..2d303469c 100644 --- a/beacon_node/eth2-libp2p/src/rpc/mod.rs +++ b/beacon_node/eth2-libp2p/src/rpc/mod.rs @@ -94,7 +94,7 @@ where fn poll( &mut self, - _: &mut PollParameters<'_>, + _: &mut impl PollParameters, ) -> Async< NetworkBehaviourAction< ::InEvent, From af28d5e20cf5b8484ba55739e3ed04d008ec8142 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 1 Jul 2019 16:38:42 +1000 Subject: [PATCH 20/24] Add persistent network identification --- account_manager/Cargo.toml | 1 + account_manager/src/main.rs | 19 +++-- beacon_node/Cargo.toml | 1 + beacon_node/eth2-libp2p/Cargo.toml | 7 +- beacon_node/eth2-libp2p/src/config.rs | 14 +++ beacon_node/eth2-libp2p/src/discovery.rs | 103 +++++++++++++++++++---- beacon_node/eth2-libp2p/src/service.rs | 65 ++++++++++++-- beacon_node/network/src/service.rs | 6 -- beacon_node/src/main.rs | 41 +++++---- eth2/utils/eth2_config/src/lib.rs | 13 --- validator_client/Cargo.toml | 1 + validator_client/src/main.rs | 20 +++-- 12 files changed, 220 insertions(+), 71 deletions(-) diff --git a/account_manager/Cargo.toml b/account_manager/Cargo.toml index 48504d89a..b3c687eef 100644 --- a/account_manager/Cargo.toml +++ b/account_manager/Cargo.toml @@ -13,3 +13,4 @@ slog-async = "^2.3.0" validator_client = { path = "../validator_client" } types = { path = "../eth2/types" } eth2_config = { path = "../eth2/utils/eth2_config" } +dirs = "2.0.1" diff --git a/account_manager/src/main.rs b/account_manager/src/main.rs index 1c8cc8819..e242e8ae4 100644 --- a/account_manager/src/main.rs +++ b/account_manager/src/main.rs @@ -1,7 +1,7 @@ use bls::Keypair; use clap::{App, Arg, SubCommand}; -use eth2_config::get_data_dir; use slog::{crit, debug, info, o, Drain}; +use std::fs; use std::path::PathBuf; use types::test_utils::generate_deterministic_keypair; use validator_client::Config as ValidatorClientConfig; @@ -61,13 +61,22 @@ fn main() { ) .get_matches(); - let data_dir = match get_data_dir(&matches, PathBuf::from(DEFAULT_DATA_DIR)) { - Ok(dir) => dir, + let mut default_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + default_dir.push(DEFAULT_DATA_DIR); + + let data_dir = &matches + .value_of("datadir") + .and_then(|v| Some(PathBuf::from(v))) + .unwrap_or_else(|| PathBuf::from(default_dir)); + + // create the directory if needed + match fs::create_dir_all(&data_dir) { + Ok(_) => {} Err(e) => { - crit!(log, "Failed to initialize data dir"; "error" => format!("{:?}", e)); + crit!(log, "Failed to initialize data dir"; "error" => format!("{}", e)); return; } - }; + } let mut client_config = ValidatorClientConfig::default(); diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 7e43a13df..9e96f8484 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -23,3 +23,4 @@ futures = "0.1.25" exit-future = "0.1.3" state_processing = { path = "../eth2/state_processing" } env_logger = "0.6.1" +dirs = "2.0.1" diff --git a/beacon_node/eth2-libp2p/Cargo.toml b/beacon_node/eth2-libp2p/Cargo.toml index 550486e98..1fbd30872 100644 --- a/beacon_node/eth2-libp2p/Cargo.toml +++ b/beacon_node/eth2-libp2p/Cargo.toml @@ -7,9 +7,9 @@ edition = "2018" [dependencies] beacon_chain = { path = "../beacon_chain" } clap = "2.32.0" -# SigP repository - libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "8ff9f2001de0aea1175c1442f22bfbf382dc0e8b" } -enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "8ff9f2001de0aea1175c1442f22bfbf382dc0e8b", features = ["serde"] } +#SigP repository +libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "be5710bbde69d8c5be732c13ba64239e2f370a7b" } +enr = { git = "https://github.com/SigP/rust-libp2p/", rev = "be5710bbde69d8c5be732c13ba64239e2f370a7b", features = ["serde"] } types = { path = "../../eth2/types" } serde = "1.0" serde_derive = "1.0" @@ -21,3 +21,4 @@ tokio = "0.1.16" futures = "0.1.25" error-chain = "0.12.0" tokio-timer = "0.2.10" +dirs = "2.0.1" diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index cf9422520..e881408ea 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -2,6 +2,7 @@ use clap::ArgMatches; use enr::Enr; use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder}; use serde_derive::{Deserialize, Serialize}; +use std::path::PathBuf; use std::time::Duration; /// The beacon node topic string to subscribe to. @@ -14,6 +15,9 @@ pub const SHARD_TOPIC_PREFIX: &str = "shard"; #[serde(default)] /// Network configuration for lighthouse. pub struct Config { + /// Data directory where node's keyfile is stored + pub network_dir: PathBuf, + /// IP address to listen on. pub listen_address: std::net::IpAddr, @@ -46,7 +50,11 @@ pub struct Config { impl Default for Config { /// Generate a default network configuration. fn default() -> Self { + let mut network_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + network_dir.push(".lighthouse"); + network_dir.push("network"); Config { + network_dir, listen_address: "127.0.0.1".parse().expect("vaild ip address"), libp2p_port: 9000, discovery_address: "127.0.0.1".parse().expect("valid ip address"), @@ -72,6 +80,12 @@ impl Config { } pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), String> { + dbg!(self.network_dir.clone()); + if let Some(dir) = args.value_of("datadir") { + self.network_dir = PathBuf::from(dir).join("network"); + }; + dbg!(self.network_dir.clone()); + if let Some(listen_address_str) = args.value_of("listen-address") { let listen_address = listen_address_str .parse() diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index b69f45be7..104cd0285 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -1,7 +1,7 @@ use crate::{error, NetworkConfig}; /// This manages the discovery and management of peers. /// -/// Currently using Kademlia for peer discovery. +/// Currently using discv5 for peer discovery. /// use futures::prelude::*; use libp2p::core::swarm::{ @@ -13,6 +13,9 @@ use libp2p::enr::{Enr, EnrBuilder, NodeId}; use libp2p::multiaddr::Protocol; use slog::{debug, info, o, warn}; use std::collections::HashSet; +use std::fs::File; +use std::io::prelude::*; +use std::str::FromStr; use std::time::{Duration, Instant}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_timer::Delay; @@ -21,6 +24,8 @@ use tokio_timer::Delay; const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 60; /// Initial delay between peer searches. const INITIAL_SEARCH_DELAY: u64 = 5; +/// Local ENR storage filename. +const ENR_FILENAME: &str = "enr.dat"; /// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5 /// libp2p protocol. @@ -53,28 +58,22 @@ pub struct Discovery { impl Discovery { pub fn new( local_key: &Keypair, - net_conf: &NetworkConfig, + config: &NetworkConfig, log: &slog::Logger, ) -> error::Result { let log = log.new(o!("Service" => "Libp2p-Discovery")); - // Build the local ENR. - // Note: Discovery should update the ENR record's IP to the external IP as seen by the - // majority of our peers. + // checks if current ENR matches that found on disk + let local_enr = load_enr(local_key, config, &log)?; - let local_enr = EnrBuilder::new() - .ip(net_conf.discovery_address.into()) - .tcp(net_conf.libp2p_port) - .udp(net_conf.discovery_port) - .build(&local_key) - .map_err(|e| format!("Could not build Local ENR: {:?}", e))?; info!(log, "Local ENR: {}", local_enr.to_base64()); + debug!(log, "Local Node Id: {}", local_enr.node_id()); - let mut discovery = Discv5::new(local_enr, local_key.clone(), net_conf.listen_address) + let mut discovery = Discv5::new(local_enr, local_key.clone(), config.listen_address) .map_err(|e| format!("Discv5 service failed: {:?}", e))?; // Add bootnodes to routing table - for bootnode_enr in net_conf.boot_nodes.clone() { + for bootnode_enr in config.boot_nodes.clone() { debug!( log, "Adding node to routing table: {}", @@ -85,10 +84,10 @@ impl Discovery { Ok(Self { connected_peers: HashSet::new(), - max_peers: net_conf.max_peers, + max_peers: config.max_peers, peer_discovery_delay: Delay::new(Instant::now()), past_discovery_delay: INITIAL_SEARCH_DELAY, - tcp_port: net_conf.libp2p_port, + tcp_port: config.libp2p_port, discovery, log, }) @@ -238,3 +237,77 @@ where Async::NotReady } } + +/// Loads an ENR from file if it exists and matches the current NodeId and sequence number. If none +/// exists, generates a new one. +/// +/// If an ENR exists, with the same NodeId and IP addresses, we use the disk-generated one as it's +/// ENR sequence will be equal or higher than a newly generated one. +fn load_enr( + local_key: &Keypair, + config: &NetworkConfig, + log: &slog::Logger, +) -> Result { + // Build the local ENR. + // Note: Discovery should update the ENR record's IP to the external IP as seen by the + // majority of our peers. + let mut local_enr = EnrBuilder::new() + .ip(config.discovery_address.into()) + .tcp(config.libp2p_port) + .udp(config.discovery_port) + .build(&local_key) + .map_err(|e| format!("Could not build Local ENR: {:?}", e))?; + + let enr_f = config.network_dir.join(ENR_FILENAME); + if let Ok(mut enr_file) = File::open(enr_f.clone()) { + let mut enr_string = String::new(); + match enr_file.read_to_string(&mut enr_string) { + Err(_) => debug!(log, "Could not read ENR from file"), + Ok(_) => { + match Enr::from_str(&enr_string) { + Ok(enr) => { + debug!(log, "ENR found in file: {:?}", enr_f); + + if enr.node_id() == local_enr.node_id() { + if enr.ip() == config.discovery_address.into() + && enr.tcp() == Some(config.libp2p_port) + && enr.udp() == Some(config.discovery_port) + { + debug!(log, "ENR loaded from file"); + // the stored ENR has the same configuration, use it + return Ok(enr); + } + + // same node id, different configuration - update the sequence number + let new_seq_no = enr.seq().checked_add(1).ok_or_else(|| "ENR sequence number on file is too large. Remove it to generate a new NodeId")?; + local_enr.set_seq(new_seq_no, local_key).map_err(|e| { + format!("Could not update ENR sequence number: {:?}", e) + })?; + debug!(log, "ENR sequence number increased to: {}", new_seq_no); + } + } + Err(e) => { + warn!(log, "ENR from file could not be decoded: {:?}", e); + } + } + } + } + } + + // write ENR to disk + let _ = std::fs::create_dir_all(&config.network_dir); + match File::create(enr_f.clone()) + .and_then(|mut f| f.write_all(&local_enr.to_base64().as_bytes())) + { + Ok(_) => { + debug!(log, "ENR written to disk"); + } + Err(e) => { + warn!( + log, + "Could not write ENR to file: {:?}. Error: {}", enr_f, e + ); + } + } + Ok(local_enr) +} diff --git a/beacon_node/eth2-libp2p/src/service.rs b/beacon_node/eth2-libp2p/src/service.rs index 1db855cd4..69f8a1ca5 100644 --- a/beacon_node/eth2-libp2p/src/service.rs +++ b/beacon_node/eth2-libp2p/src/service.rs @@ -8,22 +8,25 @@ use crate::{BEACON_ATTESTATION_TOPIC, BEACON_PUBSUB_TOPIC}; use futures::prelude::*; use futures::Stream; use libp2p::core::{ - identity, + identity::Keypair, multiaddr::Multiaddr, muxing::StreamMuxerBox, nodes::Substream, transport::boxed::Boxed, upgrade::{InboundUpgradeExt, OutboundUpgradeExt}, }; -use libp2p::identify::protocol::IdentifyInfo; use libp2p::{core, secio, PeerId, Swarm, Transport}; use slog::{debug, info, trace, warn}; +use std::fs::File; +use std::io::prelude::*; use std::io::{Error, ErrorKind}; use std::time::Duration; type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>; type Libp2pBehaviour = Behaviour>; +const NETWORK_KEY_FILENAME: &str = "key"; + /// The configuration and state of the libp2p components for the beacon node. pub struct Service { /// The libp2p Swarm handler. @@ -39,9 +42,9 @@ impl Service { pub fn new(config: NetworkConfig, log: slog::Logger) -> error::Result { debug!(log, "Network-libp2p Service starting"); - // TODO: Save and recover node key from disk - // TODO: Currently using secp256k1 keypairs - currently required for discv5 - let local_private_key = identity::Keypair::generate_secp256k1(); + // load the private key from CLI flag, disk or generate a new one + let local_private_key = load_private_key(&config, &log); + let local_peer_id = PeerId::from(local_private_key.public()); info!(log, "Local peer id: {:?}", local_peer_id); @@ -142,7 +145,7 @@ impl Stream for Service { /// The implementation supports TCP/IP, WebSockets over TCP/IP, secio as the encryption layer, and /// mplex or yamux as the multiplexing layer. -fn build_transport(local_private_key: identity::Keypair) -> Boxed<(PeerId, StreamMuxerBox), Error> { +fn build_transport(local_private_key: Keypair) -> Boxed<(PeerId, StreamMuxerBox), Error> { // TODO: The Wire protocol currently doesn't specify encryption and this will need to be customised // in the future. let transport = libp2p::tcp::TcpConfig::new(); @@ -179,8 +182,6 @@ pub enum Libp2pEvent { RPC(PeerId, RPCEvent), /// Initiated the connection to a new peer. PeerDialed(PeerId), - /// Received information about a peer on the network. - Identified(PeerId, Box), /// Received pubsub message. PubsubMessage { source: PeerId, @@ -188,3 +189,51 @@ pub enum Libp2pEvent { message: Box, }, } + +/// Loads a private key from disk. If this fails, a new key is +/// generated and is then saved to disk. +/// +/// Currently only secp256k1 keys are allowed, as these are the only keys supported by discv5. +fn load_private_key(config: &NetworkConfig, log: &slog::Logger) -> Keypair { + // TODO: Currently using secp256k1 keypairs - currently required for discv5 + // check for key from disk + let network_key_f = config.network_dir.join(NETWORK_KEY_FILENAME); + if let Ok(mut network_key_file) = File::open(network_key_f.clone()) { + let mut key_bytes: Vec = Vec::with_capacity(36); + match network_key_file.read_to_end(&mut key_bytes) { + Err(_) => debug!(log, "Could not read network key file"), + Ok(_) => { + // only accept secp256k1 keys for now + if let Ok(secret_key) = + libp2p::core::identity::secp256k1::SecretKey::from_bytes(&mut key_bytes) + { + let kp: libp2p::core::identity::secp256k1::Keypair = secret_key.into(); + debug!(log, "Loaded network key from disk."); + return Keypair::Secp256k1(kp); + } else { + debug!(log, "Network key file is not a valid secp256k1 key"); + } + } + } + } + + // if a key could not be loaded from disk, generate a new one and save it + let local_private_key = Keypair::generate_secp256k1(); + if let Keypair::Secp256k1(key) = local_private_key.clone() { + let _ = std::fs::create_dir_all(&config.network_dir); + match File::create(network_key_f.clone()) + .and_then(|mut f| f.write_all(&key.secret().to_bytes())) + { + Ok(_) => { + debug!(log, "New network key generated and written to disk"); + } + Err(e) => { + warn!( + log, + "Could not write node key to file: {:?}. Error: {}", network_key_f, e + ); + } + } + } + local_private_key +} diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index c19aef004..b2ecc1a0b 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -126,12 +126,6 @@ fn network_service( .send(HandlerMessage::PeerDialed(peer_id)) .map_err(|_| "failed to send rpc to handler")?; } - Libp2pEvent::Identified(peer_id, info) => { - debug!( - log, - "We have identified peer: {:?} with {:?}", peer_id, info - ); - } Libp2pEvent::PubsubMessage { source, message, .. } => { diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index f7b92275a..651ad8e0c 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -3,8 +3,9 @@ mod run; use clap::{App, Arg}; use client::{ClientConfig, Eth2Config}; use env_logger::{Builder, Env}; -use eth2_config::{get_data_dir, read_from_file, write_to_file}; +use eth2_config::{read_from_file, write_to_file}; use slog::{crit, o, Drain, Level}; +use std::fs; use std::path::PathBuf; pub const DEFAULT_DATA_DIR: &str = ".lighthouse"; @@ -27,7 +28,6 @@ fn main() { .value_name("DIR") .help("Data directory for keys and databases.") .takes_value(true) - .default_value(DEFAULT_DATA_DIR), ) // network related arguments .arg( @@ -69,7 +69,7 @@ fn main() { Arg::with_name("discovery-address") .long("discovery-address") .value_name("Address") - .help("The address to broadcast to other peers on how to reach this node.") + .help("The IP address to broadcast to other peers on how to reach this node.") .takes_value(true), ) // rpc related arguments @@ -159,15 +159,24 @@ fn main() { _ => drain.filter_level(Level::Info), }; - let logger = slog::Logger::root(drain.fuse(), o!()); + let log = slog::Logger::root(drain.fuse(), o!()); - let data_dir = match get_data_dir(&matches, PathBuf::from(DEFAULT_DATA_DIR)) { - Ok(dir) => dir, + let mut default_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + default_dir.push(DEFAULT_DATA_DIR); + + let data_dir = &matches + .value_of("datadir") + .and_then(|v| Some(PathBuf::from(v))) + .unwrap_or_else(|| PathBuf::from(default_dir)); + + // create the directory if needed + match fs::create_dir_all(&data_dir) { + Ok(_) => {} Err(e) => { - crit!(logger, "Failed to initialize data dir"; "error" => format!("{:?}", e)); + crit!(log, "Failed to initialize data dir"; "error" => format!("{}", e)); return; } - }; + } let client_config_path = data_dir.join(CLIENT_CONFIG_FILENAME); @@ -179,13 +188,13 @@ fn main() { Ok(None) => { let default = ClientConfig::default(); if let Err(e) = write_to_file(client_config_path, &default) { - crit!(logger, "Failed to write default ClientConfig to file"; "error" => format!("{:?}", e)); + crit!(log, "Failed to write default ClientConfig to file"; "error" => format!("{:?}", e)); return; } default } Err(e) => { - crit!(logger, "Failed to load a ChainConfig file"; "error" => format!("{:?}", e)); + crit!(log, "Failed to load a ChainConfig file"; "error" => format!("{:?}", e)); return; } }; @@ -197,7 +206,7 @@ fn main() { match client_config.apply_cli_args(&matches) { Ok(()) => (), Err(s) => { - crit!(logger, "Failed to parse ClientConfig CLI arguments"; "error" => s); + crit!(log, "Failed to parse ClientConfig CLI arguments"; "error" => s); return; } }; @@ -216,13 +225,13 @@ fn main() { _ => unreachable!(), // Guarded by slog. }; if let Err(e) = write_to_file(eth2_config_path, &default) { - crit!(logger, "Failed to write default Eth2Config to file"; "error" => format!("{:?}", e)); + crit!(log, "Failed to write default Eth2Config to file"; "error" => format!("{:?}", e)); return; } default } Err(e) => { - crit!(logger, "Failed to load/generate an Eth2Config"; "error" => format!("{:?}", e)); + crit!(log, "Failed to load/generate an Eth2Config"; "error" => format!("{:?}", e)); return; } }; @@ -231,13 +240,13 @@ fn main() { match eth2_config.apply_cli_args(&matches) { Ok(()) => (), Err(s) => { - crit!(logger, "Failed to parse Eth2Config CLI arguments"; "error" => s); + crit!(log, "Failed to parse Eth2Config CLI arguments"; "error" => s); return; } }; - match run::run_beacon_node(client_config, eth2_config, &logger) { + match run::run_beacon_node(client_config, eth2_config, &log) { Ok(_) => {} - Err(e) => crit!(logger, "Beacon node failed to start"; "reason" => format!("{:}", e)), + Err(e) => crit!(log, "Beacon node failed to start"; "reason" => format!("{:}", e)), } } diff --git a/eth2/utils/eth2_config/src/lib.rs b/eth2/utils/eth2_config/src/lib.rs index 9d50a95c1..f6ad54c21 100644 --- a/eth2/utils/eth2_config/src/lib.rs +++ b/eth2/utils/eth2_config/src/lib.rs @@ -1,6 +1,5 @@ use clap::ArgMatches; use serde_derive::{Deserialize, Serialize}; -use std::fs; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; @@ -105,15 +104,3 @@ where Ok(None) } } - -pub fn get_data_dir(args: &ArgMatches, default_data_dir: PathBuf) -> Result { - if let Some(data_dir) = args.value_of("data_dir") { - Ok(PathBuf::from(data_dir)) - } else { - let path = dirs::home_dir() - .ok_or_else(|| "Unable to locate home directory")? - .join(&default_data_dir); - fs::create_dir_all(&path).map_err(|_| "Unable to create data_dir")?; - Ok(path) - } -} diff --git a/validator_client/Cargo.toml b/validator_client/Cargo.toml index fdb4c33c0..1972f870c 100644 --- a/validator_client/Cargo.toml +++ b/validator_client/Cargo.toml @@ -34,3 +34,4 @@ toml = "^0.5" error-chain = "0.12.0" bincode = "^1.1.2" futures = "0.1.25" +dirs = "2.0.1" diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index f74915438..43ef2f994 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -9,9 +9,10 @@ mod signer; use crate::config::Config as ValidatorClientConfig; use crate::service::Service as ValidatorService; use clap::{App, Arg}; -use eth2_config::{get_data_dir, read_from_file, write_to_file, Eth2Config}; +use eth2_config::{read_from_file, write_to_file, Eth2Config}; use protos::services_grpc::ValidatorServiceClient; use slog::{crit, error, info, o, Drain}; +use std::fs; use std::path::PathBuf; use types::{Keypair, MainnetEthSpec, MinimalEthSpec}; @@ -66,13 +67,22 @@ fn main() { ) .get_matches(); - let data_dir = match get_data_dir(&matches, PathBuf::from(DEFAULT_DATA_DIR)) { - Ok(dir) => dir, + let mut default_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + default_dir.push(DEFAULT_DATA_DIR); + + let data_dir = &matches + .value_of("datadir") + .and_then(|v| Some(PathBuf::from(v))) + .unwrap_or_else(|| PathBuf::from(default_dir)); + + // create the directory if needed + match fs::create_dir_all(&data_dir) { + Ok(_) => {} Err(e) => { - crit!(log, "Failed to initialize data dir"; "error" => format!("{:?}", e)); + crit!(log, "Failed to initialize data dir"; "error" => format!("{}", e)); return; } - }; + } let client_config_path = data_dir.join(CLIENT_CONFIG_FILENAME); From 177daf26094a04d740d4d58b37d78a045371cd19 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 1 Jul 2019 16:40:35 +1000 Subject: [PATCH 21/24] Typo fixes --- beacon_node/eth2-libp2p/src/discovery.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon_node/eth2-libp2p/src/discovery.rs b/beacon_node/eth2-libp2p/src/discovery.rs index 104cd0285..44b4e655b 100644 --- a/beacon_node/eth2-libp2p/src/discovery.rs +++ b/beacon_node/eth2-libp2p/src/discovery.rs @@ -177,7 +177,7 @@ where Self::OutEvent, >, > { - // search of peers if it is time + // search for peers if it is time loop { match self.peer_discovery_delay.poll() { Ok(Async::Ready(_)) => { @@ -241,7 +241,7 @@ where /// Loads an ENR from file if it exists and matches the current NodeId and sequence number. If none /// exists, generates a new one. /// -/// If an ENR exists, with the same NodeId and IP addresses, we use the disk-generated one as it's +/// If an ENR exists, with the same NodeId and IP address, we use the disk-generated one as its /// ENR sequence will be equal or higher than a newly generated one. fn load_enr( local_key: &Keypair, From bffe6c327f19bf91de45199cb8e78ec3b9ee4fc3 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Mon, 1 Jul 2019 17:23:14 +1000 Subject: [PATCH 22/24] Removes left-over debugging statements --- beacon_node/eth2-libp2p/src/config.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index e881408ea..c5e02d5e3 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -80,11 +80,9 @@ impl Config { } pub fn apply_cli_args(&mut self, args: &ArgMatches) -> Result<(), String> { - dbg!(self.network_dir.clone()); if let Some(dir) = args.value_of("datadir") { self.network_dir = PathBuf::from(dir).join("network"); }; - dbg!(self.network_dir.clone()); if let Some(listen_address_str) = args.value_of("listen-address") { let listen_address = listen_address_str From dd410535cb8a2676592369f179616567ce5b5f9d Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 2 Jul 2019 11:15:35 +1000 Subject: [PATCH 23/24] Remove Phase 1 TODO --- beacon_node/eth2-libp2p/src/config.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/beacon_node/eth2-libp2p/src/config.rs b/beacon_node/eth2-libp2p/src/config.rs index c5e02d5e3..ea87075b7 100644 --- a/beacon_node/eth2-libp2p/src/config.rs +++ b/beacon_node/eth2-libp2p/src/config.rs @@ -8,7 +8,6 @@ use std::time::Duration; /// The beacon node topic string to subscribe to. pub const BEACON_PUBSUB_TOPIC: &str = "beacon_block"; pub const BEACON_ATTESTATION_TOPIC: &str = "beacon_attestation"; -//TODO: Implement shard subnets pub const SHARD_TOPIC_PREFIX: &str = "shard"; #[derive(Clone, Debug, Serialize, Deserialize)] From 1aeec12b780fe8d66f3deef9eb841cc284987ab5 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Tue, 2 Jul 2019 17:32:14 +1000 Subject: [PATCH 24/24] Improve error handling of default directory --- account_manager/src/main.rs | 21 ++++++++++++++++----- beacon_node/eth2-libp2p/src/rpc/protocol.rs | 1 - beacon_node/src/main.rs | 21 ++++++++++++++++----- validator_client/src/main.rs | 21 ++++++++++++++++----- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/account_manager/src/main.rs b/account_manager/src/main.rs index e242e8ae4..ee0e86d60 100644 --- a/account_manager/src/main.rs +++ b/account_manager/src/main.rs @@ -61,13 +61,24 @@ fn main() { ) .get_matches(); - let mut default_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - default_dir.push(DEFAULT_DATA_DIR); - - let data_dir = &matches + let data_dir = match matches .value_of("datadir") .and_then(|v| Some(PathBuf::from(v))) - .unwrap_or_else(|| PathBuf::from(default_dir)); + { + Some(v) => v, + None => { + // use the default + let mut default_dir = match dirs::home_dir() { + Some(v) => v, + None => { + crit!(log, "Failed to find a home directory"); + return; + } + }; + default_dir.push(DEFAULT_DATA_DIR); + PathBuf::from(default_dir) + } + }; // create the directory if needed match fs::create_dir_all(&data_dir) { diff --git a/beacon_node/eth2-libp2p/src/rpc/protocol.rs b/beacon_node/eth2-libp2p/src/rpc/protocol.rs index 2f461988a..7afded3ac 100644 --- a/beacon_node/eth2-libp2p/src/rpc/protocol.rs +++ b/beacon_node/eth2-libp2p/src/rpc/protocol.rs @@ -11,7 +11,6 @@ use tokio::io::{AsyncRead, AsyncWrite}; const MAX_READ_SIZE: usize = 4_194_304; // 4M /// Implementation of the `ConnectionUpgrade` for the rpc protocol. - #[derive(Debug, Clone)] pub struct RPCProtocol; diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 651ad8e0c..791feae54 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -161,13 +161,24 @@ fn main() { let log = slog::Logger::root(drain.fuse(), o!()); - let mut default_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - default_dir.push(DEFAULT_DATA_DIR); - - let data_dir = &matches + let data_dir = match matches .value_of("datadir") .and_then(|v| Some(PathBuf::from(v))) - .unwrap_or_else(|| PathBuf::from(default_dir)); + { + Some(v) => v, + None => { + // use the default + let mut default_dir = match dirs::home_dir() { + Some(v) => v, + None => { + crit!(log, "Failed to find a home directory"); + return; + } + }; + default_dir.push(DEFAULT_DATA_DIR); + PathBuf::from(default_dir) + } + }; // create the directory if needed match fs::create_dir_all(&data_dir) { diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index 43ef2f994..e37b6530e 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -67,13 +67,24 @@ fn main() { ) .get_matches(); - let mut default_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - default_dir.push(DEFAULT_DATA_DIR); - - let data_dir = &matches + let data_dir = match matches .value_of("datadir") .and_then(|v| Some(PathBuf::from(v))) - .unwrap_or_else(|| PathBuf::from(default_dir)); + { + Some(v) => v, + None => { + // use the default + let mut default_dir = match dirs::home_dir() { + Some(v) => v, + None => { + crit!(log, "Failed to find a home directory"); + return; + } + }; + default_dir.push(DEFAULT_DATA_DIR); + PathBuf::from(default_dir) + } + }; // create the directory if needed match fs::create_dir_all(&data_dir) {