diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 45cd69ff3a..175d64371a 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -36,6 +36,8 @@ pub fn get_configs( let mut client_config = ClientConfig::default(); + client_config.spec_constants = eth2_config.spec_constants.clone(); + // Read the `--datadir` flag. // // If it's not present, try and find the home directory (`~`) and push the default data @@ -57,9 +59,23 @@ pub fn get_configs( // Load the eth2 config, if it exists . let path = client_config.data_dir.join(ETH2_CONFIG_FILENAME); if path.exists() { - eth2_config = read_from_file(path.clone()) + let loaded_eth2_config: Eth2Config = read_from_file(path.clone()) .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))? .ok_or_else(|| format!("{:?} file does not exist", path))?; + + // The loaded spec must be using the same spec constants (e.g., minimal, mainnet) as the + // client expects. + if loaded_eth2_config.spec_constants == client_config.spec_constants { + eth2_config = loaded_eth2_config + } else { + return Err( + format!( + "Eth2 config loaded from disk does not match client spec version. Got {} expected {}", + &loaded_eth2_config.spec_constants, + &client_config.spec_constants + ) + ); + } } // Read the `--testnet-dir` flag. diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 4a36d631a7..bd36e82afc 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -407,6 +407,9 @@ pub struct YamlConfig { max_deposits: u32, max_voluntary_exits: u32, + // Eth1 + eth1_follow_distance: u64, + // Unused #[serde(skip_serializing)] early_derived_secret_penalty_max_future_epochs: u32, @@ -503,6 +506,9 @@ impl YamlConfig { max_deposits: T::MaxDeposits::to_u32(), max_voluntary_exits: T::MaxVoluntaryExits::to_u32(), + // Eth1 + eth1_follow_distance: spec.eth1_follow_distance, + // Unused early_derived_secret_penalty_max_future_epochs: 0, max_seed_lookahead: 0, @@ -580,6 +586,7 @@ impl YamlConfig { domain_voluntary_exit: self.domain_voluntary_exit, boot_nodes: chain_spec.boot_nodes.clone(), genesis_fork: chain_spec.genesis_fork.clone(), + eth1_follow_distance: self.eth1_follow_distance, ..*chain_spec }) } diff --git a/lcli/src/deploy_deposit_contract.rs b/lcli/src/deploy_deposit_contract.rs index 41a1928110..7ae0b33627 100644 --- a/lcli/src/deploy_deposit_contract.rs +++ b/lcli/src/deploy_deposit_contract.rs @@ -17,6 +17,12 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< .parse::() .map_err(|e| format!("Failed to parse min_genesis_time: {}", e))?; + let min_genesis_active_validator_count = matches + .value_of("min-genesis-active-validator-count") + .ok_or_else(|| "min-genesis-active-validator-count not specified")? + .parse::() + .map_err(|e| format!("Failed to parse min-genesis-active-validator-count: {}", e))?; + let confirmations = matches .value_of("confirmations") .ok_or_else(|| "Confirmations not specified")? @@ -90,6 +96,7 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< let mut spec = lighthouse_testnet_spec(env.core_context().eth2_config.spec.clone()); spec.min_genesis_time = min_genesis_time; + spec.min_genesis_active_validator_count = min_genesis_active_validator_count; let testnet_config: Eth2TestnetConfig = Eth2TestnetConfig { deposit_contract_address: format!("{}", deposit_contract.address()), @@ -111,13 +118,17 @@ pub fn lighthouse_testnet_spec(mut spec: ChainSpec) -> ChainSpec { spec.ejection_balance = 1_600_000_000; spec.effective_balance_increment = 100_000_000; + spec.eth1_follow_distance = 16; + // This value must be at least 2x the `ETH1_FOLLOW_DISTANCE` otherwise `all_eth1_data` can // become a subset of `new_eth1_data` which may result in an Exception in the spec // implementation. // // This value determines the delay between the eth1 block that triggers genesis and the first // slot of that new chain. - spec.seconds_per_day = SECONDS_PER_ETH1_BLOCK * spec.eth1_follow_distance * 2; + // + // With a follow distance of 16, this is 40mins. + spec.seconds_per_day = SECONDS_PER_ETH1_BLOCK * spec.eth1_follow_distance * 2 * 5; spec } diff --git a/lcli/src/main.rs b/lcli/src/main.rs index bc16737c2a..76811192a0 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -4,23 +4,19 @@ extern crate log; mod deploy_deposit_contract; mod eth1_genesis; mod parse_hex; -mod pycli; mod refund_deposit_contract; mod transition_blocks; -use clap::{App, Arg, SubCommand}; +use clap::{App, Arg, ArgMatches, SubCommand}; use environment::EnvironmentBuilder; use log::Level; use parse_hex::run_parse_hex; -use pycli::run_pycli; use std::fs::File; use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; use transition_blocks::run_transition_blocks; use types::{test_utils::TestingBeaconStateBuilder, EthSpec, MainnetEthSpec, MinimalEthSpec}; -type LocalEthSpec = MinimalEthSpec; - fn main() { simple_logger::init_with_level(Level::Info).expect("logger should initialize"); @@ -55,7 +51,7 @@ fn main() { .takes_value(true) .required(true) .possible_values(&["minimal", "mainnet"]) - .default_value("minimal") + .default_value("mainnet") .help("Eth2 genesis time (seconds since UNIX epoch)."), ) .arg( @@ -135,6 +131,15 @@ fn main() { .default_value("0") .help("The MIN_GENESIS_TIME constant."), ) + .arg( + Arg::with_name("min-genesis-active-validator-count") + .short("v") + .long("min-genesis-active-validator-count") + .value_name("INTEGER") + .takes_value(true) + .default_value("64") + .help("The MIN_GENESIS_ACTIVE_VALIDATOR_COUNT constant."), + ) .arg( Arg::with_name("eth1-endpoint") .short("e") @@ -222,22 +227,27 @@ fn main() { .help("The URL to the eth1 JSON-RPC http API."), ) ) - .subcommand( - SubCommand::with_name("pycli") - .about("TODO") - .arg( - Arg::with_name("pycli-path") - .long("pycli-path") - .short("p") - .value_name("PATH") - .takes_value(true) - .default_value("../../pycli") - .help("Path to the pycli repository."), - ), - ) .get_matches(); - let env = EnvironmentBuilder::minimal() + macro_rules! run_with_spec { + ($env_builder: expr) => { + run($env_builder, &matches) + }; + } + + match matches.value_of("spec") { + Some("minimal") => run_with_spec!(EnvironmentBuilder::minimal()), + Some("mainnet") => run_with_spec!(EnvironmentBuilder::mainnet()), + Some("interop") => run_with_spec!(EnvironmentBuilder::interop()), + spec => { + // This path should be unreachable due to slog having a `default_value` + unreachable!("Unknown spec configuration: {:?}", spec); + } + } +} + +fn run(env_builder: EnvironmentBuilder, matches: &ArgMatches) { + let env = env_builder .multi_threaded_tokio_runtime() .expect("should start tokio runtime") .async_logger("trace") @@ -283,22 +293,19 @@ fn main() { }; info!("Genesis state YAML file created. Exiting successfully."); } - ("transition-blocks", Some(matches)) => run_transition_blocks(matches) + ("transition-blocks", Some(matches)) => run_transition_blocks::(matches) .unwrap_or_else(|e| error!("Failed to transition blocks: {}", e)), - ("pretty-hex", Some(matches)) => { - run_parse_hex(matches).unwrap_or_else(|e| error!("Failed to pretty print hex: {}", e)) - } - ("pycli", Some(matches)) => run_pycli::(matches) - .unwrap_or_else(|e| error!("Failed to run pycli: {}", e)), + ("pretty-hex", Some(matches)) => run_parse_hex::(matches) + .unwrap_or_else(|e| error!("Failed to pretty print hex: {}", e)), ("deploy-deposit-contract", Some(matches)) => { - deploy_deposit_contract::run::(env, matches) + deploy_deposit_contract::run::(env, matches) .unwrap_or_else(|e| error!("Failed to run deploy-deposit-contract command: {}", e)) } ("refund-deposit-contract", Some(matches)) => { - refund_deposit_contract::run::(env, matches) + refund_deposit_contract::run::(env, matches) .unwrap_or_else(|e| error!("Failed to run refund-deposit-contract command: {}", e)) } - ("eth1-genesis", Some(matches)) => eth1_genesis::run::(env, matches) + ("eth1-genesis", Some(matches)) => eth1_genesis::run::(env, matches) .unwrap_or_else(|e| error!("Failed to run eth1-genesis command: {}", e)), (other, _) => error!("Unknown subcommand {}. See --help.", other), } diff --git a/lcli/src/parse_hex.rs b/lcli/src/parse_hex.rs index 7f2f7f90ff..8f1e3b7080 100644 --- a/lcli/src/parse_hex.rs +++ b/lcli/src/parse_hex.rs @@ -1,9 +1,9 @@ use clap::ArgMatches; use serde::Serialize; use ssz::Decode; -use types::{BeaconBlock, BeaconState, MinimalEthSpec}; +use types::{BeaconBlock, BeaconState, EthSpec}; -pub fn run_parse_hex(matches: &ArgMatches) -> Result<(), String> { +pub fn run_parse_hex(matches: &ArgMatches) -> Result<(), String> { let type_str = matches .value_of("type") .ok_or_else(|| "No type supplied".to_string())?; @@ -22,8 +22,8 @@ pub fn run_parse_hex(matches: &ArgMatches) -> Result<(), String> { info!("Type: {:?}", type_str); match type_str { - "block" => decode_and_print::>(&hex)?, - "state" => decode_and_print::>(&hex)?, + "block" => decode_and_print::>(&hex)?, + "state" => decode_and_print::>(&hex)?, other => return Err(format!("Unknown type: {}", other)), }; diff --git a/lcli/src/pycli.rs b/lcli/src/pycli.rs deleted file mode 100644 index dda61dd721..0000000000 --- a/lcli/src/pycli.rs +++ /dev/null @@ -1,79 +0,0 @@ -use clap::ArgMatches; -use ssz::Decode; -use std::fs; -use std::path::PathBuf; -use std::process::Command; -use types::{BeaconState, EthSpec}; - -pub fn run_pycli(matches: &ArgMatches) -> Result<(), String> { - let cmd_path = matches - .value_of("pycli-path") - .ok_or_else(|| "No pycli-path supplied")?; - - let pycli = PyCli::new(cmd_path.to_string())?; - - let block_path = PathBuf::from("/tmp/trinity/block_16.ssz"); - let pre_state_path = PathBuf::from("/tmp/trinity/state_15.ssz"); - - pycli - .transition_blocks::(block_path, pre_state_path) - .map_err(|e| e.to_string())?; - - Ok(()) -} - -/// A wrapper around Danny Ryan's `pycli` utility: -/// -/// https://github.com/djrtwo/pycli -/// -/// Provides functions for testing consensus logic against the executable Python spec. -pub struct PyCli { - cmd_path: PathBuf, -} - -impl PyCli { - /// Create a new instance, parsing the given `cmd_path` as a canonical path. - pub fn new(cmd_path: String) -> Result { - Ok(Self { - cmd_path: fs::canonicalize(cmd_path) - .map_err(|e| format!("Failed to canonicalize pycli path: {:?}", e))?, - }) - } - - /// Performs block processing on the state at the given `pre_state_path`, using the block at - /// `block_path`. - /// - /// Returns an SSZ-encoded `BeaconState` on success. - pub fn transition_blocks( - &self, - block_path: PathBuf, - pre_state_path: PathBuf, - ) -> Result, String> { - let output = Command::new("python") - .current_dir(self.cmd_path.clone()) - .arg("pycli.py") - .arg("transition") - .arg("blocks") - .arg(format!("--pre={}", path_string(pre_state_path)?)) - .arg(path_string(block_path)?) - .output() - .map_err(|e| format!("Failed to run command: {:?}", e))?; - - if output.status.success() { - let state = BeaconState::from_ssz_bytes(&output.stdout) - .map_err(|e| format!("Failed to parse SSZ: {:?}", e))?; - Ok(state) - } else { - Err(format!("pycli returned an error: {:?}", output)) - } - } -} - -fn path_string(path: PathBuf) -> Result { - let path = - fs::canonicalize(path).map_err(|e| format!("Unable to canonicalize path: {:?}", e))?; - - path.into_os_string() - .into_string() - .map_err(|p| format!("Unable to stringify path: {:?}", p)) -} diff --git a/lcli/src/transition_blocks.rs b/lcli/src/transition_blocks.rs index 01e639db8a..7428011c2f 100644 --- a/lcli/src/transition_blocks.rs +++ b/lcli/src/transition_blocks.rs @@ -4,9 +4,9 @@ use state_processing::{per_block_processing, per_slot_processing, BlockSignature use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; -use types::{BeaconBlock, BeaconState, EthSpec, MinimalEthSpec}; +use types::{BeaconBlock, BeaconState, EthSpec}; -pub fn run_transition_blocks(matches: &ArgMatches) -> Result<(), String> { +pub fn run_transition_blocks(matches: &ArgMatches) -> Result<(), String> { let pre_state_path = matches .value_of("pre-state") .ok_or_else(|| "No pre-state file supplied".to_string())? @@ -29,8 +29,8 @@ pub fn run_transition_blocks(matches: &ArgMatches) -> Result<(), String> { info!("Pre-state path: {:?}", pre_state_path); info!("Block path: {:?}", block_path); - let pre_state: BeaconState = load_from_ssz(pre_state_path)?; - let block: BeaconBlock = load_from_ssz(block_path)?; + let pre_state: BeaconState = load_from_ssz(pre_state_path)?; + let block: BeaconBlock = load_from_ssz(block_path)?; let post_state = do_transition(pre_state, block)?; diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index 72c92f88ba..7e5cf987ad 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -33,7 +33,7 @@ fn main() { .takes_value(true) .possible_values(&["mainnet", "minimal", "interop"]) .global(true) - .default_value("minimal"), + .default_value("mainnet"), ) .arg( Arg::with_name("logfile")