From 5f0509be501585f1043111df720f157441e8567a Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Sat, 31 Aug 2019 12:34:27 +1000 Subject: [PATCH] Improve and extend CLI interface --- beacon_node/beacon_chain/src/beacon_chain.rs | 19 +++-- beacon_node/client/src/lib.rs | 84 ++++++++++++-------- beacon_node/src/config.rs | 38 ++++++++- beacon_node/src/main.rs | 42 +++++++++- beacon_node/src/run.rs | 7 +- 5 files changed, 141 insertions(+), 49 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index afc7a992a..6380d03b3 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -158,12 +158,6 @@ impl BeaconChain { genesis_state_root, )); - info!(log, "BeaconChain init"; - "genesis_validator_count" => genesis_state.validators.len(), - "genesis_state_root" => format!("{}", genesis_state_root), - "genesis_block_root" => format!("{}", genesis_block_root), - ); - // Slot clock let slot_clock = T::SlotClock::from_eth2_genesis( spec.genesis_slot, @@ -172,6 +166,12 @@ impl BeaconChain { ) .ok_or_else(|| Error::SlotClockDidNotStart)?; + info!(log, "Beacon chain initialized from genesis"; + "validator_count" => genesis_state.validators.len(), + "state_root" => format!("{}", genesis_state_root), + "block_root" => format!("{}", genesis_block_root), + ); + Ok(Self { spec, slot_clock, @@ -211,6 +211,13 @@ impl BeaconChain { let op_pool = p.op_pool.into_operation_pool(state, &spec); + info!(log, "Beacon chain initialized from store"; + "head_root" => format!("{}", p.canonical_head.beacon_block_root), + "head_epoch" => format!("{}", p.canonical_head.beacon_block.slot.epoch(T::EthSpec::slots_per_epoch())), + "finalized_root" => format!("{}", last_finalized_root), + "finalized_epoch" => format!("{}", last_finalized_block.slot.epoch(T::EthSpec::slots_per_epoch())), + ); + Ok(Some(BeaconChain { spec, slot_clock, diff --git a/beacon_node/client/src/lib.rs b/beacon_node/client/src/lib.rs index c7558dd5e..766d12c56 100644 --- a/beacon_node/client/src/lib.rs +++ b/beacon_node/client/src/lib.rs @@ -82,34 +82,70 @@ where let beacon_chain_builder = match &client_config.beacon_chain_start_method { BeaconChainStartMethod::Resume => { + info!( + log, + "Starting beacon chain"; + "method" => "resume" + ); BeaconChainBuilder::from_store(spec.clone(), log.clone()) } BeaconChainStartMethod::Mainnet => { crit!(log, "No mainnet beacon chain startup specification."); - return Err("Mainnet is not yet specified. We're working on it.".into()); + return Err("Mainnet launch is not yet announced.".into()); } BeaconChainStartMethod::RecentGenesis { validator_count, minutes, - } => BeaconChainBuilder::recent_genesis( - *validator_count, - *minutes, - spec.clone(), - log.clone(), - )?, + } => { + info!( + log, + "Starting beacon chain"; + "validator_count" => validator_count, + "minutes" => minutes, + "method" => "recent" + ); + BeaconChainBuilder::recent_genesis( + *validator_count, + *minutes, + spec.clone(), + log.clone(), + )? + } BeaconChainStartMethod::Generated { validator_count, genesis_time, - } => BeaconChainBuilder::quick_start( - *genesis_time, - *validator_count, - spec.clone(), - log.clone(), - )?, + } => { + info!( + log, + "Starting beacon chain"; + "validator_count" => validator_count, + "genesis_time" => genesis_time, + "method" => "quick" + ); + BeaconChainBuilder::quick_start( + *genesis_time, + *validator_count, + spec.clone(), + log.clone(), + )? + } BeaconChainStartMethod::Yaml { file } => { + info!( + log, + "Starting beacon chain"; + "file" => format!("{:?}", file), + "method" => "yaml" + ); BeaconChainBuilder::yaml_state(file, spec.clone(), log.clone())? } - BeaconChainStartMethod::HttpBootstrap { server, .. } => { + BeaconChainStartMethod::HttpBootstrap { server, port } => { + info!( + log, + "Starting beacon chain"; + "port" => port, + "server" => server, + "method" => "bootstrap" + ); BeaconChainBuilder::http_bootstrap(server, spec.clone(), log.clone())? } }; @@ -124,26 +160,6 @@ where panic!("Cannot start client before genesis!") } - // Block starting the client until we have caught the state up to the current slot. - // - // If we don't block here we create an initial scenario where we're unable to process any - // blocks and we're basically useless. - { - let state_slot = beacon_chain.head().beacon_state.slot; - let wall_clock_slot = beacon_chain - .slot() - .expect("Cannot start client before genesis"); - let slots_since_genesis = beacon_chain.slots_since_genesis().unwrap(); - info!( - log, - "BeaconState cache init"; - "state_slot" => state_slot, - "wall_clock_slot" => wall_clock_slot, - "slots_since_genesis" => slots_since_genesis, - "catchup_distance" => wall_clock_slot - state_slot, - ); - } - let network_config = &client_config.network; let (network, network_send) = NetworkService::new(beacon_chain.clone(), network_config, executor, log.clone())?; diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 7c471e8ac..c4fa5eebc 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -15,6 +15,12 @@ type Result = std::result::Result; type Config = (ClientConfig, Eth2Config); /// Gets the fully-initialized global client and eth2 configuration objects. +/// +/// The top-level `clap` arguments should be provied as `cli_args`. +/// +/// The output of this function depends primarily upon the given `cli_args`, however it's behaviour +/// may be influenced by other external services like the contents of the file system or the +/// response of some remote server. pub fn get_configs(cli_args: &ArgMatches, log: &Logger) -> Result { let mut builder = ConfigBuilder::new(cli_args, log)?; @@ -95,7 +101,7 @@ fn process_testnet_subcommand( "path" => format!("{:?}", builder.data_dir) ); - // Start matching on the second subcommand (e.g., `testnet bootstrap ...`) + // Start matching on the second subcommand (e.g., `testnet bootstrap ...`). match cli_args.subcommand() { ("bootstrap", Some(cli_args)) => { let server = cli_args @@ -131,6 +137,24 @@ fn process_testnet_subcommand( minutes, }) } + ("quick", Some(cli_args)) => { + let validator_count = cli_args + .value_of("validator_count") + .ok_or_else(|| "No validator_count specified")? + .parse::() + .map_err(|e| format!("Unable to parse validator_count: {:?}", e))?; + + let genesis_time = cli_args + .value_of("genesis_time") + .ok_or_else(|| "No genesis time supplied")? + .parse::() + .map_err(|e| format!("Unable to parse genesis time: {:?}", e))?; + + builder.set_beacon_chain_start_method(BeaconChainStartMethod::Generated { + validator_count, + genesis_time, + }) + } _ => return Err("No testnet method specified. See 'testnet --help'.".into()), }; @@ -420,6 +444,18 @@ impl<'a> ConfigBuilder<'a> { self.client_config .apply_cli_args(cli_args, &mut self.log.clone())?; + if let Some(bump) = cli_args.value_of("port-bump") { + let bump = bump + .parse::() + .map_err(|e| format!("Unable to parse port bump: {}", e))?; + + self.client_config.network.libp2p_port += bump; + self.client_config.network.discovery_port += bump; + self.client_config.rpc.port += bump; + self.client_config.rpc.port += bump; + self.client_config.rest_api.port += bump; + } + if self.eth2_config.spec_constants != self.client_config.spec_constants { crit!(self.log, "Specification constants do not match."; "client_config" => format!("{}", self.client_config.spec_constants), diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 8ab20a481..02e30b660 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -48,18 +48,29 @@ fn main() { /* * Network parameters. */ + .arg( + Arg::with_name("port-bump") + .long("port-bump") + .short("b") + .value_name("INCREMENT") + .help("Sets all listening TCP/UDP ports to default values, but with each port increased by \ + INCREMENT. Useful when starting multiple nodes on a single machine. Using increments \ + in multiples of 10 is recommended.") + .takes_value(true), + ) .arg( Arg::with_name("listen-address") .long("listen-address") .value_name("ADDRESS") .help("The address lighthouse will listen for UDP and TCP connections. (default 127.0.0.1).") - .takes_value(true), + .takes_value(true) ) .arg( Arg::with_name("port") .long("port") .value_name("PORT") .help("The TCP/UDP port to listen on. The UDP port can be modified by the --discovery-port flag.") + .conflicts_with("port-bump") .takes_value(true), ) .arg( @@ -81,6 +92,7 @@ fn main() { .long("disc-port") .value_name("PORT") .help("The discovery UDP port.") + .conflicts_with("port-bump") .takes_value(true), ) .arg( @@ -125,6 +137,7 @@ fn main() { Arg::with_name("rpc-port") .long("rpc-port") .help("Listen port for RPC endpoint.") + .conflicts_with("port-bump") .takes_value(true), ) /* Client related arguments */ @@ -147,6 +160,7 @@ fn main() { .long("api-port") .value_name("APIPORT") .help("Set the listen TCP port for the RESTful HTTP API server.") + .conflicts_with("port-bump") .takes_value(true), ) @@ -230,8 +244,6 @@ fn main() { .conflicts_with("random-datadir") ) /* - * Testnet sub-commands. - * * `boostrap` * * Start a new node by downloading genesis and network info from another node via the @@ -272,7 +284,29 @@ fn main() { .default_value("15") .help("The maximum number of minutes that will have elapsed before genesis")) ) - .subcommand(SubCommand::with_name("yaml-genesis-state") + /* + * `quick` + * + * Start a new node, specifying the number of validators and genesis time + */ + .subcommand(SubCommand::with_name("quick") + .about("Creates a new genesis state from the specified validator count and genesis time. \ + Compatible with the `quick-start genesis` defined in the eth2.0-pm repo.") + .arg(Arg::with_name("validator_count") + .value_name("VALIDATOR_COUNT") + .required(true) + .help("The number of validators in the genesis state")) + .arg(Arg::with_name("genesis_time") + .value_name("UNIX_EPOCH_SECONDS") + .required(true) + .help("The genesis time for the given state.")) + ) + /* + * `yaml` + * + * Start a new node, using a genesis state loaded from a YAML file + */ + .subcommand(SubCommand::with_name("yaml") .about("Creates a new datadir where the genesis state is read from YAML. Will fail to parse \ a YAML state that was generated to a different spec than that specified by --spec.") .arg(Arg::with_name("file") diff --git a/beacon_node/src/run.rs b/beacon_node/src/run.rs index 620cb64bb..26225cc92 100644 --- a/beacon_node/src/run.rs +++ b/beacon_node/src/run.rs @@ -41,11 +41,10 @@ pub fn run_beacon_node( info!( log, - "BeaconNode init"; - "p2p_listen_address" => format!("{:?}", &other_client_config.network.listen_address), - "network_dir" => format!("{:?}", other_client_config.network.network_dir), - "spec_constants" => &spec_constants, + "Starting beacon node"; + "p2p_listen_address" => format!("{}", &other_client_config.network.listen_address), "db_type" => &other_client_config.db_type, + "spec_constants" => &spec_constants, ); match (db_type.as_str(), spec_constants.as_str()) {