Tidy Eth2Config generation at runtime (#912)
* Move the codes that loads Eth2Config from config to environment * Move the codes that setups Eth2Config for testnet * Move the codes that creates a new ChainSpec * Remove unused `mut` * Reduce local variable number * Remove unused outputs of config::get_configs() * Change the method name from plural to singular * DRY the const `ETH2_CONFIG_FILENAME` * Add comments * Remove unnecessary blank line * cargo fmt * Add tests for EnvironmentBuilder::setup_eth2_config() * Remove the comment that have been fixed * Reduce local variable * Remove redundant local variable * Remove prysm-specific codes Now the spec is in the eth2-testnets repo
This commit is contained in:
parent
26bdc2927b
commit
93bcee147d
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -1045,9 +1045,12 @@ dependencies = [
|
|||||||
name = "environment"
|
name = "environment"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"beacon_node 0.1.0",
|
||||||
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"eth2_config 0.1.0",
|
"eth2_config 0.1.0",
|
||||||
|
"eth2_testnet_config 0.1.0",
|
||||||
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"logging 0.1.0",
|
"logging 0.1.0",
|
||||||
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -308,14 +308,5 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
.required(true)
|
.required(true)
|
||||||
.help("A file from which to read the state"))
|
.help("A file from which to read the state"))
|
||||||
)
|
)
|
||||||
/*
|
|
||||||
* `prysm`
|
|
||||||
*
|
|
||||||
* Connect to the Prysmatic Labs testnet.
|
|
||||||
*/
|
|
||||||
.subcommand(SubCommand::with_name("prysm")
|
|
||||||
.about("Connect to the Prysmatic Labs testnet on Goerli. Not guaranteed to be \
|
|
||||||
up-to-date or functioning.")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use client::{config::DEFAULT_DATADIR, ClientConfig, ClientGenesis, Eth2Config};
|
use client::{config::DEFAULT_DATADIR, ClientConfig, ClientGenesis, Eth2Config};
|
||||||
|
use environment::ETH2_CONFIG_FILENAME;
|
||||||
use eth2_config::{read_from_file, write_to_file};
|
use eth2_config::{read_from_file, write_to_file};
|
||||||
use eth2_libp2p::{Enr, Multiaddr};
|
use eth2_libp2p::{Enr, Multiaddr};
|
||||||
use eth2_testnet_config::Eth2TestnetConfig;
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
@ -14,14 +15,12 @@ use std::path::PathBuf;
|
|||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
|
|
||||||
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
|
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
|
||||||
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
|
|
||||||
pub const BEACON_NODE_DIR: &str = "beacon";
|
pub const BEACON_NODE_DIR: &str = "beacon";
|
||||||
pub const NETWORK_DIR: &str = "network";
|
pub const NETWORK_DIR: &str = "network";
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, String>;
|
type Result<T> = std::result::Result<T, String>;
|
||||||
type Config = (ClientConfig, Eth2Config, Logger);
|
|
||||||
|
|
||||||
/// Gets the fully-initialized global client and eth2 configuration objects.
|
/// Gets the fully-initialized global client.
|
||||||
///
|
///
|
||||||
/// The top-level `clap` arguments should be provided as `cli_args`.
|
/// The top-level `clap` arguments should be provided as `cli_args`.
|
||||||
///
|
///
|
||||||
@ -29,26 +28,17 @@ type Config = (ClientConfig, Eth2Config, Logger);
|
|||||||
/// may be influenced by other external services like the contents of the file system or the
|
/// may be influenced by other external services like the contents of the file system or the
|
||||||
/// response of some remote server.
|
/// response of some remote server.
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn get_configs<E: EthSpec>(
|
pub fn get_config<E: EthSpec>(
|
||||||
cli_args: &ArgMatches,
|
cli_args: &ArgMatches,
|
||||||
mut eth2_config: Eth2Config,
|
eth2_config: Eth2Config,
|
||||||
core_log: Logger,
|
core_log: Logger,
|
||||||
) -> Result<Config> {
|
) -> Result<ClientConfig> {
|
||||||
let log = core_log.clone();
|
let log = core_log.clone();
|
||||||
|
|
||||||
let mut client_config = ClientConfig::default();
|
let mut client_config = ClientConfig::default();
|
||||||
|
|
||||||
client_config.spec_constants = eth2_config.spec_constants.clone();
|
client_config.spec_constants = eth2_config.spec_constants.clone();
|
||||||
|
client_config.data_dir = get_data_dir(cli_args);
|
||||||
// Read the `--datadir` flag.
|
|
||||||
//
|
|
||||||
// If it's not present, try and find the home directory (`~`) and push the default data
|
|
||||||
// directory onto it.
|
|
||||||
client_config.data_dir = cli_args
|
|
||||||
.value_of("datadir")
|
|
||||||
.map(|path| PathBuf::from(path).join(BEACON_NODE_DIR))
|
|
||||||
.or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR)))
|
|
||||||
.unwrap_or_else(|| PathBuf::from("."));
|
|
||||||
|
|
||||||
// Load the client config, if it exists .
|
// Load the client config, if it exists .
|
||||||
let path = client_config.data_dir.join(CLIENT_CONFIG_FILENAME);
|
let path = client_config.data_dir.join(CLIENT_CONFIG_FILENAME);
|
||||||
@ -58,32 +48,7 @@ pub fn get_configs<E: EthSpec>(
|
|||||||
.ok_or_else(|| format!("{:?} file does not exist", path))?;
|
.ok_or_else(|| format!("{:?} file does not exist", path))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the eth2 config, if it exists .
|
client_config.testnet_dir = get_testnet_dir(cli_args);
|
||||||
let path = client_config.data_dir.join(ETH2_CONFIG_FILENAME);
|
|
||||||
if path.exists() {
|
|
||||||
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.
|
|
||||||
if let Some(val) = cli_args.value_of("testnet-dir") {
|
|
||||||
client_config.testnet_dir = Some(PathBuf::from(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Networking
|
* Networking
|
||||||
@ -222,7 +187,7 @@ pub fn get_configs<E: EthSpec>(
|
|||||||
|
|
||||||
match cli_args.subcommand() {
|
match cli_args.subcommand() {
|
||||||
("testnet", Some(sub_cmd_args)) => {
|
("testnet", Some(sub_cmd_args)) => {
|
||||||
process_testnet_subcommand(&mut client_config, &mut eth2_config, sub_cmd_args)?
|
process_testnet_subcommand(&mut client_config, ð2_config, sub_cmd_args)?
|
||||||
}
|
}
|
||||||
// No sub-command assumes a resume operation.
|
// No sub-command assumes a resume operation.
|
||||||
_ => {
|
_ => {
|
||||||
@ -238,7 +203,7 @@ pub fn get_configs<E: EthSpec>(
|
|||||||
"Starting from an empty database";
|
"Starting from an empty database";
|
||||||
"data_dir" => format!("{:?}", client_config.data_dir)
|
"data_dir" => format!("{:?}", client_config.data_dir)
|
||||||
);
|
);
|
||||||
init_new_client::<E>(&mut client_config, &mut eth2_config)?
|
init_new_client::<E>(&mut client_config, ð2_config)?
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
log,
|
log,
|
||||||
@ -317,7 +282,42 @@ pub fn get_configs<E: EthSpec>(
|
|||||||
client_config.network.discovery_address =
|
client_config.network.discovery_address =
|
||||||
Some("127.0.0.1".parse().expect("Valid IP address"))
|
Some("127.0.0.1".parse().expect("Valid IP address"))
|
||||||
}
|
}
|
||||||
Ok((client_config, eth2_config, log))
|
Ok(client_config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the datadir which should be used.
|
||||||
|
pub fn get_data_dir(cli_args: &ArgMatches) -> PathBuf {
|
||||||
|
// Read the `--datadir` flag.
|
||||||
|
//
|
||||||
|
// If it's not present, try and find the home directory (`~`) and push the default data
|
||||||
|
// directory onto it.
|
||||||
|
cli_args
|
||||||
|
.value_of("datadir")
|
||||||
|
.map(|path| PathBuf::from(path).join(BEACON_NODE_DIR))
|
||||||
|
.or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR)))
|
||||||
|
.unwrap_or_else(|| PathBuf::from("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the testnet dir which should be used.
|
||||||
|
pub fn get_testnet_dir(cli_args: &ArgMatches) -> Option<PathBuf> {
|
||||||
|
// Read the `--testnet-dir` flag.
|
||||||
|
if let Some(val) = cli_args.value_of("testnet-dir") {
|
||||||
|
Some(PathBuf::from(val))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_eth2_testnet_config<E: EthSpec>(
|
||||||
|
testnet_dir: &Option<PathBuf>,
|
||||||
|
) -> Result<Eth2TestnetConfig<E>> {
|
||||||
|
Ok(if let Some(testnet_dir) = testnet_dir {
|
||||||
|
Eth2TestnetConfig::load(testnet_dir.clone())
|
||||||
|
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))?
|
||||||
|
} else {
|
||||||
|
Eth2TestnetConfig::hard_coded()
|
||||||
|
.map_err(|e| format!("Unable to load hard-coded testnet dir: {}", e))?
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load from an existing database.
|
/// Load from an existing database.
|
||||||
@ -352,37 +352,17 @@ fn load_from_datadir(client_config: &mut ClientConfig) -> Result<()> {
|
|||||||
/// Create a new client with the default configuration.
|
/// Create a new client with the default configuration.
|
||||||
fn init_new_client<E: EthSpec>(
|
fn init_new_client<E: EthSpec>(
|
||||||
client_config: &mut ClientConfig,
|
client_config: &mut ClientConfig,
|
||||||
eth2_config: &mut Eth2Config,
|
eth2_config: &Eth2Config,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let eth2_testnet_config: Eth2TestnetConfig<E> =
|
let eth2_testnet_config: Eth2TestnetConfig<E> =
|
||||||
if let Some(testnet_dir) = &client_config.testnet_dir {
|
get_eth2_testnet_config(&client_config.testnet_dir)?;
|
||||||
Eth2TestnetConfig::load(testnet_dir.clone())
|
|
||||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))?
|
|
||||||
} else {
|
|
||||||
Eth2TestnetConfig::hard_coded()
|
|
||||||
.map_err(|e| format!("Unable to load hard-coded testnet dir: {}", e))?
|
|
||||||
};
|
|
||||||
|
|
||||||
eth2_config.spec = eth2_testnet_config
|
|
||||||
.yaml_config
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| "The testnet directory must contain a spec config".to_string())?
|
|
||||||
.apply_to_chain_spec::<E>(ð2_config.spec)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
format!(
|
|
||||||
"The loaded config is not compatible with the {} spec",
|
|
||||||
ð2_config.spec_constants
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let spec = &mut eth2_config.spec;
|
|
||||||
|
|
||||||
client_config.eth1.deposit_contract_address =
|
client_config.eth1.deposit_contract_address =
|
||||||
format!("{:?}", eth2_testnet_config.deposit_contract_address()?);
|
format!("{:?}", eth2_testnet_config.deposit_contract_address()?);
|
||||||
client_config.eth1.deposit_contract_deploy_block =
|
client_config.eth1.deposit_contract_deploy_block =
|
||||||
eth2_testnet_config.deposit_contract_deploy_block;
|
eth2_testnet_config.deposit_contract_deploy_block;
|
||||||
|
|
||||||
client_config.eth1.follow_distance = spec.eth1_follow_distance / 2;
|
client_config.eth1.follow_distance = eth2_config.spec.eth1_follow_distance / 2;
|
||||||
client_config.eth1.lowest_cached_block_number = client_config
|
client_config.eth1.lowest_cached_block_number = client_config
|
||||||
.eth1
|
.eth1
|
||||||
.deposit_contract_deploy_block
|
.deposit_contract_deploy_block
|
||||||
@ -445,7 +425,7 @@ pub fn create_new_datadir(client_config: &ClientConfig, eth2_config: &Eth2Config
|
|||||||
/// Process the `testnet` CLI subcommand arguments, updating the `builder`.
|
/// Process the `testnet` CLI subcommand arguments, updating the `builder`.
|
||||||
fn process_testnet_subcommand(
|
fn process_testnet_subcommand(
|
||||||
client_config: &mut ClientConfig,
|
client_config: &mut ClientConfig,
|
||||||
eth2_config: &mut Eth2Config,
|
eth2_config: &Eth2Config,
|
||||||
cli_args: &ArgMatches,
|
cli_args: &ArgMatches,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Specifies that a random datadir should be used.
|
// Specifies that a random datadir should be used.
|
||||||
@ -476,15 +456,6 @@ fn process_testnet_subcommand(
|
|||||||
client_config.network.propagation_percentage = Some(percentage);
|
client_config.network.propagation_percentage = Some(percentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify the `SECONDS_PER_SLOT` "constant".
|
|
||||||
if let Some(slot_time) = cli_args.value_of("slot-time") {
|
|
||||||
let slot_time = slot_time
|
|
||||||
.parse::<u64>()
|
|
||||||
.map_err(|e| format!("Unable to parse slot-time: {:?}", e))?;
|
|
||||||
|
|
||||||
eth2_config.spec.milliseconds_per_slot = slot_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start matching on the second subcommand (e.g., `testnet bootstrap ...`).
|
// Start matching on the second subcommand (e.g., `testnet bootstrap ...`).
|
||||||
match cli_args.subcommand() {
|
match cli_args.subcommand() {
|
||||||
("recent", Some(cli_args)) => {
|
("recent", Some(cli_args)) => {
|
||||||
@ -545,24 +516,6 @@ fn process_testnet_subcommand(
|
|||||||
|
|
||||||
client_config.genesis = start_method;
|
client_config.genesis = start_method;
|
||||||
}
|
}
|
||||||
("prysm", Some(_)) => {
|
|
||||||
let mut spec = &mut eth2_config.spec;
|
|
||||||
|
|
||||||
spec.min_deposit_amount = 100;
|
|
||||||
spec.max_effective_balance = 3_200_000_000;
|
|
||||||
spec.ejection_balance = 1_600_000_000;
|
|
||||||
spec.effective_balance_increment = 100_000_000;
|
|
||||||
spec.min_genesis_time = 0;
|
|
||||||
spec.genesis_fork_version = [0, 0, 0, 2];
|
|
||||||
|
|
||||||
client_config.eth1.deposit_contract_address =
|
|
||||||
"0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string();
|
|
||||||
client_config.eth1.deposit_contract_deploy_block = 1_487_270;
|
|
||||||
client_config.eth1.follow_distance = 16;
|
|
||||||
client_config.dummy_eth1_backend = false;
|
|
||||||
|
|
||||||
client_config.genesis = ClientGenesis::DepositContract;
|
|
||||||
}
|
|
||||||
(cmd, Some(_)) => {
|
(cmd, Some(_)) => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Invalid valid method specified: {}. See 'testnet --help'.",
|
"Invalid valid method specified: {}. See 'testnet --help'.",
|
||||||
|
@ -7,6 +7,7 @@ mod config;
|
|||||||
pub use beacon_chain;
|
pub use beacon_chain;
|
||||||
pub use cli::cli_app;
|
pub use cli::cli_app;
|
||||||
pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis};
|
pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis};
|
||||||
|
pub use config::{get_data_dir, get_eth2_testnet_config, get_testnet_dir};
|
||||||
pub use eth2_config::Eth2Config;
|
pub use eth2_config::Eth2Config;
|
||||||
|
|
||||||
use beacon_chain::{
|
use beacon_chain::{
|
||||||
@ -14,7 +15,7 @@ use beacon_chain::{
|
|||||||
slot_clock::SystemTimeSlotClock,
|
slot_clock::SystemTimeSlotClock,
|
||||||
};
|
};
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use config::get_configs;
|
use config::get_config;
|
||||||
use environment::RuntimeContext;
|
use environment::RuntimeContext;
|
||||||
use futures::{Future, IntoFuture};
|
use futures::{Future, IntoFuture};
|
||||||
use slog::{info, warn};
|
use slog::{info, warn};
|
||||||
@ -51,20 +52,12 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
|
|||||||
/// given `matches` and potentially configuration files on the local filesystem or other
|
/// given `matches` and potentially configuration files on the local filesystem or other
|
||||||
/// configurations hosted remotely.
|
/// configurations hosted remotely.
|
||||||
pub fn new_from_cli<'a, 'b>(
|
pub fn new_from_cli<'a, 'b>(
|
||||||
mut context: RuntimeContext<E>,
|
context: RuntimeContext<E>,
|
||||||
matches: &ArgMatches<'b>,
|
matches: &ArgMatches<'b>,
|
||||||
) -> impl Future<Item = Self, Error = String> + 'a {
|
) -> impl Future<Item = Self, Error = String> + 'a {
|
||||||
let log = context.log.clone();
|
get_config::<E>(&matches, context.eth2_config.clone(), context.log.clone())
|
||||||
|
|
||||||
// TODO: the eth2 config in the env is being modified.
|
|
||||||
//
|
|
||||||
// See https://github.com/sigp/lighthouse/issues/602
|
|
||||||
get_configs::<E>(&matches, context.eth2_config.clone(), log)
|
|
||||||
.into_future()
|
.into_future()
|
||||||
.and_then(move |(client_config, eth2_config, _log)| {
|
.and_then(move |client_config| Self::new(context, client_config))
|
||||||
context.eth2_config = eth2_config;
|
|
||||||
Self::new(context, client_config)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts a new beacon node `Client` in the given `environment`.
|
/// Starts a new beacon node `Client` in the given `environment`.
|
||||||
|
@ -5,11 +5,13 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = "2.33.0"
|
||||||
tokio = "0.1.22"
|
tokio = "0.1.22"
|
||||||
slog = { version = "^2.2.3" , features = ["max_level_trace"] }
|
slog = { version = "^2.2.3" , features = ["max_level_trace"] }
|
||||||
sloggers = "0.3.4"
|
sloggers = "0.3.4"
|
||||||
types = { "path" = "../../eth2/types" }
|
types = { "path" = "../../eth2/types" }
|
||||||
eth2_config = { "path" = "../../eth2/utils/eth2_config" }
|
eth2_config = { "path" = "../../eth2/utils/eth2_config" }
|
||||||
|
eth2_testnet_config = { path = "../../eth2/utils/eth2_testnet_config" }
|
||||||
env_logger = "0.6.1"
|
env_logger = "0.6.1"
|
||||||
logging = { path = "../../eth2/utils/logging" }
|
logging = { path = "../../eth2/utils/logging" }
|
||||||
slog-term = "^2.4.0"
|
slog-term = "^2.4.0"
|
||||||
@ -18,3 +20,6 @@ ctrlc = { version = "3.1.1", features = ["termination"] }
|
|||||||
futures = "0.1.25"
|
futures = "0.1.25"
|
||||||
parking_lot = "0.7"
|
parking_lot = "0.7"
|
||||||
slog-json = "2.3.0"
|
slog-json = "2.3.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
beacon_node = { path = "../../beacon_node" }
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
//! `Context` which can be handed to any service that wishes to start async tasks or perform
|
//! `Context` which can be handed to any service that wishes to start async tasks or perform
|
||||||
//! logging.
|
//! logging.
|
||||||
|
|
||||||
use eth2_config::Eth2Config;
|
use clap::ArgMatches;
|
||||||
|
use eth2_config::{read_from_file, Eth2Config};
|
||||||
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
use futures::{sync::oneshot, Future};
|
use futures::{sync::oneshot, Future};
|
||||||
use slog::{info, o, Drain, Level, Logger};
|
use slog::{info, o, Drain, Level, Logger};
|
||||||
use sloggers::{null::NullLoggerBuilder, Build};
|
use sloggers::{null::NullLoggerBuilder, Build};
|
||||||
@ -19,6 +21,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||||||
use tokio::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor};
|
use tokio::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor};
|
||||||
use types::{EthSpec, InteropEthSpec, MainnetEthSpec, MinimalEthSpec};
|
use types::{EthSpec, InteropEthSpec, MainnetEthSpec, MinimalEthSpec};
|
||||||
|
|
||||||
|
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
|
||||||
|
|
||||||
/// Builds an `Environment`.
|
/// Builds an `Environment`.
|
||||||
pub struct EnvironmentBuilder<E: EthSpec> {
|
pub struct EnvironmentBuilder<E: EthSpec> {
|
||||||
runtime: Option<Runtime>,
|
runtime: Option<Runtime>,
|
||||||
@ -134,6 +138,73 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Setups eth2 config using the CLI arguments.
|
||||||
|
pub fn setup_eth2_config(
|
||||||
|
mut self,
|
||||||
|
datadir: PathBuf,
|
||||||
|
eth2_testnet_config: Eth2TestnetConfig<E>,
|
||||||
|
cli_args: &ArgMatches,
|
||||||
|
) -> Result<Self, String> {
|
||||||
|
self.load_eth2_config(&datadir)?;
|
||||||
|
|
||||||
|
match cli_args.subcommand() {
|
||||||
|
("testnet", Some(sub_cli_args)) => {
|
||||||
|
// Modify the `SECONDS_PER_SLOT` "constant".
|
||||||
|
if let Some(slot_time) = sub_cli_args.value_of("slot-time") {
|
||||||
|
let slot_time = slot_time
|
||||||
|
.parse::<u64>()
|
||||||
|
.map_err(|e| format!("Unable to parse slot-time: {:?}", e))?;
|
||||||
|
|
||||||
|
self.eth2_config.spec.milliseconds_per_slot = slot_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if !datadir.exists() {
|
||||||
|
// Create a new chain spec from the default configuration.
|
||||||
|
self.eth2_config.spec = eth2_testnet_config
|
||||||
|
.yaml_config
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
"The testnet directory must contain a spec config".to_string()
|
||||||
|
})?
|
||||||
|
.apply_to_chain_spec::<E>(&self.eth2_config.spec)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"The loaded config is not compatible with the {} spec",
|
||||||
|
&self.eth2_config.spec_constants
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads the eth2 config if the file exists.
|
||||||
|
fn load_eth2_config(&mut self, datadir: &PathBuf) -> Result<(), String> {
|
||||||
|
let filename = datadir.join(ETH2_CONFIG_FILENAME);
|
||||||
|
if filename.exists() {
|
||||||
|
let loaded_eth2_config: Eth2Config = read_from_file(filename.clone())
|
||||||
|
.map_err(|e| format!("Unable to parse {:?} file: {:?}", filename, e))?
|
||||||
|
.ok_or_else(|| format!("{:?} file does not exist", filename))?;
|
||||||
|
|
||||||
|
// The loaded spec must be using the same spec constants (e.g., minimal, mainnet) as the
|
||||||
|
// client expects.
|
||||||
|
if loaded_eth2_config.spec_constants == self.eth2_config.spec_constants {
|
||||||
|
self.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, &self.eth2_config.spec_constants
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes the builder, returning an `Environment`.
|
/// Consumes the builder, returning an `Environment`.
|
||||||
pub fn build(self) -> Result<Environment<E>, String> {
|
pub fn build(self) -> Result<Environment<E>, String> {
|
||||||
Ok(Environment {
|
Ok(Environment {
|
||||||
|
87
lighthouse/environment/tests/environment_builder.rs
Normal file
87
lighthouse/environment/tests/environment_builder.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
|
use environment::EnvironmentBuilder;
|
||||||
|
use eth2_testnet_config::Eth2TestnetConfig;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use types::{Epoch, MainnetEthSpec, YamlConfig};
|
||||||
|
|
||||||
|
fn builder() -> EnvironmentBuilder<MainnetEthSpec> {
|
||||||
|
EnvironmentBuilder::mainnet()
|
||||||
|
.single_thread_tokio_runtime()
|
||||||
|
.expect("should set runtime")
|
||||||
|
.null_logger()
|
||||||
|
.expect("should set logger")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dummy_data_dir() -> PathBuf {
|
||||||
|
PathBuf::from("./tests/datadir_that_does_not_exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eth2_testnet_config() -> Eth2TestnetConfig<MainnetEthSpec> {
|
||||||
|
Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params")
|
||||||
|
}
|
||||||
|
|
||||||
|
mod setup_eth2_config {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn returns_err_if_the_loaded_config_doesnt_match() {
|
||||||
|
// `Minimal` spec
|
||||||
|
let path_to_minimal_spec = PathBuf::from("./tests/minimal_spec");
|
||||||
|
|
||||||
|
// `Mainnet` spec
|
||||||
|
let builder = builder();
|
||||||
|
|
||||||
|
let result = builder.setup_eth2_config(
|
||||||
|
path_to_minimal_spec,
|
||||||
|
eth2_testnet_config(),
|
||||||
|
&ArgMatches::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert_eq!(
|
||||||
|
result.err().unwrap(),
|
||||||
|
"Eth2 config loaded from disk does not match client spec version. Got minimal expected mainnet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_slot_time() {
|
||||||
|
// testnet
|
||||||
|
let cli_args =
|
||||||
|
beacon_node::cli_app().get_matches_from(vec!["app", "testnet", "--slot-time", "999"]);
|
||||||
|
|
||||||
|
let environment = builder()
|
||||||
|
.setup_eth2_config(dummy_data_dir(), eth2_testnet_config(), &cli_args)
|
||||||
|
.expect("should setup eth2_config")
|
||||||
|
.build()
|
||||||
|
.expect("should build environment");
|
||||||
|
|
||||||
|
assert_eq!(environment.eth2_config.spec.milliseconds_per_slot, 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn update_spec_with_yaml_config() {
|
||||||
|
let config_yaml = PathBuf::from("./tests/testnet_dir/config.yaml");
|
||||||
|
|
||||||
|
let mut eth2_testnet_config = eth2_testnet_config();
|
||||||
|
eth2_testnet_config.yaml_config =
|
||||||
|
Some(YamlConfig::from_file(config_yaml.as_path()).expect("should load yaml config"));
|
||||||
|
|
||||||
|
let environment = builder()
|
||||||
|
.setup_eth2_config(
|
||||||
|
dummy_data_dir(),
|
||||||
|
eth2_testnet_config,
|
||||||
|
&ArgMatches::default(),
|
||||||
|
)
|
||||||
|
.expect("should setup eth2_config")
|
||||||
|
.build()
|
||||||
|
.expect("should build environment");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
environment.eth2_config.spec.far_future_epoch,
|
||||||
|
Epoch::new(999) // see testnet_dir/config.yaml
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
42
lighthouse/environment/tests/minimal_spec/eth2-spec.toml
Normal file
42
lighthouse/environment/tests/minimal_spec/eth2-spec.toml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
spec_constants = "minimal" # for testing
|
||||||
|
|
||||||
|
[spec]
|
||||||
|
genesis_slot = 0
|
||||||
|
base_rewards_per_epoch = 4
|
||||||
|
deposit_contract_tree_depth = 32
|
||||||
|
max_committees_per_slot = 64
|
||||||
|
target_committee_size = 128
|
||||||
|
min_per_epoch_churn_limit = 4
|
||||||
|
churn_limit_quotient = 65536
|
||||||
|
shuffle_round_count = 90
|
||||||
|
min_genesis_active_validator_count = 16384
|
||||||
|
min_genesis_time = 1578009600
|
||||||
|
min_deposit_amount = 1000000000
|
||||||
|
max_effective_balance = 32000000000
|
||||||
|
ejection_balance = 16000000000
|
||||||
|
effective_balance_increment = 1000000000
|
||||||
|
genesis_fork_version = "0x00000000"
|
||||||
|
bls_withdrawal_prefix_byte = "0x00"
|
||||||
|
min_genesis_delay = 86400
|
||||||
|
milliseconds_per_slot = 12000
|
||||||
|
min_attestation_inclusion_delay = 1
|
||||||
|
min_seed_lookahead = 1
|
||||||
|
max_seed_lookahead = 4
|
||||||
|
min_epochs_to_inactivity_penalty = 4
|
||||||
|
min_validator_withdrawability_delay = 256
|
||||||
|
persistent_committee_period = 2048
|
||||||
|
base_reward_factor = 64
|
||||||
|
whistleblower_reward_quotient = 512
|
||||||
|
proposer_reward_quotient = 8
|
||||||
|
inactivity_penalty_quotient = 33554432
|
||||||
|
min_slashing_penalty_quotient = 32
|
||||||
|
domain_beacon_proposer = 0
|
||||||
|
domain_beacon_attester = 1
|
||||||
|
domain_randao = 2
|
||||||
|
domain_deposit = 3
|
||||||
|
domain_voluntary_exit = 4
|
||||||
|
safe_slots_to_update_justified = 8
|
||||||
|
eth1_follow_distance = 1024
|
||||||
|
seconds_per_eth1_block = 14
|
||||||
|
boot_nodes = []
|
||||||
|
network_id = 1
|
56
lighthouse/environment/tests/testnet_dir/config.yaml
Normal file
56
lighthouse/environment/tests/testnet_dir/config.yaml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
FAR_FUTURE_EPOCH: 999 # for testing
|
||||||
|
BASE_REWARDS_PER_EPOCH: 4
|
||||||
|
DEPOSIT_CONTRACT_TREE_DEPTH: 32
|
||||||
|
MAX_COMMITTEES_PER_SLOT: 64
|
||||||
|
TARGET_COMMITTEE_SIZE: 128
|
||||||
|
MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||||
|
CHURN_LIMIT_QUOTIENT: 65536
|
||||||
|
SHUFFLE_ROUND_COUNT: 90
|
||||||
|
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 4096
|
||||||
|
MIN_GENESIS_TIME: 0
|
||||||
|
MIN_GENESIS_DELAY: 3600
|
||||||
|
MIN_DEPOSIT_AMOUNT: 10000000
|
||||||
|
MAX_EFFECTIVE_BALANCE: 3200000000
|
||||||
|
EJECTION_BALANCE: 1600000000
|
||||||
|
EFFECTIVE_BALANCE_INCREMENT: 100000000
|
||||||
|
GENESIS_SLOT: 0
|
||||||
|
GENESIS_FORK_VERSION: 0x01030307
|
||||||
|
BLS_WITHDRAWAL_PREFIX: 0x00
|
||||||
|
SECONDS_PER_SLOT: 12
|
||||||
|
MIN_ATTESTATION_INCLUSION_DELAY: 1
|
||||||
|
MIN_SEED_LOOKAHEAD: 1
|
||||||
|
MAX_SEED_LOOKAHEAD: 4
|
||||||
|
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
|
||||||
|
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
|
||||||
|
PERSISTENT_COMMITTEE_PERIOD: 2048
|
||||||
|
BASE_REWARD_FACTOR: 64
|
||||||
|
WHISTLEBLOWER_REWARD_QUOTIENT: 512
|
||||||
|
PROPOSER_REWARD_QUOTIENT: 8
|
||||||
|
INACTIVITY_PENALTY_QUOTIENT: 33554432
|
||||||
|
MIN_SLASHING_PENALTY_QUOTIENT: 32
|
||||||
|
SAFE_SLOTS_TO_UPDATE_JUSTIFIED: 8
|
||||||
|
DOMAIN_BEACON_PROPOSER: 0x00000000
|
||||||
|
DOMAIN_BEACON_ATTESTER: 0x01000000
|
||||||
|
DOMAIN_RANDAO: 0x02000000
|
||||||
|
DOMAIN_DEPOSIT: 0x03000000
|
||||||
|
DOMAIN_VOLUNTARY_EXIT: 0x04000000
|
||||||
|
JUSTIFICATION_BITS_LENGTH: 0x04000000
|
||||||
|
MAX_VALIDATORS_PER_COMMITTEE: 2048
|
||||||
|
GENESIS_EPOCH: 0
|
||||||
|
SLOTS_PER_EPOCH: 32
|
||||||
|
SLOTS_PER_ETH1_VOTING_PERIOD: 1024
|
||||||
|
SLOTS_PER_HISTORICAL_ROOT: 8192
|
||||||
|
EPOCHS_PER_HISTORICAL_VECTOR: 65536
|
||||||
|
EPOCHS_PER_SLASHINGS_VECTOR: 8192
|
||||||
|
HISTORICAL_ROOTS_LIMIT: 16777216
|
||||||
|
VALIDATOR_REGISTRY_LIMIT: 1099511627776
|
||||||
|
MAX_PROPOSER_SLASHINGS: 16
|
||||||
|
MAX_ATTESTER_SLASHINGS: 1
|
||||||
|
MAX_ATTESTATIONS: 128
|
||||||
|
MAX_DEPOSITS: 16
|
||||||
|
MAX_VOLUNTARY_EXITS: 16
|
||||||
|
ETH1_FOLLOW_DISTANCE: 16
|
||||||
|
TARGET_AGGREGATORS_PER_COMMITTEE: 0
|
||||||
|
RANDOM_SUBNETS_PER_VALIDATOR: 0
|
||||||
|
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION: 0
|
||||||
|
SECONDS_PER_ETH1_BLOCK: 14
|
@ -1,7 +1,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
||||||
use beacon_node::ProductionBeaconNode;
|
use beacon_node::{get_data_dir, get_eth2_testnet_config, get_testnet_dir, ProductionBeaconNode};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use env_logger::{Builder, Env};
|
use env_logger::{Builder, Env};
|
||||||
use environment::EnvironmentBuilder;
|
use environment::EnvironmentBuilder;
|
||||||
@ -114,6 +114,11 @@ fn run<E: EthSpec>(
|
|||||||
let mut environment = environment_builder
|
let mut environment = environment_builder
|
||||||
.async_logger(debug_level, log_format)?
|
.async_logger(debug_level, log_format)?
|
||||||
.multi_threaded_tokio_runtime()?
|
.multi_threaded_tokio_runtime()?
|
||||||
|
.setup_eth2_config(
|
||||||
|
get_data_dir(matches),
|
||||||
|
get_eth2_testnet_config(&get_testnet_dir(matches))?,
|
||||||
|
matches,
|
||||||
|
)?
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let log = environment.core_context().log;
|
let log = environment.core_context().log;
|
||||||
|
Loading…
Reference in New Issue
Block a user