Add regression tests for boot_node (#2749)

## Issue Addressed
Resolves #2602

## Proposed Changes

*Note: For a review it might help to look at the individual commits.*

### `boot_node`
Add support for the flags `dump-config` and `immediate-shutdown`. For `immediate-shutdown` the actual behavior could be described as `dump-config-and-exit`.

Both flags are handled in `boot_node::main`, which appears to be the simplest approach.

### `boot_node` regression tests
Added in `lighthouse/tests/boot_node.rs`.

### `CommandLineTestExec`
Factors out boilerplate related to CLI tests. It's used in the regression tests for `boot_node`, `beacon_node` and `validator_client`.

## Open TODO
Add tests for `boot_node` flags `enable-enr-auto-update` and `disable-packet-filter`. They end up in [`Discv5Config`](9ed2cba6bc/boot_node/src/config.rs (L29)), which doesn't support serde (de)serialization.

I haven't found a workaround - guidance would be appreciated.
This commit is contained in:
mooori 2021-11-08 01:37:58 +00:00
parent fbafe416d1
commit d01fe02824
11 changed files with 473 additions and 292 deletions

5
Cargo.lock generated
View File

@ -479,11 +479,15 @@ version = "2.0.1"
dependencies = [
"beacon_node",
"clap",
"clap_utils",
"eth2_ssz",
"hex",
"lighthouse_network",
"log",
"logging",
"serde",
"serde_derive",
"serde_json",
"slog",
"slog-async",
"slog-scope",
@ -3193,6 +3197,7 @@ dependencies = [
"lighthouse_network",
"lighthouse_version",
"malloc_utils",
"serde",
"serde_json",
"slashing_protection",
"slog",

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
beacon_node = { path = "../beacon_node" }
clap = "2.33.3"
clap_utils = { path = "../common/clap_utils" }
lighthouse_network = { path = "../beacon_node/lighthouse_network" }
types = { path = "../consensus/types" }
eth2_ssz = "0.4.0"
@ -19,3 +20,6 @@ slog-async = "2.5.0"
slog-scope = "4.3.0"
slog-stdlog = "4.0.0"
hex = "0.4.2"
serde = "1.0.116"
serde_derive = "1.0.116"
serde_json = "1.0.66"

View File

@ -5,6 +5,7 @@ use lighthouse_network::{
discovery::{create_enr_builder_from_config, load_enr_from_disk, use_or_load_enr},
load_private_key, CombinedKeyExt, NetworkConfig,
};
use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use std::convert::TryFrom;
use std::net::SocketAddr;
@ -130,3 +131,35 @@ impl<T: EthSpec> TryFrom<&ArgMatches<'_>> for BootNodeConfig<T> {
})
}
}
/// The set of configuration parameters that can safely be (de)serialized.
///
/// Its fields are a subset of the fields of `BootNodeConfig`.
#[derive(Serialize, Deserialize)]
pub struct BootNodeConfigSerialization {
pub listen_socket: SocketAddr,
// TODO: Generalise to multiaddr
pub boot_nodes: Vec<Enr>,
pub local_enr: Enr,
}
impl BootNodeConfigSerialization {
/// Returns a `BootNodeConfigSerialization` obtained from copying resp. cloning the
/// relevant fields of `config`
pub fn from_config_ref<T: EthSpec>(config: &BootNodeConfig<T>) -> Self {
let BootNodeConfig {
listen_socket,
boot_nodes,
local_enr,
local_key: _,
discv5_config: _,
phantom: _,
} = config;
BootNodeConfigSerialization {
listen_socket: *listen_socket,
boot_nodes: boot_nodes.clone(),
local_enr: local_enr.clone(),
}
}
}

View File

@ -3,17 +3,24 @@ use clap::ArgMatches;
use slog::{o, Drain, Level, Logger};
use std::convert::TryFrom;
use std::fs::File;
use std::path::PathBuf;
mod cli;
mod config;
pub mod config;
mod server;
pub use cli::cli_app;
use config::BootNodeConfig;
use config::{BootNodeConfig, BootNodeConfigSerialization};
use types::{EthSpec, EthSpecId};
const LOG_CHANNEL_SIZE: usize = 2048;
/// Run the bootnode given the CLI configuration.
pub fn run(matches: &ArgMatches<'_>, eth_spec_id: EthSpecId, debug_level: String) {
pub fn run(
lh_matches: &ArgMatches<'_>,
bn_matches: &ArgMatches<'_>,
eth_spec_id: EthSpecId,
debug_level: String,
) {
let debug_level = match debug_level.as_str() {
"trace" => log::Level::Trace,
"debug" => log::Level::Debug,
@ -49,24 +56,40 @@ pub fn run(matches: &ArgMatches<'_>, eth_spec_id: EthSpecId, debug_level: String
let log = slog_scope::logger();
// Run the main function emitting any errors
if let Err(e) = match eth_spec_id {
EthSpecId::Minimal => main::<types::MinimalEthSpec>(matches, log),
EthSpecId::Mainnet => main::<types::MainnetEthSpec>(matches, log),
EthSpecId::Minimal => main::<types::MinimalEthSpec>(lh_matches, bn_matches, log),
EthSpecId::Mainnet => main::<types::MainnetEthSpec>(lh_matches, bn_matches, log),
} {
slog::crit!(slog_scope::logger(), "{}", e);
}
}
fn main<T: EthSpec>(matches: &ArgMatches<'_>, log: slog::Logger) -> Result<(), String> {
fn main<T: EthSpec>(
lh_matches: &ArgMatches<'_>,
bn_matches: &ArgMatches<'_>,
log: slog::Logger,
) -> Result<(), String> {
// Builds a custom executor for the bootnode
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.map_err(|e| format!("Failed to build runtime: {}", e))?;
// parse the CLI args into a useable config
let config: BootNodeConfig<T> = BootNodeConfig::try_from(matches)?;
// Parse the CLI args into a useable config
let config: BootNodeConfig<T> = BootNodeConfig::try_from(bn_matches)?;
// Dump config if `dump-config` flag is set
let dump_config = clap_utils::parse_optional::<PathBuf>(lh_matches, "dump-config")?;
if let Some(dump_path) = dump_config {
let config_sz = BootNodeConfigSerialization::from_config_ref(&config);
let mut file = File::create(dump_path)
.map_err(|e| format!("Failed to create dumped config: {:?}", e))?;
serde_json::to_writer(&mut file, &config_sz)
.map_err(|e| format!("Error serializing config: {:?}", e))?;
}
// Run the boot node
runtime.block_on(server::run(config, log));
if !lh_matches.is_present("immediate-shutdown") {
runtime.block_on(server::run(config, log));
}
Ok(())
}

View File

@ -37,6 +37,7 @@ lighthouse_version = { path = "../common/lighthouse_version" }
account_utils = { path = "../common/account_utils" }
lighthouse_metrics = { path = "../common/lighthouse_metrics" }
lazy_static = "1.4.0"
serde = { version = "1.0.116", features = ["derive"] }
serde_json = "1.0.59"
task_executor = { path = "../common/task_executor" }
malloc_utils = { path = "../common/malloc_utils" }

View File

@ -204,7 +204,7 @@ fn main() {
.expect("Debug-level must be present")
.into();
boot_node::run(bootnode_matches, eth_spec_id, debug_info);
boot_node::run(&matches, bootnode_matches, eth_spec_id, debug_info);
return Ok(());
}

View File

@ -1,25 +1,21 @@
use beacon_node::ClientConfig as Config;
use crate::exec::{CommandLineTestExec, CompletedTest};
use lighthouse_network::PeerId;
use serde_json::from_reader;
use std::fs::File;
use std::io::Write;
use std::net::{IpAddr, Ipv4Addr};
use std::net::{TcpListener, UdpSocket};
use std::path::PathBuf;
use std::process::{Command, Output};
use std::str::{from_utf8, FromStr};
use std::process::Command;
use std::str::FromStr;
use std::string::ToString;
use tempfile::TempDir;
use types::{Checkpoint, Epoch, Hash256};
const BEACON_CMD: &str = "beacon_node";
const CONFIG_NAME: &str = "bn_dump.json";
const DUMP_CONFIG_CMD: &str = "dump-config";
const IMMEDIATE_SHUTDOWN_CMD: &str = "immediate-shutdown";
const DEFAULT_ETH1_ENDPOINT: &str = "http://localhost:8545/";
/// Returns the `lighthouse beacon_node --immediate-shutdown` command.
/// Returns the `lighthouse beacon_node` command.
fn base_cmd() -> Command {
let lighthouse_bin = env!("CARGO_BIN_EXE_lighthouse");
let path = lighthouse_bin
@ -27,25 +23,10 @@ fn base_cmd() -> Command {
.expect("should parse CARGO_TARGET_DIR");
let mut cmd = Command::new(path);
cmd.arg(BEACON_CMD)
.arg(format!("--{}", IMMEDIATE_SHUTDOWN_CMD));
cmd.arg("beacon_node");
cmd
}
/// Executes a `Command`, returning a `Result` based upon the success exit code of the command.
fn output_result(cmd: &mut Command) -> Result<Output, String> {
let output = cmd.output().expect("should run command");
if output.status.success() {
Ok(output)
} else {
Err(from_utf8(&output.stderr)
.expect("stderr is not utf8")
.to_string())
}
}
// Wrapper around `Command` for easier Command Line Testing.
struct CommandLineTest {
cmd: Command,
@ -56,89 +37,24 @@ impl CommandLineTest {
CommandLineTest { cmd: base_cmd }
}
fn flag(mut self, flag: &str, value: Option<&str>) -> Self {
// Build the command by adding the flag and any values.
self.cmd.arg(format!("--{}", flag));
if let Some(value) = value {
self.cmd.arg(value);
}
self
}
fn run(&mut self) -> CompletedTest {
// Setup temp directories.
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_path: PathBuf = tmp_dir.path().join(CONFIG_NAME);
// Add --datadir <temp_dir> --dump-config <temp_path> -z to cmd.
self.cmd
.arg("--datadir")
.arg(tmp_dir.path().as_os_str())
.arg(format!("--{}", DUMP_CONFIG_CMD))
.arg(tmp_path.as_os_str())
.arg("-z");
// Run the command.
let output = output_result(&mut self.cmd);
if let Err(e) = output {
panic!("{:?}", e);
}
// Grab the config.
let config: Config =
from_reader(File::open(tmp_path).expect("Unable to open dumped config"))
.expect("Unable to deserialize to ClientConfig");
CompletedTest {
config,
dir: tmp_dir,
}
}
fn run_with_no_zero_port(&mut self) -> CompletedTest {
// Setup temp directories.
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_path: PathBuf = tmp_dir.path().join(CONFIG_NAME);
// Add --datadir <temp_dir> --dump-config <temp_path> to cmd.
self.cmd
.arg("--datadir")
.arg(tmp_dir.path().as_os_str())
.arg(format!("--{}", DUMP_CONFIG_CMD))
.arg(tmp_path.as_os_str());
// Run the command.
let output = output_result(&mut self.cmd);
if let Err(e) = output {
panic!("{:?}", e);
}
// Grab the config.
let config: Config =
from_reader(File::open(tmp_path).expect("Unable to open dumped config"))
.expect("Unable to deserialize to ClientConfig");
CompletedTest {
config,
dir: tmp_dir,
}
fn run_with_zero_port(&mut self) -> CompletedTest<Config> {
self.cmd.arg("-z");
self.run()
}
}
struct CompletedTest {
config: Config,
dir: TempDir,
}
impl CompletedTest {
fn with_config<F: Fn(&Config)>(self, func: F) {
func(&self.config);
}
fn with_config_and_dir<F: Fn(&Config, &TempDir)>(self, func: F) {
func(&self.config, &self.dir);
impl CommandLineTestExec for CommandLineTest {
type Config = Config;
fn cmd_mut(&mut self) -> &mut Command {
&mut self.cmd
}
}
#[test]
fn datadir_flag() {
CommandLineTest::new()
.run()
.run_with_zero_port()
.with_config_and_dir(|config, dir| assert_eq!(config.data_dir, dir.path().join("beacon")));
}
@ -146,7 +62,7 @@ fn datadir_flag() {
fn staking_flag() {
CommandLineTest::new()
.flag("staking", None)
.run()
.run_with_zero_port()
.with_config(|config| {
assert!(config.http_api.enabled);
assert!(config.sync_eth1_chain);
@ -166,21 +82,21 @@ fn wss_checkpoint_flag() {
"wss-checkpoint",
Some("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef:1010"),
)
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.chain.weak_subjectivity_checkpoint, state));
}
#[test]
fn max_skip_slots_flag() {
CommandLineTest::new()
.flag("max-skip-slots", Some("10"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.chain.import_max_skip_slots, Some(10)));
}
#[test]
fn enable_lock_timeouts_default() {
CommandLineTest::new()
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.chain.enable_lock_timeouts));
}
@ -188,7 +104,7 @@ fn enable_lock_timeouts_default() {
fn disable_lock_timeouts_flag() {
CommandLineTest::new()
.flag("disable-lock-timeouts", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(!config.chain.enable_lock_timeouts));
}
@ -197,7 +113,7 @@ fn freezer_dir_flag() {
let dir = TempDir::new().expect("Unable to create temporary directory");
CommandLineTest::new()
.flag("freezer-dir", dir.path().as_os_str().to_str())
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.freezer_db_path, Some(dir.path().to_path_buf())));
}
@ -205,7 +121,7 @@ fn freezer_dir_flag() {
fn graffiti_flag() {
CommandLineTest::new()
.flag("graffiti", Some("nice-graffiti"))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.graffiti.to_string(),
@ -222,7 +138,7 @@ fn trusted_peers_flag() {
"trusted-peers",
Some(format!("{},{}", peers[0].to_string(), peers[1].to_string()).as_str()),
)
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
PeerId::from(config.network.trusted_peers[0].clone()).to_bytes(),
@ -240,14 +156,14 @@ fn trusted_peers_flag() {
fn dummy_eth1_flag() {
CommandLineTest::new()
.flag("dummy-eth1", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.dummy_eth1_backend));
}
#[test]
fn eth1_flag() {
CommandLineTest::new()
.flag("eth1", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.sync_eth1_chain));
}
#[test]
@ -257,7 +173,7 @@ fn eth1_endpoints_flag() {
"eth1-endpoints",
Some("http://localhost:9545,https://infura.io/secret"),
)
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.eth1.endpoints[0].full.to_string(),
@ -279,14 +195,14 @@ fn eth1_endpoints_flag() {
fn eth1_blocks_per_log_query_flag() {
CommandLineTest::new()
.flag("eth1-blocks-per-log-query", Some("500"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.eth1.blocks_per_log_query, 500));
}
#[test]
fn eth1_purge_cache_flag() {
CommandLineTest::new()
.flag("eth1-purge-cache", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.eth1.purge_cache));
}
@ -296,14 +212,14 @@ fn network_dir_flag() {
let dir = TempDir::new().expect("Unable to create temporary directory");
CommandLineTest::new()
.flag("network-dir", dir.path().as_os_str().to_str())
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.network_dir, dir.path()));
}
#[test]
fn network_target_peers_flag() {
CommandLineTest::new()
.flag("target-peers", Some("55"))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.network.target_peers, "55".parse::<usize>().unwrap());
});
@ -312,27 +228,27 @@ fn network_target_peers_flag() {
fn network_subscribe_all_subnets_flag() {
CommandLineTest::new()
.flag("subscribe-all-subnets", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.network.subscribe_all_subnets));
}
#[test]
fn network_import_all_attestations_flag() {
CommandLineTest::new()
.flag("import-all-attestations", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.network.import_all_attestations));
}
#[test]
fn network_shutdown_after_sync_flag() {
CommandLineTest::new()
.flag("shutdown-after-sync", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.network.shutdown_after_sync));
}
#[test]
fn network_shutdown_after_sync_disabled_flag() {
CommandLineTest::new()
.run()
.run_with_zero_port()
.with_config(|config| assert!(!config.network.shutdown_after_sync));
}
#[test]
@ -340,7 +256,7 @@ fn network_listen_address_flag() {
let addr = "127.0.0.2".parse::<Ipv4Addr>().unwrap();
CommandLineTest::new()
.flag("listen-address", Some("127.0.0.2"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.listen_address, addr));
}
#[test]
@ -348,7 +264,7 @@ fn network_port_flag() {
let port = unused_port("tcp").expect("Unable to find unused port.");
CommandLineTest::new()
.flag("port", Some(port.to_string().as_str()))
.run_with_no_zero_port()
.run()
.with_config(|config| {
assert_eq!(config.network.libp2p_port, port);
assert_eq!(config.network.discovery_port, port);
@ -361,7 +277,7 @@ fn network_port_and_discovery_port_flags() {
CommandLineTest::new()
.flag("port", Some(port1.to_string().as_str()))
.flag("discovery-port", Some(port2.to_string().as_str()))
.run_with_no_zero_port()
.run()
.with_config(|config| {
assert_eq!(config.network.libp2p_port, port1);
assert_eq!(config.network.discovery_port, port2);
@ -371,14 +287,14 @@ fn network_port_and_discovery_port_flags() {
fn disable_discovery_flag() {
CommandLineTest::new()
.flag("disable-discovery", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.network.disable_discovery));
}
#[test]
fn disable_upnp_flag() {
CommandLineTest::new()
.flag("disable-upnp", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(!config.network.upnp_enabled));
}
#[test]
@ -386,7 +302,7 @@ fn default_boot_nodes() {
let mainnet = vec![
// Lighthouse Team (Sigma Prime)
"enr:-Jq4QItoFUuug_n_qbYbU0OY04-np2wT8rUCauOOXNi0H3BWbDj-zbfZb7otA7jZ6flbBpx1LNZK2TDebZ9dEKx84LYBhGV0aDKQtTA_KgEAAAD__________4JpZIJ2NIJpcISsaa0ZiXNlY3AyNTZrMaEDHAD2JKYevx89W0CcFJFiskdcEzkH_Wdv9iW42qLK79ODdWRwgiMo",
"enr:-Jq4QN_YBsUOqQsty1OGvYv48PMaiEt1AzGD1NkYQHaxZoTyVGqMYXg0K9c0LPNWC9pkXmggApp8nygYLsQwScwAgfgBhGV0aDKQtTA_KgEAAAD__________4JpZIJ2NIJpcISLosQxiXNlY3AyNTZrMaEDBJj7_dLFACaxBfaI8KZTh_SSJUjhyAyfshimvSqo22WDdWRwgiMo",
"enr:-Jq4QN_YBsUOqQsty1OGvYv48PMaiEt1AzGD1NkYQHaxZoTyVGqMYXg0K9c0LPNWC9pkXmggApp8nygYLsQwScwAgfgBhGV0aDKQtTA_KgEAAAD__________4JpZIJ2NIJpcISLosQxiXNlY3AyNTZrMaEDBJj7_dLFACaxBfaI8KZTh_SSJUjhyAyfshimvSqo22WDdWRwgiMo",
// EF Team
"enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg",
"enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg",
@ -404,26 +320,28 @@ fn default_boot_nodes() {
"enr:-LK4QKWrXTpV9T78hNG6s8AM6IO4XH9kFT91uZtFg1GcsJ6dKovDOr1jtAAFPnS2lvNltkOGA9k29BUN7lFh_sjuc9QBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhANAdd-Jc2VjcDI1NmsxoQLQa6ai7y9PMN5hpLe5HmiJSlYzMuzP7ZhwRiwHvqNXdoN0Y3CCI4yDdWRwgiOM"
];
CommandLineTest::new().run().with_config(|config| {
// Lighthouse Team (Sigma Prime)
assert_eq!(config.network.boot_nodes_enr[0].to_base64(), mainnet[0]);
assert_eq!(config.network.boot_nodes_enr[1].to_base64(), mainnet[1]);
// EF Team
assert_eq!(config.network.boot_nodes_enr[2].to_base64(), mainnet[2]);
assert_eq!(config.network.boot_nodes_enr[3].to_base64(), mainnet[3]);
assert_eq!(config.network.boot_nodes_enr[4].to_base64(), mainnet[4]);
assert_eq!(config.network.boot_nodes_enr[5].to_base64(), mainnet[5]);
// Teku team (Consensys)
assert_eq!(config.network.boot_nodes_enr[6].to_base64(), mainnet[6]);
assert_eq!(config.network.boot_nodes_enr[7].to_base64(), mainnet[7]);
// Prysm team (Prysmatic Labs)
assert_eq!(config.network.boot_nodes_enr[8].to_base64(), mainnet[8]);
assert_eq!(config.network.boot_nodes_enr[9].to_base64(), mainnet[9]);
assert_eq!(config.network.boot_nodes_enr[10].to_base64(), mainnet[10]);
// Nimbus team
assert_eq!(config.network.boot_nodes_enr[11].to_base64(), mainnet[11]);
assert_eq!(config.network.boot_nodes_enr[12].to_base64(), mainnet[12]);
});
CommandLineTest::new()
.run_with_zero_port()
.with_config(|config| {
// Lighthouse Team (Sigma Prime)
assert_eq!(config.network.boot_nodes_enr[0].to_base64(), mainnet[0]);
assert_eq!(config.network.boot_nodes_enr[1].to_base64(), mainnet[1]);
// EF Team
assert_eq!(config.network.boot_nodes_enr[2].to_base64(), mainnet[2]);
assert_eq!(config.network.boot_nodes_enr[3].to_base64(), mainnet[3]);
assert_eq!(config.network.boot_nodes_enr[4].to_base64(), mainnet[4]);
assert_eq!(config.network.boot_nodes_enr[5].to_base64(), mainnet[5]);
// Teku team (Consensys)
assert_eq!(config.network.boot_nodes_enr[6].to_base64(), mainnet[6]);
assert_eq!(config.network.boot_nodes_enr[7].to_base64(), mainnet[7]);
// Prysm team (Prysmatic Labs)
assert_eq!(config.network.boot_nodes_enr[8].to_base64(), mainnet[8]);
assert_eq!(config.network.boot_nodes_enr[9].to_base64(), mainnet[9]);
assert_eq!(config.network.boot_nodes_enr[10].to_base64(), mainnet[10]);
// Nimbus team
assert_eq!(config.network.boot_nodes_enr[11].to_base64(), mainnet[11]);
assert_eq!(config.network.boot_nodes_enr[12].to_base64(), mainnet[12]);
});
}
#[test]
fn boot_nodes_flag() {
@ -432,7 +350,7 @@ fn boot_nodes_flag() {
let enr: Vec<&str> = nodes.split(',').collect();
CommandLineTest::new()
.flag("boot-nodes", Some(nodes))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.network.boot_nodes_enr[0].to_base64(), enr[0]);
assert_eq!(config.network.boot_nodes_enr[1].to_base64(), enr[1]);
@ -445,7 +363,7 @@ fn boot_nodes_multiaddr_flag() {
let multiaddr: Vec<&str> = nodes.split(',').collect();
CommandLineTest::new()
.flag("boot-nodes", Some(nodes))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.network.boot_nodes_multiaddr[0].to_string(),
@ -461,16 +379,18 @@ fn boot_nodes_multiaddr_flag() {
fn private_flag() {
CommandLineTest::new()
.flag("private", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.network.private));
}
#[test]
fn zero_ports_flag() {
CommandLineTest::new().run().with_config(|config| {
assert_eq!(config.network.enr_address, None);
assert_eq!(config.http_api.listen_port, 0);
assert_eq!(config.http_metrics.listen_port, 0);
});
CommandLineTest::new()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.network.enr_address, None);
assert_eq!(config.http_api.listen_port, 0);
assert_eq!(config.http_metrics.listen_port, 0);
});
}
// Tests for ENR flags.
@ -479,7 +399,7 @@ fn enr_udp_port_flags() {
let port = unused_port("udp").expect("Unable to find unused port.");
CommandLineTest::new()
.flag("enr-udp-port", Some(port.to_string().as_str()))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_udp_port, Some(port)));
}
#[test]
@ -487,7 +407,7 @@ fn enr_tcp_port_flags() {
let port = unused_port("tcp").expect("Unable to find unused port.");
CommandLineTest::new()
.flag("enr-tcp-port", Some(port.to_string().as_str()))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_tcp_port, Some(port)));
}
#[test]
@ -500,7 +420,7 @@ fn enr_match_flag() {
.flag("listen-address", Some("127.0.0.2"))
.flag("discovery-port", Some(port1.to_string().as_str()))
.flag("port", Some(port2.to_string().as_str()))
.run_with_no_zero_port()
.run()
.with_config(|config| {
assert_eq!(config.network.listen_address, addr);
assert_eq!(config.network.enr_address, Some(addr));
@ -515,7 +435,7 @@ fn enr_address_flag() {
CommandLineTest::new()
.flag("enr-address", Some("192.167.1.1"))
.flag("enr-udp-port", Some(port.to_string().as_str()))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.network.enr_address, Some(addr));
assert_eq!(config.network.enr_udp_port, Some(port));
@ -529,7 +449,7 @@ fn enr_address_dns_flag() {
CommandLineTest::new()
.flag("enr-address", Some("localhost"))
.flag("enr-udp-port", Some(port.to_string().as_str()))
.run()
.run_with_zero_port()
.with_config(|config| {
assert!(
config.network.enr_address == Some(addr)
@ -542,7 +462,7 @@ fn enr_address_dns_flag() {
fn disable_enr_auto_update_flag() {
CommandLineTest::new()
.flag("disable-enr-auto-update", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.network.discv5_config.enr_update));
}
@ -551,7 +471,7 @@ fn disable_enr_auto_update_flag() {
fn http_flag() {
CommandLineTest::new()
.flag("http", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.http_api.enabled));
}
#[test]
@ -559,7 +479,7 @@ fn http_address_flag() {
let addr = "127.0.0.99".parse::<Ipv4Addr>().unwrap();
CommandLineTest::new()
.flag("http-address", Some("127.0.0.99"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.http_api.listen_addr, addr));
}
#[test]
@ -569,14 +489,14 @@ fn http_port_flag() {
CommandLineTest::new()
.flag("http-port", Some(port1.to_string().as_str()))
.flag("port", Some(port2.to_string().as_str()))
.run_with_no_zero_port()
.run()
.with_config(|config| assert_eq!(config.http_api.listen_port, port1));
}
#[test]
fn http_allow_origin_flag() {
CommandLineTest::new()
.flag("http-allow-origin", Some("127.0.0.99"))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.http_api.allow_origin, Some("127.0.0.99".to_string()));
});
@ -585,7 +505,7 @@ fn http_allow_origin_flag() {
fn http_allow_origin_all_flag() {
CommandLineTest::new()
.flag("http-allow-origin", Some("*"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.http_api.allow_origin, Some("*".to_string())));
}
#[test]
@ -601,7 +521,7 @@ fn http_tls_flags() {
"http-tls-key",
dir.path().join("private.key").as_os_str().to_str(),
)
.run()
.run_with_zero_port()
.with_config(|config| {
let tls_config = config
.http_api
@ -618,7 +538,7 @@ fn http_tls_flags() {
fn metrics_flag() {
CommandLineTest::new()
.flag("metrics", None)
.run()
.run_with_zero_port()
.with_config(|config| {
assert!(config.http_metrics.enabled);
assert!(config.network.metrics_enabled);
@ -630,7 +550,7 @@ fn metrics_address_flag() {
CommandLineTest::new()
.flag("metrics", None)
.flag("metrics-address", Some("127.0.0.99"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.http_metrics.listen_addr, addr));
}
#[test]
@ -641,7 +561,7 @@ fn metrics_port_flag() {
.flag("metrics", None)
.flag("metrics-port", Some(port1.to_string().as_str()))
.flag("port", Some(port2.to_string().as_str()))
.run_with_no_zero_port()
.run()
.with_config(|config| assert_eq!(config.http_metrics.listen_port, port1));
}
#[test]
@ -649,7 +569,7 @@ fn metrics_allow_origin_flag() {
CommandLineTest::new()
.flag("metrics", None)
.flag("metrics-allow-origin", Some("http://localhost:5059"))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.http_metrics.allow_origin,
@ -662,7 +582,7 @@ fn metrics_allow_origin_all_flag() {
CommandLineTest::new()
.flag("metrics", None)
.flag("metrics-allow-origin", Some("*"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.http_metrics.allow_origin, Some("*".to_string())));
}
@ -671,7 +591,7 @@ fn metrics_allow_origin_all_flag() {
fn validator_monitor_auto_flag() {
CommandLineTest::new()
.flag("validator-monitor-auto", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.validator_monitor_auto));
}
#[test]
@ -679,7 +599,7 @@ fn validator_monitor_pubkeys_flag() {
CommandLineTest::new()
.flag("validator-monitor-pubkeys", Some("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef,\
0xbeefdeadbeefdeaddeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"))
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.validator_monitor_pubkeys[0].to_string(), "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
assert_eq!(config.validator_monitor_pubkeys[1].to_string(), "0xbeefdeadbeefdeaddeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
@ -694,7 +614,7 @@ fn validator_monitor_file_flag() {
.expect("Unable to write to file");
CommandLineTest::new()
.flag("validator-monitor-file", dir.path().join("pubkeys.txt").as_os_str().to_str())
.run()
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.validator_monitor_pubkeys[0].to_string(), "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
assert_eq!(config.validator_monitor_pubkeys[1].to_string(), "0xbeefdeadbeefdeaddeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
@ -706,21 +626,21 @@ fn validator_monitor_file_flag() {
fn slots_per_restore_point_flag() {
CommandLineTest::new()
.flag("slots-per-restore-point", Some("64"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.store.slots_per_restore_point, 64));
}
#[test]
fn block_cache_size_flag() {
CommandLineTest::new()
.flag("block-cache-size", Some("4"))
.run()
.run_with_zero_port()
.with_config(|config| assert_eq!(config.store.block_cache_size, 4_usize));
}
#[test]
fn auto_compact_db_flag() {
CommandLineTest::new()
.flag("auto-compact-db", Some("false"))
.run()
.run_with_zero_port()
.with_config(|config| assert!(!config.store.compact_on_prune));
}
#[test]
@ -728,20 +648,20 @@ fn compact_db_flag() {
CommandLineTest::new()
.flag("auto-compact-db", Some("false"))
.flag("compact-db", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.store.compact_on_init));
}
#[test]
fn reconstruct_historic_states_flag() {
CommandLineTest::new()
.flag("reconstruct-historic-states", None)
.run()
.run_with_zero_port()
.with_config(|config| assert!(config.chain.reconstruct_historic_states));
}
#[test]
fn no_reconstruct_historic_states_flag() {
CommandLineTest::new()
.run()
.run_with_zero_port()
.with_config(|config| assert!(!config.chain.reconstruct_historic_states));
}
@ -751,7 +671,7 @@ fn slasher_flag() {
CommandLineTest::new()
.flag("slasher", None)
.flag("slasher-max-db-size", Some("16"))
.run()
.run_with_zero_port()
.with_config_and_dir(|config, dir| {
if let Some(slasher_config) = &config.slasher {
assert_eq!(
@ -770,7 +690,7 @@ fn slasher_dir_flag() {
.flag("slasher", None)
.flag("slasher-dir", dir.path().as_os_str().to_str())
.flag("slasher-max-db-size", Some("16"))
.run()
.run_with_zero_port()
.with_config(|config| {
if let Some(slasher_config) = &config.slasher {
assert_eq!(slasher_config.database_path, dir.path());
@ -785,7 +705,7 @@ fn slasher_update_period_flag() {
.flag("slasher", None)
.flag("slasher-max-db-size", Some("16"))
.flag("slasher-update-period", Some("100"))
.run()
.run_with_zero_port()
.with_config(|config| {
if let Some(slasher_config) = &config.slasher {
assert_eq!(slasher_config.update_period, 100);
@ -819,7 +739,7 @@ fn slasher_history_length_flag() {
.flag("slasher", None)
.flag("slasher-max-db-size", Some("16"))
.flag("slasher-history-length", Some("2048"))
.run()
.run_with_zero_port()
.with_config(|config| {
if let Some(slasher_config) = &config.slasher {
assert_eq!(slasher_config.history_length, 2048);
@ -833,7 +753,7 @@ fn slasher_max_db_size_flag() {
CommandLineTest::new()
.flag("slasher", None)
.flag("slasher-max-db-size", Some("10"))
.run()
.run_with_zero_port()
.with_config(|config| {
let slasher_config = config
.slasher
@ -848,7 +768,7 @@ fn slasher_chunk_size_flag() {
.flag("slasher", None)
.flag("slasher-chunk-size", Some("32"))
.flag("slasher-max-db-size", Some("16"))
.run()
.run_with_zero_port()
.with_config(|config| {
let slasher_config = config
.slasher
@ -863,7 +783,7 @@ fn slasher_validator_chunk_size_flag() {
.flag("slasher", None)
.flag("slasher-max-db-size", Some("16"))
.flag("slasher-validator-chunk-size", Some("512"))
.run()
.run_with_zero_port()
.with_config(|config| {
let slasher_config = config
.slasher
@ -878,7 +798,7 @@ fn slasher_broadcast_flag() {
.flag("slasher", None)
.flag("slasher-broadcast", None)
.flag("slasher-max-db-size", Some("16"))
.run()
.run_with_zero_port()
.with_config(|config| {
let slasher_config = config
.slasher
@ -891,7 +811,7 @@ fn slasher_broadcast_flag() {
pub fn malloc_tuning_flag() {
CommandLineTest::new()
.flag("disable-malloc-tuning", None)
.run()
.run_with_zero_port()
.with_config(|config| {
assert!(!config.http_metrics.allocator_metrics_enabled);
});
@ -902,7 +822,7 @@ fn ensure_panic_on_failed_launch() {
CommandLineTest::new()
.flag("slasher", None)
.flag("slasher-chunk-size", Some("10"))
.run()
.run_with_zero_port()
.with_config(|config| {
let slasher_config = config
.slasher

View File

@ -0,0 +1,164 @@
use boot_node::config::BootNodeConfigSerialization;
use crate::exec::{CommandLineTestExec, CompletedTest};
use beacon_node::get_eth2_network_config;
use clap::ArgMatches;
use lighthouse_network::discovery::ENR_FILENAME;
use lighthouse_network::Enr;
use std::fs::File;
use std::io::Write;
use std::net::{Ipv4Addr, UdpSocket};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use tempfile::TempDir;
const IP_ADDRESS: &str = "192.168.2.108";
/// Returns the `lighthouse boot_node` command.
fn base_cmd() -> Command {
let lighthouse_bin = env!("CARGO_BIN_EXE_lighthouse");
let path = lighthouse_bin
.parse::<PathBuf>()
.expect("should parse CARGO_TARGET_DIR");
let mut cmd = Command::new(path);
cmd.arg("boot_node");
cmd
}
struct CommandLineTest {
cmd: Command,
}
impl CommandLineTest {
fn new() -> CommandLineTest {
let base_cmd = base_cmd();
CommandLineTest { cmd: base_cmd }
}
fn run_with_ip(&mut self) -> CompletedTest<BootNodeConfigSerialization> {
self.cmd.arg(IP_ADDRESS);
self.run()
}
}
impl CommandLineTestExec for CommandLineTest {
type Config = BootNodeConfigSerialization;
fn cmd_mut(&mut self) -> &mut Command {
&mut self.cmd
}
}
fn unused_port() -> u16 {
let socket =
UdpSocket::bind("127.0.0.1:0").expect("should create udp socket to find unused port");
let local_addr = socket
.local_addr()
.expect("should read udp socket to find unused port");
local_addr.port()
}
#[test]
fn enr_address_arg() {
let mut test = CommandLineTest::new();
test.run_with_ip().with_config(|config| {
assert_eq!(config.local_enr.ip(), Some(IP_ADDRESS.parse().unwrap()));
});
}
#[test]
fn port_flag() {
let port = unused_port();
CommandLineTest::new()
.flag("port", Some(port.to_string().as_str()))
.run_with_ip()
.with_config(|config| {
assert_eq!(config.listen_socket.port(), port);
})
}
#[test]
fn listen_address_flag() {
let addr = "127.0.0.2".parse::<Ipv4Addr>().unwrap();
CommandLineTest::new()
.flag("listen-address", Some("127.0.0.2"))
.run_with_ip()
.with_config(|config| {
assert_eq!(config.listen_socket.ip(), addr);
});
}
#[test]
fn boot_nodes_flag() {
// Get hardcoded boot-nodes to verify they end up in the config.
// Pass empty `ArgMatches` to `get_eth2_network_config` as we want to
// receive the default `Eth2NetworkConfig`.
let empty_args = ArgMatches::default();
let default_enr = get_eth2_network_config(&empty_args)
.unwrap()
.boot_enr
.unwrap();
let default_enr: Vec<String> = default_enr.iter().map(|enr| enr.to_base64()).collect();
// Nodes passed via `--boot-nodes` are added to the local routing table.
let extra_nodes = "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8,enr:-LK4QFOFWca5ABQzxiCRcy37G7wy1K6zD4qMYBSN5ozzanwze_XVvXVhCk9JvF0cHXOBZrHK1E4vU7Gn-a0bHVczoDU6h2F0dG5ldHOIAAAAAAAAAACEZXRoMpA7CIeVAAAgCf__________gmlkgnY0gmlwhNIy-4iJc2VjcDI1NmsxoQJA3AXQJ6M3NpBWtJS3HPtbXG14t7qHjXuIaL6IOz89T4N0Y3CCIyiDdWRwgiMo";
let extra_enr: Vec<&str> = extra_nodes.split(",").collect();
// Construct vector of enr expected in config.
let default_enr_str: Vec<&str> = default_enr.iter().map(|s| s.as_str()).collect();
let mut expect_enr = Vec::new();
expect_enr.extend_from_slice(&default_enr_str);
expect_enr.extend_from_slice(&extra_enr);
CommandLineTest::new()
.flag("boot-nodes", Some(extra_nodes))
.run_with_ip()
.with_config(|config| {
assert_eq!(config.boot_nodes.len(), expect_enr.len());
for (i, enr) in config.boot_nodes.iter().enumerate() {
assert_eq!(
enr.to_base64(),
expect_enr[i],
"ENR missmatch at index [{}]",
i
);
}
})
}
#[test]
fn enr_port_flag() {
let port = unused_port();
CommandLineTest::new()
.flag("enr-port", Some(port.to_string().as_str()))
.run_with_ip()
.with_config(|config| {
assert_eq!(config.local_enr.udp(), Some(port));
})
}
// TODO add tests for flags `enable-enr-auto-update` and `disable-packet-filter`.
//
// These options end up in `Discv5Config`, which doesn't support serde (de)serialization.
#[test]
fn network_dir_flag() {
// Save enr to temp dir.
let enr = Enr::from_str("enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8").unwrap();
let tmp_dir = TempDir::new().unwrap();
save_enr_to_disk(tmp_dir.path(), &enr).unwrap();
CommandLineTest::new()
.flag("network-dir", Some(tmp_dir.path().to_str().unwrap()))
.run()
.with_config(|config| assert_eq!(config.local_enr, enr))
}
fn save_enr_to_disk(dir: &Path, enr: &Enr) -> Result<(), String> {
let mut file = File::create(dir.join(Path::new(ENR_FILENAME)))
.map_err(|e| format!("Could not create ENR file: {:?}", e))?;
file.write_all(enr.to_base64().as_bytes())
.map_err(|e| format!("Could not write ENR to file: {:?}", e))
}

113
lighthouse/tests/exec.rs Normal file
View File

@ -0,0 +1,113 @@
use serde::de::DeserializeOwned;
use serde_json::from_reader;
use std::fs::File;
use std::path::PathBuf;
use std::process::{Command, Output};
use std::str::from_utf8;
use tempfile::TempDir;
pub trait CommandLineTestExec {
type Config: DeserializeOwned;
fn cmd_mut(&mut self) -> &mut Command;
/// Adds a flag with optional value to the command.
fn flag(&mut self, flag: &str, value: Option<&str>) -> &mut Self {
self.cmd_mut().arg(format!("--{}", flag));
if let Some(value) = value {
self.cmd_mut().arg(value);
}
self
}
/// Executes the `Command` returned by `Self::cmd_mut` with temporary data directory, dumps
/// the configuration and shuts down immediately.
///
/// Options `--datadir`, `--dump-config` and `--immediate-shutdown` must not be set on the
/// command.
fn run(&mut self) -> CompletedTest<Self::Config> {
// Setup temp directory.
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_config_path: PathBuf = tmp_dir.path().join("config.json");
// Add args --datadir <tmp_dir> --dump-config <tmp_config_path> --immediate-shutdown
let cmd = self.cmd_mut();
cmd.arg("--datadir")
.arg(tmp_dir.path().as_os_str())
.arg(format!("--{}", "--dump-config"))
.arg(tmp_config_path.as_os_str())
.arg("--immediate-shutdown");
// Run the command.
let output = output_result(cmd);
if let Err(e) = output {
panic!("{:?}", e);
}
// Grab the config.
let config_file = File::open(tmp_config_path).expect("Unable to open dumped config");
let config: Self::Config = from_reader(config_file).expect("Unable to deserialize config");
CompletedTest::new(config, tmp_dir)
}
/// Executes the `Command` returned by `Self::cmd_mut` dumps the configuration and
/// shuts down immediately.
///
/// Options `--dump-config` and `--immediate-shutdown` must not be set on the command.
fn run_with_no_datadir(&mut self) -> CompletedTest<Self::Config> {
// Setup temp directory.
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_config_path: PathBuf = tmp_dir.path().join("config.json");
// Add args --datadir <tmp_dir> --dump-config <tmp_config_path> --immediate-shutdown
let cmd = self.cmd_mut();
cmd.arg(format!("--{}", "--dump-config"))
.arg(tmp_config_path.as_os_str())
.arg("--immediate-shutdown");
// Run the command.
let output = output_result(cmd);
if let Err(e) = output {
panic!("{:?}", e);
}
// Grab the config.
let config_file = File::open(tmp_config_path).expect("Unable to open dumped config");
let config: Self::Config = from_reader(config_file).expect("Unable to deserialize config");
CompletedTest::new(config, tmp_dir)
}
}
/// Executes a `Command`, returning a `Result` based upon the success exit code of the command.
fn output_result(cmd: &mut Command) -> Result<Output, String> {
let output = cmd.output().expect("should run command");
if output.status.success() {
Ok(output)
} else {
Err(from_utf8(&output.stderr)
.expect("stderr is not utf8")
.to_string())
}
}
pub struct CompletedTest<C> {
config: C,
dir: TempDir,
}
impl<C> CompletedTest<C> {
fn new(config: C, dir: TempDir) -> Self {
CompletedTest { config, dir }
}
pub fn with_config<F: Fn(&C)>(self, func: F) {
func(&self.config);
}
pub fn with_config_and_dir<F: Fn(&C, &TempDir)>(self, func: F) {
func(&self.config, &self.dir);
}
}

View File

@ -2,4 +2,6 @@
mod account_manager;
mod beacon_node;
mod boot_node;
mod exec;
mod validator_client;

View File

@ -1,22 +1,16 @@
use validator_client::Config;
use crate::exec::CommandLineTestExec;
use bls::{Keypair, PublicKeyBytes};
use serde_json::from_reader;
use std::fs::File;
use std::io::Write;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use std::process::{Command, Output};
use std::str::from_utf8;
use std::process::Command;
use std::string::ToString;
use tempfile::TempDir;
const VALIDATOR_CMD: &str = "validator_client";
const CONFIG_NAME: &str = "vc_dump.json";
const DUMP_CONFIG_CMD: &str = "dump-config";
const IMMEDIATE_SHUTDOWN_CMD: &str = "immediate-shutdown";
/// Returns the `lighthouse validator_client --immediate-shutdown` command.
/// Returns the `lighthouse validator_client` command.
fn base_cmd() -> Command {
let lighthouse_bin = env!("CARGO_BIN_EXE_lighthouse");
let path = lighthouse_bin
@ -24,25 +18,10 @@ fn base_cmd() -> Command {
.expect("should parse CARGO_TARGET_DIR");
let mut cmd = Command::new(path);
cmd.arg(VALIDATOR_CMD)
.arg(format!("--{}", IMMEDIATE_SHUTDOWN_CMD));
cmd.arg("validator_client");
cmd
}
/// Executes a `Command`, returning a `Result` based upon the success exit code of the command.
fn output_result(cmd: &mut Command) -> Result<Output, String> {
let output = cmd.output().expect("should run command");
if output.status.success() {
Ok(output)
} else {
Err(from_utf8(&output.stderr)
.expect("stderr is not utf8")
.to_string())
}
}
// Wrapper around `Command` for easier Command Line Testing.
struct CommandLineTest {
cmd: Command,
@ -52,76 +31,13 @@ impl CommandLineTest {
let base_cmd = base_cmd();
CommandLineTest { cmd: base_cmd }
}
fn flag(mut self, flag: &str, value: Option<&str>) -> Self {
// Build the command by adding the flag and any values.
self.cmd.arg(format!("--{}", flag));
if let Some(value) = value {
self.cmd.arg(value);
}
self
}
fn run(&mut self) -> CompletedTest {
// Setup temp directories.
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_path: PathBuf = tmp_dir.path().join(CONFIG_NAME);
// Add --datadir <temp_dir> --dump-config <temp_path> to cmd.
self.cmd
.arg("--datadir")
.arg(tmp_dir.path().as_os_str())
.arg(format!("--{}", DUMP_CONFIG_CMD))
.arg(tmp_path.as_os_str());
// Run the command.
let _output = output_result(&mut self.cmd).expect("Unable to run command");
// Grab the config.
let config: Config =
from_reader(File::open(tmp_path).expect("Unable to open dumped config"))
.expect("Unable to deserialize to ClientConfig");
CompletedTest {
config,
dir: tmp_dir,
}
}
// In order to test custom validator and secrets directory flags,
// datadir cannot be defined.
fn run_with_no_datadir(&mut self) -> CompletedTest {
// Setup temp directories
let tmp_dir = TempDir::new().expect("Unable to create temporary directory");
let tmp_path: PathBuf = tmp_dir.path().join(CONFIG_NAME);
// Add --dump-config <temp_path> to cmd.
self.cmd
.arg(format!("--{}", DUMP_CONFIG_CMD))
.arg(tmp_path.as_os_str());
// Run the command.
let _output = output_result(&mut self.cmd).expect("Unable to run command");
// Grab the config.
let config: Config =
from_reader(File::open(tmp_path).expect("Unable to open dumped config"))
.expect("Unable to deserialize to ClientConfig");
CompletedTest {
config,
dir: tmp_dir,
}
}
}
struct CompletedTest {
config: Config,
dir: TempDir,
}
impl CompletedTest {
fn with_config<F: Fn(&Config)>(self, func: F) {
func(&self.config);
}
fn with_config_and_dir<F: Fn(&Config, &TempDir)>(self, func: F) {
func(&self.config, &self.dir);
impl CommandLineTestExec for CommandLineTest {
type Config = Config;
fn cmd_mut(&mut self) -> &mut Command {
&mut self.cmd
}
}