mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-08 17:26:04 +00:00
Add lcli eth1-genesis command
This commit is contained in:
@@ -179,7 +179,7 @@ fn run_new_validator_subcommand<T: EthSpec>(
|
|||||||
.parse::<PathBuf>()
|
.parse::<PathBuf>()
|
||||||
.map_err(|e| format!("Unable to parse testnet-dir: {}", e))?;
|
.map_err(|e| format!("Unable to parse testnet-dir: {}", e))?;
|
||||||
|
|
||||||
let eth2_testnet_dir = Eth2TestnetDir::load(testnet_dir)
|
let eth2_testnet_dir: Eth2TestnetDir<T> = Eth2TestnetDir::load(testnet_dir)
|
||||||
.map_err(|e| format!("Failed to load testnet dir: {}", e))?;
|
.map_err(|e| format!("Failed to load testnet dir: {}", e))?;
|
||||||
|
|
||||||
// Convert from `types::Address` to `web3::types::Address`.
|
// Convert from `types::Address` to `web3::types::Address`.
|
||||||
|
|||||||
@@ -194,38 +194,8 @@ where
|
|||||||
"eth1_node" => &config.endpoint
|
"eth1_node" => &config.endpoint
|
||||||
);
|
);
|
||||||
|
|
||||||
let genesis_service = Eth1GenesisService::new(
|
let genesis_service =
|
||||||
// Some of the configuration options for `Eth1Config` are
|
Eth1GenesisService::new(config, context.log.clone());
|
||||||
// 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 future = genesis_service
|
let future = genesis_service
|
||||||
.wait_for_genesis_state(
|
.wait_for_genesis_state(
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ impl Default for ClientGenesis {
|
|||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub data_dir: PathBuf,
|
pub data_dir: PathBuf,
|
||||||
|
pub testnet_dir: PathBuf,
|
||||||
pub db_type: String,
|
pub db_type: String,
|
||||||
pub db_name: String,
|
pub db_name: String,
|
||||||
pub freezer_db_path: Option<PathBuf>,
|
pub freezer_db_path: Option<PathBuf>,
|
||||||
@@ -63,6 +64,7 @@ impl Default for Config {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data_dir: PathBuf::from(".lighthouse"),
|
data_dir: PathBuf::from(".lighthouse"),
|
||||||
|
testnet_dir: PathBuf::from("testnet"),
|
||||||
log_file: PathBuf::from(""),
|
log_file: PathBuf::from(""),
|
||||||
db_type: "disk".to_string(),
|
db_type: "disk".to_string(),
|
||||||
db_name: "chain_db".to_string(),
|
db_name: "chain_db".to_string(),
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ impl BlockCache {
|
|||||||
self.blocks.first().map(|block| block.timestamp)
|
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.
|
/// Returns the lowest block number stored.
|
||||||
pub fn lowest_block_number(&self) -> Option<u64> {
|
pub fn lowest_block_number(&self) -> Option<u64> {
|
||||||
self.blocks.first().map(|block| block.number)
|
self.blocks.first().map(|block| block.number)
|
||||||
|
|||||||
@@ -173,6 +173,11 @@ impl Service {
|
|||||||
self.inner.block_cache.read().earliest_block_timestamp()
|
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.
|
/// Returns the lowest block number stored.
|
||||||
pub fn lowest_block_number(&self) -> Option<u64> {
|
pub fn lowest_block_number(&self) -> Option<u64> {
|
||||||
self.inner.block_cache.read().lowest_block_number()
|
self.inner.block_cache.read().lowest_block_number()
|
||||||
|
|||||||
@@ -37,7 +37,31 @@ pub struct Eth1GenesisService {
|
|||||||
|
|
||||||
impl Eth1GenesisService {
|
impl Eth1GenesisService {
|
||||||
/// Creates a new service. Does not attempt to connect to the Eth1 node.
|
/// 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 {
|
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 {
|
Self {
|
||||||
core: Service::new(config, log),
|
core: Service::new(config, log),
|
||||||
highest_processed_block: Arc::new(Mutex::new(None)),
|
highest_processed_block: Arc::new(Mutex::new(None)),
|
||||||
@@ -81,6 +105,7 @@ impl Eth1GenesisService {
|
|||||||
let service_4 = service.clone();
|
let service_4 = service.clone();
|
||||||
let log = service.core.log.clone();
|
let log = service.core.log.clone();
|
||||||
let min_genesis_active_validator_count = spec.min_genesis_active_validator_count;
|
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)
|
Delay::new(Instant::now() + update_interval)
|
||||||
.map_err(|e| format!("Delay between genesis deposit checks failed: {:?}", e))
|
.map_err(|e| format!("Delay between genesis deposit checks failed: {:?}", e))
|
||||||
@@ -161,6 +186,9 @@ impl Eth1GenesisService {
|
|||||||
trace!(
|
trace!(
|
||||||
service_4.core.log,
|
service_4.core.log,
|
||||||
"No eth1 genesis block found";
|
"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_blocks" => service_4.core.block_cache_len(),
|
||||||
"cached_deposits" => service_4.core.deposit_cache_len(),
|
"cached_deposits" => service_4.core.deposit_cache_len(),
|
||||||
"cache_head" => service_4.highest_known_block(),
|
"cache_head" => service_4.highest_known_block(),
|
||||||
@@ -218,7 +246,7 @@ impl Eth1GenesisService {
|
|||||||
return false;
|
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| {
|
.and_then(|val| {
|
||||||
*highest_processed_block = Some(block.number);
|
*highest_processed_block = Some(block.number);
|
||||||
Ok(val)
|
Ok(val)
|
||||||
@@ -313,6 +341,7 @@ impl Eth1GenesisService {
|
|||||||
&self,
|
&self,
|
||||||
target_block: &Eth1Block,
|
target_block: &Eth1Block,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
|
log: &Logger,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
if target_block.timestamp < spec.min_genesis_time {
|
if target_block.timestamp < spec.min_genesis_time {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
@@ -357,8 +386,16 @@ impl Eth1GenesisService {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
process_activations(&mut local_state, spec);
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
Arg::with_name("network-dir")
|
Arg::with_name("network-dir")
|
||||||
.long("network-dir")
|
.long("network-dir")
|
||||||
.value_name("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)
|
.takes_value(true)
|
||||||
.global(true)
|
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("freezer-dir")
|
Arg::with_name("freezer-dir")
|
||||||
@@ -23,7 +23,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
.value_name("DIR")
|
.value_name("DIR")
|
||||||
.help("Data directory for the freezer database.")
|
.help("Data directory for the freezer database.")
|
||||||
.takes_value(true)
|
.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.
|
* Network parameters.
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ use slog::{crit, info, Logger};
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use types::{Epoch, Fork};
|
use types::{Epoch, EthSpec, Fork};
|
||||||
|
|
||||||
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 ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
|
||||||
pub const ETH2_TESTNET_DIR: &str = "testnet";
|
pub const ETH2_TESTNET_DIR: &str = "testnet";
|
||||||
pub const BEACON_NODE_DIR: &str = "beacon";
|
pub const BEACON_NODE_DIR: &str = "beacon";
|
||||||
|
pub const NETWORK_DIR: &str = "network";
|
||||||
|
|
||||||
pub const SECONDS_PER_ETH1_BLOCK: u64 = 15;
|
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
|
/// 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
|
/// 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.
|
||||||
pub fn get_configs(
|
pub fn get_configs<E: EthSpec>(
|
||||||
cli_args: &ArgMatches,
|
cli_args: &ArgMatches,
|
||||||
mut eth2_config: Eth2Config,
|
mut eth2_config: Eth2Config,
|
||||||
core_log: Logger,
|
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
|
// If it's not present, try and find the home directory (`~`) and push the default data
|
||||||
// directory onto it.
|
// directory onto it.
|
||||||
//
|
client_config.data_dir = cli_args
|
||||||
// Note: the `config.data_dir` defines the _global_ lighthouse datadir, not just the beacon
|
|
||||||
// node specific datadir.
|
|
||||||
let data_dir: PathBuf = cli_args
|
|
||||||
.value_of("datadir")
|
.value_of("datadir")
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
.or_else(|| dirs::home_dir().map(|home| home.join(".lighthouse").join(BEACON_NODE_DIR)))
|
.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())?;
|
.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.
|
// 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 a network dir has been specified, override the `datadir` definition.
|
||||||
if let Some(dir) = cli_args.value_of("network-dir") {
|
if let Some(dir) = cli_args.value_of("network-dir") {
|
||||||
client_config.network.network_dir = PathBuf::from(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") {
|
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
|
// 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).
|
// to start a new chain (e.g., from a genesis YAML file, another node, etc).
|
||||||
if !client_config.data_dir.exists() {
|
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 {
|
} else {
|
||||||
// If the `testnet` command was not provided, attempt to load an existing datadir and
|
// If the `testnet` command was not provided, attempt to load an existing datadir and
|
||||||
// continue with an existing chain.
|
// 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.
|
/// 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;
|
let spec = &mut eth2_config.spec;
|
||||||
|
|
||||||
spec.min_deposit_amount = 100;
|
spec.min_deposit_amount = 100;
|
||||||
spec.max_effective_balance = 3_200_000_000;
|
spec.max_effective_balance = 3_200_000_000;
|
||||||
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.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))
|
let testnet_dir = client_config.testnet_dir.clone();
|
||||||
.map_err(|e| format!("Unable to open testnet dir: {}", e))?;
|
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 =
|
client_config.eth1.deposit_contract_address =
|
||||||
format!("{:?}", eth2_testnet_dir.deposit_contract_address()?);
|
format!("{:?}", eth2_testnet_dir.deposit_contract_address()?);
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
|
|||||||
// TODO: the eth2 config in the env is being modified.
|
// TODO: the eth2 config in the env is being modified.
|
||||||
//
|
//
|
||||||
// See https://github.com/sigp/lighthouse/issues/602
|
// 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()
|
.into_future()
|
||||||
.and_then(move |(client_config, eth2_config, _log)| {
|
.and_then(move |(client_config, eth2_config, _log)| {
|
||||||
context.eth2_config = eth2_config;
|
context.eth2_config = eth2_config;
|
||||||
|
|||||||
@@ -10,38 +10,40 @@
|
|||||||
use eth2_libp2p::Enr;
|
use eth2_libp2p::Enr;
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, File};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use types::Address;
|
use types::{Address, BeaconState, EthSpec};
|
||||||
|
|
||||||
pub const ADDRESS_FILE: &str = "deposit_contract.txt";
|
pub const ADDRESS_FILE: &str = "deposit_contract.txt";
|
||||||
pub const DEPLOY_BLOCK_FILE: &str = "deploy_block.txt";
|
pub const DEPLOY_BLOCK_FILE: &str = "deploy_block.txt";
|
||||||
pub const MIN_GENESIS_TIME_FILE: &str = "min_genesis_time.txt";
|
pub const MIN_GENESIS_TIME_FILE: &str = "min_genesis_time.txt";
|
||||||
pub const BOOT_NODES_FILE: &str = "boot_nodes.json";
|
pub const BOOT_NODES_FILE: &str = "boot_enr.json";
|
||||||
|
pub const GENESIS_STATE_FILE: &str = "genesis.ssz";
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct Eth2TestnetDir {
|
pub struct Eth2TestnetDir<E: EthSpec> {
|
||||||
deposit_contract_address: String,
|
pub deposit_contract_address: String,
|
||||||
pub deposit_contract_deploy_block: u64,
|
pub deposit_contract_deploy_block: u64,
|
||||||
pub min_genesis_time: u64,
|
pub min_genesis_time: u64,
|
||||||
pub boot_nodes: Vec<Enr>,
|
pub boot_enr: Option<Vec<Enr>>,
|
||||||
|
pub genesis_state: Option<BeaconState<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eth2TestnetDir {
|
impl<E: EthSpec> Eth2TestnetDir<E> {
|
||||||
pub fn new(
|
// Write the files to the directory, only if the directory doesn't already exist.
|
||||||
base_dir: PathBuf,
|
pub fn write_to_file(&self, base_dir: PathBuf) -> Result<(), String> {
|
||||||
deposit_contract_address: String,
|
|
||||||
deposit_contract_deploy_block: u64,
|
|
||||||
min_genesis_time: u64,
|
|
||||||
boot_nodes: Vec<Enr>,
|
|
||||||
) -> Result<Self, String> {
|
|
||||||
if base_dir.exists() {
|
if base_dir.exists() {
|
||||||
return Err("Testnet directory already exists".to_string());
|
return Err("Testnet directory already exists".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.force_write_to_file(base_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the files to the directory, even if the directory already exists.
|
||||||
|
pub fn force_write_to_file(&self, base_dir: PathBuf) -> Result<(), String> {
|
||||||
create_dir_all(&base_dir)
|
create_dir_all(&base_dir)
|
||||||
.map_err(|e| format!("Unable to create testnet directory: {:?}", e))?;
|
.map_err(|e| format!("Unable to create testnet directory: {:?}", e))?;
|
||||||
|
|
||||||
macro_rules! write_to_file {
|
macro_rules! write_to_file {
|
||||||
($file: ident, $variable: ident) => {
|
($file: ident, $variable: expr) => {
|
||||||
File::create(base_dir.join($file))
|
File::create(base_dir.join($file))
|
||||||
.map_err(|e| format!("Unable to create {}: {:?}", $file, e))
|
.map_err(|e| format!("Unable to create {}: {:?}", $file, e))
|
||||||
.and_then(|file| {
|
.and_then(|file| {
|
||||||
@@ -51,17 +53,19 @@ impl Eth2TestnetDir {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
write_to_file!(ADDRESS_FILE, deposit_contract_address);
|
write_to_file!(ADDRESS_FILE, self.deposit_contract_address);
|
||||||
write_to_file!(DEPLOY_BLOCK_FILE, deposit_contract_deploy_block);
|
write_to_file!(DEPLOY_BLOCK_FILE, self.deposit_contract_deploy_block);
|
||||||
write_to_file!(MIN_GENESIS_TIME_FILE, min_genesis_time);
|
write_to_file!(MIN_GENESIS_TIME_FILE, self.min_genesis_time);
|
||||||
write_to_file!(BOOT_NODES_FILE, boot_nodes);
|
|
||||||
|
|
||||||
Ok(Self {
|
if let Some(boot_enr) = &self.boot_enr {
|
||||||
deposit_contract_address,
|
write_to_file!(BOOT_NODES_FILE, boot_enr);
|
||||||
deposit_contract_deploy_block,
|
}
|
||||||
min_genesis_time,
|
|
||||||
boot_nodes,
|
if let Some(genesis_state) = &self.genesis_state {
|
||||||
})
|
write_to_file!(GENESIS_STATE_FILE, genesis_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(base_dir: PathBuf) -> Result<Self, String> {
|
pub fn load(base_dir: PathBuf) -> Result<Self, String> {
|
||||||
@@ -76,16 +80,28 @@ impl Eth2TestnetDir {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! optional_load_from_file {
|
||||||
|
($file: ident) => {
|
||||||
|
if base_dir.join($file).exists() {
|
||||||
|
Some(load_from_file!($file))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let deposit_contract_address = load_from_file!(ADDRESS_FILE);
|
let deposit_contract_address = load_from_file!(ADDRESS_FILE);
|
||||||
let deposit_contract_deploy_block = load_from_file!(DEPLOY_BLOCK_FILE);
|
let deposit_contract_deploy_block = load_from_file!(DEPLOY_BLOCK_FILE);
|
||||||
let min_genesis_time = load_from_file!(MIN_GENESIS_TIME_FILE);
|
let min_genesis_time = load_from_file!(MIN_GENESIS_TIME_FILE);
|
||||||
let boot_nodes = load_from_file!(BOOT_NODES_FILE);
|
let boot_enr = optional_load_from_file!(BOOT_NODES_FILE);
|
||||||
|
let genesis_state = optional_load_from_file!(GENESIS_STATE_FILE);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
deposit_contract_address,
|
deposit_contract_address,
|
||||||
deposit_contract_deploy_block,
|
deposit_contract_deploy_block,
|
||||||
min_genesis_time,
|
min_genesis_time,
|
||||||
boot_nodes,
|
boot_enr,
|
||||||
|
genesis_state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +120,9 @@ impl Eth2TestnetDir {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
|
use types::MinimalEthSpec;
|
||||||
|
|
||||||
|
type E = MinimalEthSpec;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip() {
|
fn round_trip() {
|
||||||
@@ -113,15 +132,19 @@ mod tests {
|
|||||||
let deposit_contract_deploy_block = 42;
|
let deposit_contract_deploy_block = 42;
|
||||||
let min_genesis_time = 1337;
|
let min_genesis_time = 1337;
|
||||||
|
|
||||||
let testnet = Eth2TestnetDir::new(
|
let testnet: Eth2TestnetDir<E> = Eth2TestnetDir {
|
||||||
base_dir.clone(),
|
deposit_contract_address: deposit_contract_address.clone(),
|
||||||
deposit_contract_address.clone(),
|
deposit_contract_deploy_block: deposit_contract_deploy_block,
|
||||||
deposit_contract_deploy_block,
|
|
||||||
min_genesis_time,
|
min_genesis_time,
|
||||||
// TODO: add some Enr for testing.
|
// TODO: add some Enr for testing.
|
||||||
vec![],
|
boot_enr: None,
|
||||||
)
|
// TODO: add a genesis state for testing.
|
||||||
.expect("should create struct");
|
genesis_state: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
testnet
|
||||||
|
.write_to_file(base_dir.clone())
|
||||||
|
.expect("should write to file");
|
||||||
|
|
||||||
let decoded = Eth2TestnetDir::load(base_dir).expect("should load struct");
|
let decoded = Eth2TestnetDir::load(base_dir).expect("should load struct");
|
||||||
|
|
||||||
|
|||||||
@@ -24,3 +24,4 @@ environment = { path = "../lighthouse/environment" }
|
|||||||
web3 = "0.8.0"
|
web3 = "0.8.0"
|
||||||
eth2_testnet = { path = "../eth2/utils/eth2_testnet" }
|
eth2_testnet = { path = "../eth2/utils/eth2_testnet" }
|
||||||
dirs = "2.0"
|
dirs = "2.0"
|
||||||
|
genesis = { path = "../beacon_node/genesis" }
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ use std::path::PathBuf;
|
|||||||
use types::EthSpec;
|
use types::EthSpec;
|
||||||
use web3::{transports::Http, Web3};
|
use web3::{transports::Http, Web3};
|
||||||
|
|
||||||
pub const DEFAULT_DATA_DIR: &str = ".lighthouse/testnet";
|
|
||||||
|
|
||||||
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
||||||
let min_genesis_time = matches
|
let min_genesis_time = matches
|
||||||
.value_of("min-genesis-time")
|
.value_of("min-genesis-time")
|
||||||
@@ -29,10 +27,7 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
|
|||||||
.and_then(|output| output.parse::<PathBuf>().map_err(|_| ()))
|
.and_then(|output| output.parse::<PathBuf>().map_err(|_| ()))
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
dirs::home_dir()
|
dirs::home_dir()
|
||||||
.map(|mut home| {
|
.map(|home| home.join(".lighthouse").join("testnet"))
|
||||||
home.push(DEFAULT_DATA_DIR);
|
|
||||||
home
|
|
||||||
})
|
|
||||||
.expect("should locate home directory")
|
.expect("should locate home directory")
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,12 +86,15 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
|
|||||||
|
|
||||||
info!("Writing config to {:?}", output_dir);
|
info!("Writing config to {:?}", output_dir);
|
||||||
|
|
||||||
Eth2TestnetDir::new(
|
let testnet_dir: Eth2TestnetDir<T> = Eth2TestnetDir {
|
||||||
output_dir,
|
deposit_contract_address: format!("{}", deposit_contract.address()),
|
||||||
format!("{}", deposit_contract.address()),
|
deposit_contract_deploy_block: deploy_block.as_u64(),
|
||||||
deploy_block.as_u64(),
|
|
||||||
min_genesis_time,
|
min_genesis_time,
|
||||||
)?;
|
boot_enr: None,
|
||||||
|
genesis_state: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
testnet_dir.write_to_file(output_dir)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
67
lcli/src/eth1_genesis.rs
Normal file
67
lcli/src/eth1_genesis.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use clap::ArgMatches;
|
||||||
|
use environment::Environment;
|
||||||
|
use eth2_testnet::Eth2TestnetDir;
|
||||||
|
use futures::Future;
|
||||||
|
use genesis::{Eth1Config, Eth1GenesisService};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::Duration;
|
||||||
|
use types::{EthSpec, Fork};
|
||||||
|
|
||||||
|
/// Interval between polling the eth1 node for genesis information.
|
||||||
|
pub const ETH1_GENESIS_UPDATE_INTERVAL_MILLIS: u64 = 7_000;
|
||||||
|
pub const SECONDS_PER_ETH1_BLOCK: u64 = 15;
|
||||||
|
|
||||||
|
pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<(), String> {
|
||||||
|
let endpoint = matches
|
||||||
|
.value_of("eth1-endpoint")
|
||||||
|
.ok_or_else(|| "eth1-endpoint not specified")?;
|
||||||
|
|
||||||
|
let testnet_dir = matches
|
||||||
|
.value_of("testnet-dir")
|
||||||
|
.ok_or_else(|| ())
|
||||||
|
.and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ()))
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
dirs::home_dir()
|
||||||
|
.map(|home| home.join(".lighthouse").join("testnet"))
|
||||||
|
.expect("should locate home directory")
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut eth2_testnet_dir: Eth2TestnetDir<T> = Eth2TestnetDir::load(testnet_dir.clone())?;
|
||||||
|
|
||||||
|
let mut config = Eth1Config::default();
|
||||||
|
config.endpoint = endpoint.to_string();
|
||||||
|
config.deposit_contract_address = eth2_testnet_dir.deposit_contract_address.clone();
|
||||||
|
config.deposit_contract_deploy_block = eth2_testnet_dir.deposit_contract_deploy_block;
|
||||||
|
config.lowest_cached_block_number = eth2_testnet_dir.deposit_contract_deploy_block;
|
||||||
|
|
||||||
|
let genesis_service = Eth1GenesisService::new(config, env.core_context().log.clone());
|
||||||
|
let mut spec = env.core_context().eth2_config.spec.clone();
|
||||||
|
|
||||||
|
spec.min_genesis_time = eth2_testnet_dir.min_genesis_time;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Note: these are hard-coded hacky values. This should be fixed when we can load a testnet
|
||||||
|
// dir from the `Eth2TestnetDir`.
|
||||||
|
spec.eth1_follow_distance = 16;
|
||||||
|
spec.seconds_per_day = SECONDS_PER_ETH1_BLOCK * spec.eth1_follow_distance * 2;
|
||||||
|
|
||||||
|
let future = genesis_service
|
||||||
|
.wait_for_genesis_state(
|
||||||
|
Duration::from_millis(ETH1_GENESIS_UPDATE_INTERVAL_MILLIS),
|
||||||
|
spec,
|
||||||
|
)
|
||||||
|
.map(move |genesis_state| {
|
||||||
|
eth2_testnet_dir.genesis_state = Some(genesis_state);
|
||||||
|
eth2_testnet_dir.force_write_to_file(testnet_dir)
|
||||||
|
});
|
||||||
|
|
||||||
|
env.runtime()
|
||||||
|
.block_on(future)
|
||||||
|
.map_err(|e| format!("Failed to find genesis: {}", e))??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
mod deploy_deposit_contract;
|
mod deploy_deposit_contract;
|
||||||
|
mod eth1_genesis;
|
||||||
mod parse_hex;
|
mod parse_hex;
|
||||||
mod pycli;
|
mod pycli;
|
||||||
mod refund_deposit_contract;
|
mod refund_deposit_contract;
|
||||||
@@ -198,6 +199,29 @@ fn main() {
|
|||||||
.help("The eth1 accounts[] index which will send the transaction"),
|
.help("The eth1 accounts[] index which will send the transaction"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("eth1-genesis")
|
||||||
|
.about(
|
||||||
|
"Listens to the eth1 chain and finds the genesis beacon state",
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("testnet-dir")
|
||||||
|
.short("d")
|
||||||
|
.long("testnet-dir")
|
||||||
|
.value_name("PATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The testnet dir. Defaults to ~/.lighthouse/testnet"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("eth1-endpoint")
|
||||||
|
.short("e")
|
||||||
|
.long("eth1-endpoint")
|
||||||
|
.value_name("HTTP_SERVER")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value("http://localhost:8545")
|
||||||
|
.help("The URL to the eth1 JSON-RPC http API."),
|
||||||
|
)
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("pycli")
|
SubCommand::with_name("pycli")
|
||||||
.about("TODO")
|
.about("TODO")
|
||||||
@@ -216,7 +240,7 @@ fn main() {
|
|||||||
let env = EnvironmentBuilder::minimal()
|
let env = EnvironmentBuilder::minimal()
|
||||||
.multi_threaded_tokio_runtime()
|
.multi_threaded_tokio_runtime()
|
||||||
.expect("should start tokio runtime")
|
.expect("should start tokio runtime")
|
||||||
.null_logger()
|
.async_logger("trace")
|
||||||
.expect("should start null logger")
|
.expect("should start null logger")
|
||||||
.build()
|
.build()
|
||||||
.expect("should build env");
|
.expect("should build env");
|
||||||
@@ -257,7 +281,6 @@ fn main() {
|
|||||||
"mainnet" => genesis_yaml::<MainnetEthSpec>(num_validators, genesis_time, file),
|
"mainnet" => genesis_yaml::<MainnetEthSpec>(num_validators, genesis_time, file),
|
||||||
_ => unreachable!("guarded by slog possible_values"),
|
_ => unreachable!("guarded by slog possible_values"),
|
||||||
};
|
};
|
||||||
|
|
||||||
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(matches)
|
||||||
@@ -275,6 +298,8 @@ fn main() {
|
|||||||
refund_deposit_contract::run::<LocalEthSpec>(env, matches)
|
refund_deposit_contract::run::<LocalEthSpec>(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)
|
||||||
|
.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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ use web3::{
|
|||||||
Web3,
|
Web3,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DEFAULT_DATA_DIR: &str = ".lighthouse/testnet";
|
|
||||||
|
|
||||||
/// `keccak("steal()")[0..4]`
|
/// `keccak("steal()")[0..4]`
|
||||||
pub const DEPOSIT_ROOT_FN_SIGNATURE: &[u8] = &[0xcf, 0x7a, 0x89, 0x65];
|
pub const DEPOSIT_ROOT_FN_SIGNATURE: &[u8] = &[0xcf, 0x7a, 0x89, 0x65];
|
||||||
|
|
||||||
@@ -35,14 +33,11 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
|
|||||||
.and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ()))
|
.and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ()))
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
dirs::home_dir()
|
dirs::home_dir()
|
||||||
.map(|mut home| {
|
.map(|home| home.join(".lighthouse").join("testnet"))
|
||||||
home.push(DEFAULT_DATA_DIR);
|
|
||||||
home
|
|
||||||
})
|
|
||||||
.expect("should locate home directory")
|
.expect("should locate home directory")
|
||||||
});
|
});
|
||||||
|
|
||||||
let eth2_testnet_dir = Eth2TestnetDir::load(testnet_dir)?;
|
let eth2_testnet_dir: Eth2TestnetDir<T> = Eth2TestnetDir::load(testnet_dir)?;
|
||||||
|
|
||||||
let (_event_loop, transport) = Http::new(&endpoint).map_err(|e| {
|
let (_event_loop, transport) = Http::new(&endpoint).map_err(|e| {
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
Reference in New Issue
Block a user