From ce37f958612229370791ae170e85780a07362656 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Thu, 15 Aug 2019 16:41:02 +1000 Subject: [PATCH] Allow bootstrapper to scrape libp2p address --- beacon_node/client/Cargo.toml | 1 + beacon_node/client/src/bootstrapper.rs | 29 +++++++++++++++++- beacon_node/client/src/config.rs | 42 ++++++++++++++++++++++++-- beacon_node/eth2-libp2p/src/lib.rs | 2 +- beacon_node/network/src/service.rs | 14 ++++++++- beacon_node/rest_api/src/lib.rs | 3 ++ beacon_node/rest_api/src/network.rs | 21 ++++++++++++- beacon_node/src/main.rs | 28 +---------------- 8 files changed, 107 insertions(+), 33 deletions(-) diff --git a/beacon_node/client/Cargo.toml b/beacon_node/client/Cargo.toml index 9d5d49e17..9b5a9cf42 100644 --- a/beacon_node/client/Cargo.toml +++ b/beacon_node/client/Cargo.toml @@ -28,3 +28,4 @@ dirs = "1.0.3" exit-future = "0.1.3" futures = "0.1.25" reqwest = "0.9" +url = "1.2" diff --git a/beacon_node/client/src/bootstrapper.rs b/beacon_node/client/src/bootstrapper.rs index 9537f6f90..1fd8f1659 100644 --- a/beacon_node/client/src/bootstrapper.rs +++ b/beacon_node/client/src/bootstrapper.rs @@ -1,6 +1,8 @@ -use eth2_libp2p::Enr; +use eth2_libp2p::{Enr, Multiaddr}; use reqwest::{Error as HttpError, Url}; +use std::net::Ipv4Addr; use types::{BeaconBlock, BeaconState, Checkpoint, EthSpec, Slot}; +use url::Host; #[derive(Debug)] enum Error { @@ -25,10 +27,22 @@ impl Bootstrapper { }) } + pub fn server_ipv4_addr(&self) -> Option { + match self.url.host()? { + Host::Ipv4(addr) => Some(addr), + _ => None, + } + } + pub fn enr(&self) -> Result { get_enr(self.url.clone()).map_err(|e| format!("Unable to get ENR: {:?}", e)) } + pub fn listen_addresses(&self) -> Result, String> { + get_listen_addresses(self.url.clone()) + .map_err(|e| format!("Unable to get listen addresses: {:?}", e)) + } + pub fn genesis(&self) -> Result<(BeaconState, BeaconBlock), String> { let genesis_slot = Slot::new(0); @@ -124,3 +138,16 @@ fn get_enr(mut url: Url) -> Result { .json() .map_err(Into::into) } + +fn get_listen_addresses(mut url: Url) -> Result, Error> { + url.path_segments_mut() + .map(|mut url| { + url.push("node").push("network").push("listen_addresses"); + }) + .map_err(|_| Error::UrlCannotBeBase)?; + + reqwest::get(url)? + .error_for_status()? + .json() + .map_err(Into::into) +} diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs index 0d5d5f81d..5dd0eef52 100644 --- a/beacon_node/client/src/config.rs +++ b/beacon_node/client/src/config.rs @@ -1,8 +1,9 @@ -use crate::Eth2Config; +use crate::{Bootstrapper, Eth2Config}; use clap::ArgMatches; +use eth2_libp2p::multiaddr::{Multiaddr, Protocol}; use network::NetworkConfig; use serde_derive::{Deserialize, Serialize}; -use slog::{info, o, Drain}; +use slog::{info, o, warn, Drain}; use std::fs::{self, OpenOptions}; use std::path::PathBuf; use std::sync::Mutex; @@ -149,6 +150,43 @@ impl Config { self.update_logger(log)?; }; + // If the `--bootstrap` flag is provided, overwrite the default configuration. + if let Some(server) = args.value_of("bootstrap") { + do_bootstrapping(self, server.to_string(), &log)?; + } + Ok(()) } } + +fn do_bootstrapping(config: &mut Config, server: String, log: &slog::Logger) -> Result<(), String> { + // Set the genesis state source. + config.genesis_state = GenesisState::HttpBootstrap { + server: server.to_string(), + }; + + let bootstrapper = Bootstrapper::from_server_string(server.to_string())?; + + config.network.boot_nodes.push(bootstrapper.enr()?); + + if let Some(server_ip) = bootstrapper.server_ipv4_addr() { + let server_multiaddr: Multiaddr = bootstrapper + .listen_addresses()? + .first() + .ok_or_else(|| "Bootstrap peer returned an empty list of listen addresses")? + // Iterate through the components of the Multiaddr, replacing any Ipv4 address with the + // server address. + .iter() + .map(|protocol| match protocol { + Protocol::Ip4(_) => Protocol::Ip4(server_ip), + _ => protocol, + }) + .collect::(); + + config.network.libp2p_nodes.push(server_multiaddr); + } else { + warn!(log, "Unable to determine bootstrap server Ipv4 address. Unable to add server as libp2p peer."); + } + + Ok(()) +} diff --git a/beacon_node/eth2-libp2p/src/lib.rs b/beacon_node/eth2-libp2p/src/lib.rs index 8c2644fbb..4c84469ce 100644 --- a/beacon_node/eth2-libp2p/src/lib.rs +++ b/beacon_node/eth2-libp2p/src/lib.rs @@ -23,7 +23,7 @@ pub use libp2p::multiaddr; pub use libp2p::Multiaddr; pub use libp2p::{ gossipsub::{GossipsubConfig, GossipsubConfigBuilder}, - PeerId, + PeerId, Swarm, }; pub use rpc::RPCEvent; pub use service::Libp2pEvent; diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index ed3c9da0b..4bec03830 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -5,7 +5,7 @@ use beacon_chain::{BeaconChain, BeaconChainTypes}; use core::marker::PhantomData; use eth2_libp2p::Service as LibP2PService; use eth2_libp2p::Topic; -use eth2_libp2p::{Enr, Libp2pEvent, PeerId}; +use eth2_libp2p::{Enr, Libp2pEvent, Multiaddr, PeerId, Swarm}; use eth2_libp2p::{PubsubMessage, RPCEvent}; use futures::prelude::*; use futures::Stream; @@ -64,6 +64,8 @@ impl Service { Ok((Arc::new(network_service), network_send)) } + /// Returns the local ENR from the underlying Discv5 behaviour that external peers may connect + /// to. pub fn local_enr(&self) -> Enr { self.libp2p_service .lock() @@ -73,10 +75,19 @@ impl Service { .clone() } + /// Returns the list of `Multiaddr` that the underlying libp2p instance is listening on. + pub fn listen_multiaddrs(&self) -> Vec { + Swarm::listeners(&self.libp2p_service.lock().swarm) + .cloned() + .collect() + } + + /// Returns the number of libp2p connected peers. pub fn connected_peers(&self) -> usize { self.libp2p_service.lock().swarm.connected_peers() } + /// Returns the set of `PeerId` that are connected via libp2p. pub fn connected_peer_set(&self) -> Vec { self.libp2p_service .lock() @@ -88,6 +99,7 @@ impl Service { .collect() } + /// Provides a reference to the underlying libp2p service. pub fn libp2p_service(&self) -> Arc> { self.libp2p_service.clone() } diff --git a/beacon_node/rest_api/src/lib.rs b/beacon_node/rest_api/src/lib.rs index 349a62c3f..8ef48ad72 100644 --- a/beacon_node/rest_api/src/lib.rs +++ b/beacon_node/rest_api/src/lib.rs @@ -135,6 +135,9 @@ pub fn start_server( (&Method::GET, "/node/network/enr") => network::get_enr::(req), (&Method::GET, "/node/network/peer_count") => network::get_peer_count::(req), (&Method::GET, "/node/network/peers") => network::get_peer_list::(req), + (&Method::GET, "/node/network/listen_addresses") => { + network::get_listen_addresses::(req) + } (&Method::GET, "/spec") => spec::get_spec::(req), (&Method::GET, "/spec/slots_per_epoch") => spec::get_slots_per_epoch::(req), _ => Err(ApiError::MethodNotAllowed(path.clone())), diff --git a/beacon_node/rest_api/src/network.rs b/beacon_node/rest_api/src/network.rs index 2fd88f498..0e2448270 100644 --- a/beacon_node/rest_api/src/network.rs +++ b/beacon_node/rest_api/src/network.rs @@ -1,9 +1,28 @@ use crate::{success_response, ApiError, ApiResult, NetworkService}; use beacon_chain::BeaconChainTypes; -use eth2_libp2p::{Enr, PeerId}; +use eth2_libp2p::{Enr, Multiaddr, PeerId}; use hyper::{Body, Request}; use std::sync::Arc; +/// HTTP handle to return the list of libp2p multiaddr the client is listening on. +/// +/// Returns a list of `Multiaddr`, serialized according to their `serde` impl. +pub fn get_listen_addresses( + req: Request, +) -> ApiResult { + let network = req + .extensions() + .get::>>() + .ok_or_else(|| ApiError::ServerError("NetworkService extension missing".to_string()))?; + + let multiaddresses: Vec = network.listen_multiaddrs(); + + Ok(success_response(Body::from( + serde_json::to_string(&multiaddresses) + .map_err(|e| ApiError::ServerError(format!("Unable to serialize Enr: {:?}", e)))?, + ))) +} + /// HTTP handle to return the Discv5 ENR from the client's libp2p service. /// /// ENR is encoded as base64 string. diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 5199bddb6..ae48f692b 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -1,7 +1,7 @@ mod run; use clap::{App, Arg}; -use client::{Bootstrapper, ClientConfig, Eth2Config, GenesisState}; +use client::{ClientConfig, Eth2Config}; use env_logger::{Builder, Env}; use eth2_config::{read_from_file, write_to_file}; use slog::{crit, o, warn, Drain, Level}; @@ -298,32 +298,6 @@ fn main() { } }; - // If the `--bootstrap` flag is provided, overwrite the default configuration. - if let Some(server) = matches.value_of("bootstrap") { - // Set the genesis state source. - client_config.genesis_state = GenesisState::HttpBootstrap { - server: server.to_string(), - }; - - let bootstrapper = match Bootstrapper::from_server_string(server.to_string()) { - Ok(b) => b, - Err(e) => { - crit!(log, "Failed to load bootstrapper"; "error" => format!("{:?}", e)); - return; - } - }; - - let enr = match bootstrapper.enr() { - Ok(b) => b, - Err(e) => { - crit!(log, "Failed to read ENR from bootstrap server"; "error" => format!("{:?}", e)); - return; - } - }; - - client_config.network.boot_nodes.push(enr); - } - let eth2_config_path = data_dir.join(ETH2_CONFIG_FILENAME); // Initialise the `Eth2Config`.