Add lcli eth1-genesis command

This commit is contained in:
Paul Hauner
2019-11-28 13:20:58 +11:00
parent 291cf060d2
commit 0dd1d3d442
15 changed files with 249 additions and 108 deletions

View File

@@ -194,38 +194,8 @@ where
"eth1_node" => &config.endpoint
);
let genesis_service = Eth1GenesisService::new(
// Some of the configuration options for `Eth1Config` are
// hard-coded when listening for genesis from the deposit contract.
//
// The idea is that the `Eth1Config` supplied to this function
// (`config`) is intended for block production duties (i.e.,
// listening for deposit events and voting on eth1 data) and that
// we can make listening for genesis more efficient if we modify
// some params.
Eth1Config {
// Truncating the block cache makes searching for genesis more
// complicated.
block_cache_truncation: None,
// Scan large ranges of blocks when awaiting genesis.
blocks_per_log_query: 1_000,
// Only perform a single log request each time the eth1 node is
// polled.
//
// For small testnets this makes finding genesis much faster,
// as it usually happens within 1,000 blocks.
max_log_requests_per_update: Some(1),
// Only perform a single block request each time the eth1 node
// is polled.
//
// For small testnets, this is much faster as they do not have
// a `MIN_GENESIS_SECONDS`, so after `MIN_GENESIS_VALIDATOR_COUNT`
// has been reached only a single block needs to be read.
max_blocks_per_update: Some(1),
..config
},
context.log.clone(),
);
let genesis_service =
Eth1GenesisService::new(config, context.log.clone());
let future = genesis_service
.wait_for_genesis_state(

View File

@@ -39,6 +39,7 @@ impl Default for ClientGenesis {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub data_dir: PathBuf,
pub testnet_dir: PathBuf,
pub db_type: String,
pub db_name: String,
pub freezer_db_path: Option<PathBuf>,
@@ -63,6 +64,7 @@ impl Default for Config {
fn default() -> Self {
Self {
data_dir: PathBuf::from(".lighthouse"),
testnet_dir: PathBuf::from("testnet"),
log_file: PathBuf::from(""),
db_type: "disk".to_string(),
db_name: "chain_db".to_string(),

View File

@@ -59,6 +59,11 @@ impl BlockCache {
self.blocks.first().map(|block| block.timestamp)
}
/// Returns the timestamp of the latest block in the cache (if any).
pub fn latest_block_timestamp(&self) -> Option<u64> {
self.blocks.last().map(|block| block.timestamp)
}
/// Returns the lowest block number stored.
pub fn lowest_block_number(&self) -> Option<u64> {
self.blocks.first().map(|block| block.number)

View File

@@ -173,6 +173,11 @@ impl Service {
self.inner.block_cache.read().earliest_block_timestamp()
}
/// Returns the timestamp of the latest block in the cache (if any).
pub fn latest_block_timestamp(&self) -> Option<u64> {
self.inner.block_cache.read().latest_block_timestamp()
}
/// Returns the lowest block number stored.
pub fn lowest_block_number(&self) -> Option<u64> {
self.inner.block_cache.read().lowest_block_number()

View File

@@ -37,7 +37,31 @@ pub struct Eth1GenesisService {
impl Eth1GenesisService {
/// Creates a new service. Does not attempt to connect to the Eth1 node.
///
/// Modifies the given `config` to make it more suitable to the task of listening to genesis.
pub fn new(config: Eth1Config, log: Logger) -> Self {
let config = Eth1Config {
// Truncating the block cache makes searching for genesis more
// complicated.
block_cache_truncation: None,
// Scan large ranges of blocks when awaiting genesis.
blocks_per_log_query: 1_000,
// Only perform a single log request each time the eth1 node is
// polled.
//
// For small testnets this makes finding genesis much faster,
// as it usually happens within 1,000 blocks.
max_log_requests_per_update: Some(5),
// Only perform a single block request each time the eth1 node
// is polled.
//
// For small testnets, this is much faster as they do not have
// a `MIN_GENESIS_SECONDS`, so after `MIN_GENESIS_VALIDATOR_COUNT`
// has been reached only a single block needs to be read.
max_blocks_per_update: Some(5),
..config
};
Self {
core: Service::new(config, log),
highest_processed_block: Arc::new(Mutex::new(None)),
@@ -81,6 +105,7 @@ impl Eth1GenesisService {
let service_4 = service.clone();
let log = service.core.log.clone();
let min_genesis_active_validator_count = spec.min_genesis_active_validator_count;
let min_genesis_time = spec.min_genesis_time;
Delay::new(Instant::now() + update_interval)
.map_err(|e| format!("Delay between genesis deposit checks failed: {:?}", e))
@@ -161,6 +186,9 @@ impl Eth1GenesisService {
trace!(
service_4.core.log,
"No eth1 genesis block found";
"latest_block_timestamp" => service_4.core.latest_block_timestamp(),
"min_genesis_time" => min_genesis_time,
"min_validator_count" => min_genesis_active_validator_count,
"cached_blocks" => service_4.core.block_cache_len(),
"cached_deposits" => service_4.core.deposit_cache_len(),
"cache_head" => service_4.highest_known_block(),
@@ -218,7 +246,7 @@ impl Eth1GenesisService {
return false;
}
self.is_valid_genesis_eth1_block::<E>(block, &spec)
self.is_valid_genesis_eth1_block::<E>(block, &spec, &self.core.log)
.and_then(|val| {
*highest_processed_block = Some(block.number);
Ok(val)
@@ -313,6 +341,7 @@ impl Eth1GenesisService {
&self,
target_block: &Eth1Block,
spec: &ChainSpec,
log: &Logger,
) -> Result<bool, String> {
if target_block.timestamp < spec.min_genesis_time {
Ok(false)
@@ -357,8 +386,16 @@ impl Eth1GenesisService {
})?;
process_activations(&mut local_state, spec);
let is_valid = is_valid_genesis_state(&local_state, spec);
Ok(is_valid_genesis_state(&local_state, spec))
trace!(
log,
"Eth1 block inspected for genesis";
"active_validators" => local_state.get_active_validator_indices(local_state.current_epoch()).len(),
"validators" => local_state.validators.len()
);
Ok(is_valid)
}
}

View File

@@ -13,9 +13,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("network-dir")
.long("network-dir")
.value_name("DIR")
.help("Data directory for network keys.")
.help("Data directory for network keys. Defaults to network/ inside the beacon node \
dir.")
.takes_value(true)
.global(true)
)
.arg(
Arg::with_name("freezer-dir")
@@ -23,7 +23,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.value_name("DIR")
.help("Data directory for the freezer database.")
.takes_value(true)
.global(true)
)
.arg(
Arg::with_name("testnet-dir")
.long("testnet-dir")
.value_name("DIR")
.help("Path to directory containing eth2_testnet specs. Defaults to \
~/.lighthouse/testnet.")
.takes_value(true)
)
/*
* Network parameters.

View File

@@ -9,12 +9,13 @@ use slog::{crit, info, Logger};
use std::fs;
use std::net::Ipv4Addr;
use std::path::PathBuf;
use types::{Epoch, Fork};
use types::{Epoch, EthSpec, Fork};
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
pub const ETH2_TESTNET_DIR: &str = "testnet";
pub const BEACON_NODE_DIR: &str = "beacon";
pub const NETWORK_DIR: &str = "network";
pub const SECONDS_PER_ETH1_BLOCK: u64 = 15;
@@ -28,7 +29,7 @@ type Config = (ClientConfig, Eth2Config, Logger);
/// 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(
pub fn get_configs<E: EthSpec>(
cli_args: &ArgMatches,
mut eth2_config: Eth2Config,
core_log: Logger,
@@ -41,16 +42,20 @@ pub fn get_configs(
//
// If it's not present, try and find the home directory (`~`) and push the default data
// directory onto it.
//
// Note: the `config.data_dir` defines the _global_ lighthouse datadir, not just the beacon
// node specific datadir.
let data_dir: PathBuf = cli_args
client_config.data_dir = cli_args
.value_of("datadir")
.map(PathBuf::from)
.or_else(|| dirs::home_dir().map(|home| home.join(".lighthouse").join(BEACON_NODE_DIR)))
.ok_or_else(|| "Unable to find a home directory for the datadir".to_string())?;
client_config.data_dir = data_dir;
// Read the `--testnet-dir` flag.
//
// If it's not present, use the default dir.
client_config.testnet_dir = cli_args
.value_of("testnet-dir")
.map(PathBuf::from)
.or_else(|| dirs::home_dir().map(|home| home.join(".lighthouse").join(ETH2_TESTNET_DIR)))
.ok_or_else(|| "Unable to find a home directory for the testnet-dir".to_string())?;
// When present, use an eth1 backend that generates deterministic junk.
//
@@ -79,6 +84,8 @@ pub fn get_configs(
// If a network dir has been specified, override the `datadir` definition.
if let Some(dir) = cli_args.value_of("network-dir") {
client_config.network.network_dir = PathBuf::from(dir);
} else {
client_config.network.network_dir = client_config.data_dir.join(NETWORK_DIR);
};
if let Some(listen_address_str) = cli_args.value_of("listen-address") {
@@ -213,7 +220,7 @@ pub fn get_configs(
// Whilst there is no large testnet or mainnet force the user to specify how they want
// to start a new chain (e.g., from a genesis YAML file, another node, etc).
if !client_config.data_dir.exists() {
init_new_client(&mut client_config, &mut eth2_config)?
init_new_client::<E>(&mut client_config, &mut eth2_config)?
} else {
// If the `testnet` command was not provided, attempt to load an existing datadir and
// continue with an existing chain.
@@ -288,21 +295,20 @@ fn load_from_datadir(client_config: &mut ClientConfig, eth2_config: &mut Eth2Con
}
/// Create a new client with the default configuration.
fn init_new_client(client_config: &mut ClientConfig, eth2_config: &mut Eth2Config) -> Result<()> {
fn init_new_client<E: EthSpec>(
client_config: &mut ClientConfig,
eth2_config: &mut Eth2Config,
) -> Result<()> {
let 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.genesis_fork = Fork {
previous_version: [0; 4],
current_version: [0, 0, 0, 42],
epoch: Epoch::new(0),
};
let eth2_testnet_dir = Eth2TestnetDir::load(client_config.data_dir.join(ETH2_TESTNET_DIR))
.map_err(|e| format!("Unable to open testnet dir: {}", e))?;
let testnet_dir = client_config.testnet_dir.clone();
let eth2_testnet_dir: Eth2TestnetDir<E> = Eth2TestnetDir::load(testnet_dir.clone())
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))?;
client_config.eth1.deposit_contract_address =
format!("{:?}", eth2_testnet_dir.deposit_contract_address()?);

View File

@@ -60,7 +60,7 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
// TODO: the eth2 config in the env is being modified.
//
// See https://github.com/sigp/lighthouse/issues/602
get_configs(&matches, context.eth2_config.clone(), log)
get_configs::<E>(&matches, context.eth2_config.clone(), log)
.into_future()
.and_then(move |(client_config, eth2_config, _log)| {
context.eth2_config = eth2_config;