From 90d63a46c7674ece2dc5f6465c5300af900e33cd Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Tue, 19 Nov 2019 13:29:29 +1100 Subject: [PATCH] Add beacon_chain_sim --- Cargo.toml | 1 + beacon_node/client/src/lib.rs | 11 +++++ lighthouse/environment/src/lib.rs | 2 +- tests/beacon_chain_sim/Cargo.toml | 11 +++++ tests/beacon_chain_sim/src/main.rs | 77 ++++++++++++++++++++++++++++++ tests/node_test_rig/src/lib.rs | 26 +++++----- 6 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 tests/beacon_chain_sim/Cargo.toml create mode 100644 tests/beacon_chain_sim/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 6fc81d267f..e4c029951c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ members = [ "beacon_node/eth1", "beacon_node/beacon_chain", "beacon_node/websocket_server", + "tests/beacon_chain_sim", "tests/ef_tests", "tests/eth1_test_rig", "tests/node_test_rig", diff --git a/beacon_node/client/src/lib.rs b/beacon_node/client/src/lib.rs index 5da442bb10..f0ed63e485 100644 --- a/beacon_node/client/src/lib.rs +++ b/beacon_node/client/src/lib.rs @@ -6,6 +6,7 @@ pub mod builder; pub mod error; use beacon_chain::BeaconChain; +use eth2_libp2p::{Enr, Multiaddr}; use exit_future::Signal; use network::Service as NetworkService; use std::net::SocketAddr; @@ -48,6 +49,16 @@ impl Client { pub fn libp2p_listen_port(&self) -> Option { self.libp2p_network.as_ref().map(|n| n.listen_port()) } + + /// Returns the list of libp2p addresses the client is listening to. + pub fn libp2p_listen_addresses(&self) -> Option> { + self.libp2p_network.as_ref().map(|n| n.listen_multiaddrs()) + } + + /// Returns the local libp2p ENR of this node, for network discovery. + pub fn enr(&self) -> Option { + self.libp2p_network.as_ref().map(|n| n.local_enr()) + } } impl Drop for Client { diff --git a/lighthouse/environment/src/lib.rs b/lighthouse/environment/src/lib.rs index 69002682a3..631e799271 100644 --- a/lighthouse/environment/src/lib.rs +++ b/lighthouse/environment/src/lib.rs @@ -193,7 +193,7 @@ impl Environment { } /// Returns a `Context` where the `service_name` is added to the logger output. - pub fn service_context(&mut self, service_name: &'static str) -> RuntimeContext { + pub fn service_context(&mut self, service_name: String) -> RuntimeContext { RuntimeContext { executor: self.runtime.executor(), log: self.log.new(o!("service" => service_name)), diff --git a/tests/beacon_chain_sim/Cargo.toml b/tests/beacon_chain_sim/Cargo.toml new file mode 100644 index 0000000000..8ce05e2d66 --- /dev/null +++ b/tests/beacon_chain_sim/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "beacon_chain_sim" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +node_test_rig = { path = "../node_test_rig" } +types = { path = "../../eth2/types" } diff --git a/tests/beacon_chain_sim/src/main.rs b/tests/beacon_chain_sim/src/main.rs new file mode 100644 index 0000000000..0915bd372f --- /dev/null +++ b/tests/beacon_chain_sim/src/main.rs @@ -0,0 +1,77 @@ +use node_test_rig::{ + environment::{EnvironmentBuilder, RuntimeContext}, + testing_client_config, ClientConfig, LocalBeaconNode, ProductionClient, +}; +use types::EthSpec; + +pub type BeaconNode = LocalBeaconNode>; + +fn main() { + match simulation(4) { + Ok(()) => println!("Simulation exited successfully"), + Err(e) => println!("Simulation exited with error: {}", e), + } +} + +fn simulation(num_nodes: usize) -> Result<(), String> { + if num_nodes < 1 { + return Err("Must have at least one node".into()); + } + + let mut env = EnvironmentBuilder::minimal() + .async_logger("debug")? + .multi_threaded_tokio_runtime()? + .build()?; + + let base_config = testing_client_config(); + + let boot_node = + BeaconNode::production(env.service_context("boot_node".into()), base_config.clone()); + + let nodes = (1..num_nodes) + .map(|i| { + let context = env.service_context(format!("node_{}", i)); + new_with_bootnode_via_enr(context, &boot_node, base_config.clone()) + }) + .collect::>(); + + env.block_until_ctrl_c()?; + + Ok(()) +} + +// TODO: this function does not result in nodes connecting to each other. Age to investigate? +fn new_with_bootnode_via_enr( + context: RuntimeContext, + boot_node: &BeaconNode, + base_config: ClientConfig, +) -> BeaconNode { + let mut config = base_config; + config.network.boot_nodes.push( + boot_node + .client + .enr() + .expect("bootnode must have a network"), + ); + + BeaconNode::production(context, config) +} + +fn new_with_bootnode_via_multiaddr( + context: RuntimeContext, + boot_node: &BeaconNode, + base_config: ClientConfig, +) -> BeaconNode { + let mut config = base_config; + config.network.libp2p_nodes.push( + boot_node + .client + .libp2p_listen_addresses() + .expect("bootnode must have a network") + .first() + .expect("bootnode must have at least one listen addr") + .clone(), + ); + + BeaconNode::production(context, config) +} diff --git a/tests/node_test_rig/src/lib.rs b/tests/node_test_rig/src/lib.rs index 5a0f21e097..060215df29 100644 --- a/tests/node_test_rig/src/lib.rs +++ b/tests/node_test_rig/src/lib.rs @@ -1,13 +1,12 @@ -use beacon_node::{ - beacon_chain::BeaconChainTypes, Client, ClientConfig, ClientGenesis, ProductionBeaconNode, - ProductionClient, -}; +use beacon_node::{beacon_chain::BeaconChainTypes, Client, ClientGenesis, ProductionBeaconNode}; use environment::RuntimeContext; use futures::Future; use remote_beacon_node::RemoteBeaconNode; +use std::path::PathBuf; use tempdir::TempDir; use types::EthSpec; +pub use beacon_node::{ClientConfig, ProductionClient}; pub use environment; /// Provides a beacon node that is running in the current process. Useful for testing purposes. @@ -18,8 +17,13 @@ pub struct LocalBeaconNode { impl LocalBeaconNode> { /// Starts a new, production beacon node. - pub fn production(context: RuntimeContext) -> Self { - let (client_config, datadir) = testing_client_config(); + pub fn production(context: RuntimeContext, mut client_config: ClientConfig) -> Self { + // Creates a temporary directory that will be deleted once this `TempDir` is dropped. + let datadir = TempDir::new("lighthouse_node_test_rig") + .expect("should create temp directory for client datadir"); + + client_config.data_dir = datadir.path().into(); + client_config.network.network_dir = PathBuf::from(datadir.path()).join("network"); let client = ProductionBeaconNode::new(context, client_config) .wait() @@ -42,15 +46,9 @@ impl LocalBeaconNode> { } } -fn testing_client_config() -> (ClientConfig, TempDir) { - // Creates a temporary directory that will be deleted once this `TempDir` is dropped. - let tempdir = TempDir::new("lighthouse_node_test_rig") - .expect("should create temp directory for client datadir"); - +pub fn testing_client_config() -> ClientConfig { let mut client_config = ClientConfig::default(); - client_config.data_dir = tempdir.path().into(); - // Setting ports to `0` means that the OS will choose some available port. client_config.network.libp2p_port = 0; client_config.network.discovery_port = 0; @@ -63,5 +61,5 @@ fn testing_client_config() -> (ClientConfig, TempDir) { genesis_time: 13_371_337, }; - (client_config, tempdir) + client_config }