Fix issues when starting with mainnet spec (#674)

* Update lcli to parse spec at boot, remove pycli

* Fix issues when starting with mainnet spec

* Set default spec to mainnet

* Ensure ETH1_FOLLOW_DISTANCE is in YamlConfig

* Set testnet ETH1_FOLLOW_DISTANCE to 16

* Set testnet min validator count

* Add validator count CLI flag to lcli contract deploy

* Extend genesis delay time
This commit is contained in:
Paul Hauner 2019-12-09 17:23:56 +11:00 committed by GitHub
parent bfbb556f02
commit 988059bc9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 119 deletions

View File

@ -36,6 +36,8 @@ pub fn get_configs<E: EthSpec>(
let mut client_config = ClientConfig::default(); let mut client_config = ClientConfig::default();
client_config.spec_constants = eth2_config.spec_constants.clone();
// Read the `--datadir` flag. // Read the `--datadir` flag.
// //
// If it's not present, try and find the home directory (`~`) and push the default data // If it's not present, try and find the home directory (`~`) and push the default data
@ -57,9 +59,23 @@ pub fn get_configs<E: EthSpec>(
// Load the eth2 config, if it exists . // Load the eth2 config, if it exists .
let path = client_config.data_dir.join(ETH2_CONFIG_FILENAME); let path = client_config.data_dir.join(ETH2_CONFIG_FILENAME);
if path.exists() { 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))? .map_err(|e| format!("Unable to parse {:?} file: {:?}", path, e))?
.ok_or_else(|| format!("{:?} file does not exist", path))?; .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. // Read the `--testnet-dir` flag.

View File

@ -407,6 +407,9 @@ pub struct YamlConfig {
max_deposits: u32, max_deposits: u32,
max_voluntary_exits: u32, max_voluntary_exits: u32,
// Eth1
eth1_follow_distance: u64,
// Unused // Unused
#[serde(skip_serializing)] #[serde(skip_serializing)]
early_derived_secret_penalty_max_future_epochs: u32, early_derived_secret_penalty_max_future_epochs: u32,
@ -503,6 +506,9 @@ impl YamlConfig {
max_deposits: T::MaxDeposits::to_u32(), max_deposits: T::MaxDeposits::to_u32(),
max_voluntary_exits: T::MaxVoluntaryExits::to_u32(), max_voluntary_exits: T::MaxVoluntaryExits::to_u32(),
// Eth1
eth1_follow_distance: spec.eth1_follow_distance,
// Unused // Unused
early_derived_secret_penalty_max_future_epochs: 0, early_derived_secret_penalty_max_future_epochs: 0,
max_seed_lookahead: 0, max_seed_lookahead: 0,
@ -580,6 +586,7 @@ impl YamlConfig {
domain_voluntary_exit: self.domain_voluntary_exit, domain_voluntary_exit: self.domain_voluntary_exit,
boot_nodes: chain_spec.boot_nodes.clone(), boot_nodes: chain_spec.boot_nodes.clone(),
genesis_fork: chain_spec.genesis_fork.clone(), genesis_fork: chain_spec.genesis_fork.clone(),
eth1_follow_distance: self.eth1_follow_distance,
..*chain_spec ..*chain_spec
}) })
} }

View File

@ -17,6 +17,12 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
.parse::<u64>() .parse::<u64>()
.map_err(|e| format!("Failed to parse min_genesis_time: {}", e))?; .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::<u64>()
.map_err(|e| format!("Failed to parse min-genesis-active-validator-count: {}", e))?;
let confirmations = matches let confirmations = matches
.value_of("confirmations") .value_of("confirmations")
.ok_or_else(|| "Confirmations not specified")? .ok_or_else(|| "Confirmations not specified")?
@ -90,6 +96,7 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
let mut spec = lighthouse_testnet_spec(env.core_context().eth2_config.spec.clone()); let mut spec = lighthouse_testnet_spec(env.core_context().eth2_config.spec.clone());
spec.min_genesis_time = min_genesis_time; spec.min_genesis_time = min_genesis_time;
spec.min_genesis_active_validator_count = min_genesis_active_validator_count;
let testnet_config: Eth2TestnetConfig<T> = Eth2TestnetConfig { let testnet_config: Eth2TestnetConfig<T> = Eth2TestnetConfig {
deposit_contract_address: format!("{}", deposit_contract.address()), 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.ejection_balance = 1_600_000_000;
spec.effective_balance_increment = 100_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 // 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 // become a subset of `new_eth1_data` which may result in an Exception in the spec
// implementation. // implementation.
// //
// This value determines the delay between the eth1 block that triggers genesis and the first // This value determines the delay between the eth1 block that triggers genesis and the first
// slot of that new chain. // 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 spec
} }

View File

@ -4,23 +4,19 @@ extern crate log;
mod deploy_deposit_contract; mod deploy_deposit_contract;
mod eth1_genesis; mod eth1_genesis;
mod parse_hex; mod parse_hex;
mod pycli;
mod refund_deposit_contract; mod refund_deposit_contract;
mod transition_blocks; mod transition_blocks;
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, ArgMatches, SubCommand};
use environment::EnvironmentBuilder; use environment::EnvironmentBuilder;
use log::Level; use log::Level;
use parse_hex::run_parse_hex; use parse_hex::run_parse_hex;
use pycli::run_pycli;
use std::fs::File; use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use transition_blocks::run_transition_blocks; use transition_blocks::run_transition_blocks;
use types::{test_utils::TestingBeaconStateBuilder, EthSpec, MainnetEthSpec, MinimalEthSpec}; use types::{test_utils::TestingBeaconStateBuilder, EthSpec, MainnetEthSpec, MinimalEthSpec};
type LocalEthSpec = MinimalEthSpec;
fn main() { fn main() {
simple_logger::init_with_level(Level::Info).expect("logger should initialize"); simple_logger::init_with_level(Level::Info).expect("logger should initialize");
@ -55,7 +51,7 @@ fn main() {
.takes_value(true) .takes_value(true)
.required(true) .required(true)
.possible_values(&["minimal", "mainnet"]) .possible_values(&["minimal", "mainnet"])
.default_value("minimal") .default_value("mainnet")
.help("Eth2 genesis time (seconds since UNIX epoch)."), .help("Eth2 genesis time (seconds since UNIX epoch)."),
) )
.arg( .arg(
@ -135,6 +131,15 @@ fn main() {
.default_value("0") .default_value("0")
.help("The MIN_GENESIS_TIME constant."), .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(
Arg::with_name("eth1-endpoint") Arg::with_name("eth1-endpoint")
.short("e") .short("e")
@ -222,22 +227,27 @@ fn main() {
.help("The URL to the eth1 JSON-RPC http API."), .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(); .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<T: EthSpec>(env_builder: EnvironmentBuilder<T>, matches: &ArgMatches) {
let env = env_builder
.multi_threaded_tokio_runtime() .multi_threaded_tokio_runtime()
.expect("should start tokio runtime") .expect("should start tokio runtime")
.async_logger("trace") .async_logger("trace")
@ -283,22 +293,19 @@ fn main() {
}; };
info!("Genesis state YAML file created. Exiting successfully."); info!("Genesis state YAML file created. Exiting successfully.");
} }
("transition-blocks", Some(matches)) => run_transition_blocks(matches) ("transition-blocks", Some(matches)) => run_transition_blocks::<T>(matches)
.unwrap_or_else(|e| error!("Failed to transition blocks: {}", e)), .unwrap_or_else(|e| error!("Failed to transition blocks: {}", e)),
("pretty-hex", Some(matches)) => { ("pretty-hex", Some(matches)) => run_parse_hex::<T>(matches)
run_parse_hex(matches).unwrap_or_else(|e| error!("Failed to pretty print hex: {}", e)) .unwrap_or_else(|e| error!("Failed to pretty print hex: {}", e)),
}
("pycli", Some(matches)) => run_pycli::<LocalEthSpec>(matches)
.unwrap_or_else(|e| error!("Failed to run pycli: {}", e)),
("deploy-deposit-contract", Some(matches)) => { ("deploy-deposit-contract", Some(matches)) => {
deploy_deposit_contract::run::<LocalEthSpec>(env, matches) deploy_deposit_contract::run::<T>(env, matches)
.unwrap_or_else(|e| error!("Failed to run deploy-deposit-contract command: {}", e)) .unwrap_or_else(|e| error!("Failed to run deploy-deposit-contract command: {}", e))
} }
("refund-deposit-contract", Some(matches)) => { ("refund-deposit-contract", Some(matches)) => {
refund_deposit_contract::run::<LocalEthSpec>(env, matches) refund_deposit_contract::run::<T>(env, matches)
.unwrap_or_else(|e| error!("Failed to run refund-deposit-contract command: {}", e)) .unwrap_or_else(|e| error!("Failed to run refund-deposit-contract command: {}", e))
} }
("eth1-genesis", Some(matches)) => eth1_genesis::run::<LocalEthSpec>(env, matches) ("eth1-genesis", Some(matches)) => eth1_genesis::run::<T>(env, matches)
.unwrap_or_else(|e| error!("Failed to run eth1-genesis command: {}", e)), .unwrap_or_else(|e| error!("Failed to run eth1-genesis command: {}", e)),
(other, _) => error!("Unknown subcommand {}. See --help.", other), (other, _) => error!("Unknown subcommand {}. See --help.", other),
} }

View File

@ -1,9 +1,9 @@
use clap::ArgMatches; use clap::ArgMatches;
use serde::Serialize; use serde::Serialize;
use ssz::Decode; 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<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
let type_str = matches let type_str = matches
.value_of("type") .value_of("type")
.ok_or_else(|| "No type supplied".to_string())?; .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); info!("Type: {:?}", type_str);
match type_str { match type_str {
"block" => decode_and_print::<BeaconBlock<MinimalEthSpec>>(&hex)?, "block" => decode_and_print::<BeaconBlock<T>>(&hex)?,
"state" => decode_and_print::<BeaconState<MinimalEthSpec>>(&hex)?, "state" => decode_and_print::<BeaconState<T>>(&hex)?,
other => return Err(format!("Unknown type: {}", other)), other => return Err(format!("Unknown type: {}", other)),
}; };

View File

@ -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<T: EthSpec>(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::<T>(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<Self, String> {
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<T: EthSpec>(
&self,
block_path: PathBuf,
pre_state_path: PathBuf,
) -> Result<BeaconState<T>, 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<String, String> {
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))
}

View File

@ -4,9 +4,9 @@ use state_processing::{per_block_processing, per_slot_processing, BlockSignature
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::PathBuf; 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<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
let pre_state_path = matches let pre_state_path = matches
.value_of("pre-state") .value_of("pre-state")
.ok_or_else(|| "No pre-state file supplied".to_string())? .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!("Pre-state path: {:?}", pre_state_path);
info!("Block path: {:?}", block_path); info!("Block path: {:?}", block_path);
let pre_state: BeaconState<MinimalEthSpec> = load_from_ssz(pre_state_path)?; let pre_state: BeaconState<T> = load_from_ssz(pre_state_path)?;
let block: BeaconBlock<MinimalEthSpec> = load_from_ssz(block_path)?; let block: BeaconBlock<T> = load_from_ssz(block_path)?;
let post_state = do_transition(pre_state, block)?; let post_state = do_transition(pre_state, block)?;

View File

@ -33,7 +33,7 @@ fn main() {
.takes_value(true) .takes_value(true)
.possible_values(&["mainnet", "minimal", "interop"]) .possible_values(&["mainnet", "minimal", "interop"])
.global(true) .global(true)
.default_value("minimal"), .default_value("mainnet"),
) )
.arg( .arg(
Arg::with_name("logfile") Arg::with_name("logfile")