Misc changes for merge testnets (#2667)

* Thread eth1_block_hash into interop genesis state

* Add merge-fork-epoch flag

* Build LH with minimal spec by default

* Add verbose logs to execution_layer

* Add --http-allow-sync-stalled flag

* Update lcli new-testnet to create genesis state

* Fix http test

* Fix compile errors in tests
This commit is contained in:
Paul Hauner 2021-10-03 06:57:23 +11:00
parent a1033a9247
commit b162b067de
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
16 changed files with 236 additions and 60 deletions

View File

@ -23,9 +23,9 @@ FORKS=phase0 altair
# Binaries will most likely be found in `./target/release` # Binaries will most likely be found in `./target/release`
install: install:
ifeq ($(PORTABLE), true) ifeq ($(PORTABLE), true)
cargo install --path lighthouse --force --locked --features portable cargo install --path lighthouse --force --locked --features portable,spec-minimal
else else
cargo install --path lighthouse --force --locked cargo install --path lighthouse --force --locked --features spec-minimal
endif endif
# Builds the lcli binary in release (optimized). # Builds the lcli binary in release (optimized).

View File

@ -919,7 +919,9 @@ fn descriptive_db_error(item: &str, error: &StoreError) -> String {
mod test { mod test {
use super::*; use super::*;
use eth2_hashing::hash; use eth2_hashing::hash;
use genesis::{generate_deterministic_keypairs, interop_genesis_state}; use genesis::{
generate_deterministic_keypairs, interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH,
};
use sloggers::{null::NullLoggerBuilder, Build}; use sloggers::{null::NullLoggerBuilder, Build};
use ssz::Encode; use ssz::Encode;
use std::time::Duration; use std::time::Duration;
@ -951,6 +953,7 @@ mod test {
let genesis_state = interop_genesis_state( let genesis_state = interop_genesis_state(
&generate_deterministic_keypairs(validator_count), &generate_deterministic_keypairs(validator_count),
genesis_time, genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
&spec, &spec,
) )
.expect("should create interop genesis state"); .expect("should create interop genesis state");
@ -1016,8 +1019,13 @@ mod test {
let keypairs = generate_deterministic_keypairs(validator_count); let keypairs = generate_deterministic_keypairs(validator_count);
let state = interop_genesis_state::<TestEthSpec>(&keypairs, genesis_time, spec) let state = interop_genesis_state::<TestEthSpec>(
.expect("should build state"); &keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
spec,
)
.expect("should build state");
assert_eq!( assert_eq!(
state.eth1_data().block_hash, state.eth1_data().block_hash,

View File

@ -18,7 +18,7 @@ use execution_layer::{
ExecutionLayer, ExecutionLayer,
}; };
use futures::channel::mpsc::Receiver; use futures::channel::mpsc::Receiver;
pub use genesis::interop_genesis_state; pub use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
use int_to_bytes::int_to_bytes32; use int_to_bytes::int_to_bytes32;
use logging::test_logger; use logging::test_logger;
use merkle_proof::MerkleTree; use merkle_proof::MerkleTree;
@ -181,6 +181,7 @@ impl<E: EthSpec> Builder<EphemeralHarnessType<E>> {
let genesis_state = interop_genesis_state::<E>( let genesis_state = interop_genesis_state::<E>(
&validator_keypairs, &validator_keypairs,
HARNESS_GENESIS_TIME, HARNESS_GENESIS_TIME,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
builder.get_spec(), builder.get_spec(),
) )
.expect("should generate interop state"); .expect("should generate interop state");
@ -226,6 +227,7 @@ impl<E: EthSpec> Builder<DiskHarnessType<E>> {
let genesis_state = interop_genesis_state::<E>( let genesis_state = interop_genesis_state::<E>(
&validator_keypairs, &validator_keypairs,
HARNESS_GENESIS_TIME, HARNESS_GENESIS_TIME,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
builder.get_spec(), builder.get_spec(),
) )
.expect("should generate interop state"); .expect("should generate interop state");

View File

@ -17,7 +17,7 @@ use eth2::{
BeaconNodeHttpClient, Error as ApiError, Timeouts, BeaconNodeHttpClient, Error as ApiError, Timeouts,
}; };
use execution_layer::ExecutionLayer; use execution_layer::ExecutionLayer;
use genesis::{interop_genesis_state, Eth1GenesisService}; use genesis::{interop_genesis_state, Eth1GenesisService, DEFAULT_ETH1_BLOCK_HASH};
use lighthouse_network::NetworkGlobals; use lighthouse_network::NetworkGlobals;
use monitoring_api::{MonitoringHttpClient, ProcessType}; use monitoring_api::{MonitoringHttpClient, ProcessType};
use network::{NetworkConfig, NetworkMessage, NetworkService}; use network::{NetworkConfig, NetworkMessage, NetworkService};
@ -31,7 +31,8 @@ use std::time::Duration;
use timer::spawn_timer; use timer::spawn_timer;
use tokio::sync::{mpsc::UnboundedSender, oneshot}; use tokio::sync::{mpsc::UnboundedSender, oneshot};
use types::{ use types::{
test_utils::generate_deterministic_keypairs, BeaconState, ChainSpec, EthSpec, SignedBeaconBlock, test_utils::generate_deterministic_keypairs, BeaconState, ChainSpec, EthSpec, Hash256,
SignedBeaconBlock,
}; };
/// Interval between polling the eth1 node for genesis information. /// Interval between polling the eth1 node for genesis information.
@ -229,7 +230,12 @@ where
genesis_time, genesis_time,
} => { } => {
let keypairs = generate_deterministic_keypairs(validator_count); let keypairs = generate_deterministic_keypairs(validator_count);
let genesis_state = interop_genesis_state(&keypairs, genesis_time, &spec)?; let genesis_state = interop_genesis_state(
&keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
&spec,
)?;
builder.genesis_state(genesis_state).map(|v| (v, None))? builder.genesis_state(genesis_state).map(|v| (v, None))?
} }
ClientGenesis::SszBytes { ClientGenesis::SszBytes {

View File

@ -8,7 +8,7 @@ use engine_api::{Error as ApiError, *};
use engines::{Engine, EngineError, Engines}; use engines::{Engine, EngineError, Engines};
use lru::LruCache; use lru::LruCache;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use slog::{crit, Logger}; use slog::{crit, info, Logger};
use std::future::Future; use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
@ -177,6 +177,14 @@ impl ExecutionLayer {
random: Hash256, random: Hash256,
) -> Result<PayloadId, Error> { ) -> Result<PayloadId, Error> {
let fee_recipient = self.fee_recipient()?; let fee_recipient = self.fee_recipient()?;
info!(
self.log(),
"Issuing engine_preparePayload";
"fee_recipient" => ?fee_recipient,
"random" => ?random,
"timestamp" => timestamp,
"parent_hash" => ?parent_hash,
);
self.engines() self.engines()
.first_success(|engine| { .first_success(|engine| {
// TODO(merge): make a cache for these IDs, so we don't always have to perform this // TODO(merge): make a cache for these IDs, so we don't always have to perform this
@ -205,6 +213,14 @@ impl ExecutionLayer {
random: Hash256, random: Hash256,
) -> Result<ExecutionPayload<T>, Error> { ) -> Result<ExecutionPayload<T>, Error> {
let fee_recipient = self.fee_recipient()?; let fee_recipient = self.fee_recipient()?;
info!(
self.log(),
"Issuing engine_getPayload";
"fee_recipient" => ?fee_recipient,
"random" => ?random,
"timestamp" => timestamp,
"parent_hash" => ?parent_hash,
);
self.engines() self.engines()
.first_success(|engine| async move { .first_success(|engine| async move {
// TODO(merge): make a cache for these IDs, so we don't always have to perform this // TODO(merge): make a cache for these IDs, so we don't always have to perform this
@ -236,6 +252,14 @@ impl ExecutionLayer {
&self, &self,
execution_payload: &ExecutionPayload<T>, execution_payload: &ExecutionPayload<T>,
) -> Result<(ExecutePayloadResponse, ExecutePayloadHandle), Error> { ) -> Result<(ExecutePayloadResponse, ExecutePayloadHandle), Error> {
info!(
self.log(),
"Issuing engine_executePayload";
"parent_hash" => ?execution_payload.parent_hash,
"block_hash" => ?execution_payload.block_hash,
"block_number" => execution_payload.block_number,
);
let broadcast_results = self let broadcast_results = self
.engines() .engines()
.broadcast(|engine| engine.api.execute_payload(execution_payload.clone())) .broadcast(|engine| engine.api.execute_payload(execution_payload.clone()))
@ -296,6 +320,12 @@ impl ExecutionLayer {
block_hash: Hash256, block_hash: Hash256,
status: ConsensusStatus, status: ConsensusStatus,
) -> Result<(), Error> { ) -> Result<(), Error> {
info!(
self.log(),
"Issuing engine_consensusValidated";
"status" => ?status,
"block_hash" => ?block_hash,
);
let broadcast_results = self let broadcast_results = self
.engines() .engines()
.broadcast(|engine| engine.api.consensus_validated(block_hash, status)) .broadcast(|engine| engine.api.consensus_validated(block_hash, status))
@ -328,6 +358,12 @@ impl ExecutionLayer {
head_block_hash: Hash256, head_block_hash: Hash256,
finalized_block_hash: Hash256, finalized_block_hash: Hash256,
) -> Result<(), Error> { ) -> Result<(), Error> {
info!(
self.log(),
"Issuing engine_forkchoiceUpdated";
"finalized_block_hash" => ?finalized_block_hash,
"head_block_hash" => ?head_block_hash,
);
let broadcast_results = self let broadcast_results = self
.engines() .engines()
.broadcast(|engine| { .broadcast(|engine| {
@ -357,7 +393,8 @@ impl ExecutionLayer {
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/merge/validator.md /// https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/merge/validator.md
pub async fn get_terminal_pow_block_hash(&self) -> Result<Option<Hash256>, Error> { pub async fn get_terminal_pow_block_hash(&self) -> Result<Option<Hash256>, Error> {
self.engines() let hash_opt = self
.engines()
.first_success(|engine| async move { .first_success(|engine| async move {
if self.terminal_block_hash() != Hash256::zero() { if self.terminal_block_hash() != Hash256::zero() {
// Note: the specification is written such that if there are multiple blocks in // Note: the specification is written such that if there are multiple blocks in
@ -376,7 +413,19 @@ impl ExecutionLayer {
} }
}) })
.await .await
.map_err(Error::EngineErrors) .map_err(Error::EngineErrors)?;
if let Some(hash) = &hash_opt {
info!(
self.log(),
"Found terminal block hash";
"terminal_block_hash_override" => ?self.terminal_block_hash(),
"terminal_total_difficulty" => ?self.terminal_total_difficulty(),
"block_hash" => ?hash,
);
}
Ok(hash_opt)
} }
/// This function should remain internal. External users should use /// This function should remain internal. External users should use

View File

@ -5,6 +5,8 @@ use ssz::Encode;
use state_processing::initialize_beacon_state_from_eth1; use state_processing::initialize_beacon_state_from_eth1;
use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature}; use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature};
pub const DEFAULT_ETH1_BLOCK_HASH: &[u8] = &[0x42; 32];
/// Builds a genesis state as defined by the Eth2 interop procedure (see below). /// Builds a genesis state as defined by the Eth2 interop procedure (see below).
/// ///
/// Reference: /// Reference:
@ -12,9 +14,10 @@ use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, Publ
pub fn interop_genesis_state<T: EthSpec>( pub fn interop_genesis_state<T: EthSpec>(
keypairs: &[Keypair], keypairs: &[Keypair],
genesis_time: u64, genesis_time: u64,
eth1_block_hash: Hash256,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<BeaconState<T>, String> { ) -> Result<BeaconState<T>, String> {
let eth1_block_hash = Hash256::from_slice(&[0x42; 32]); let eth1_block_hash = eth1_block_hash;
let eth1_timestamp = 2_u64.pow(40); let eth1_timestamp = 2_u64.pow(40);
let amount = spec.max_effective_balance; let amount = spec.max_effective_balance;
@ -73,8 +76,13 @@ mod test {
let keypairs = generate_deterministic_keypairs(validator_count); let keypairs = generate_deterministic_keypairs(validator_count);
let state = interop_genesis_state::<TestEthSpec>(&keypairs, genesis_time, spec) let state = interop_genesis_state::<TestEthSpec>(
.expect("should build state"); &keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
spec,
)
.expect("should build state");
assert_eq!( assert_eq!(
state.eth1_data().block_hash, state.eth1_data().block_hash,

View File

@ -4,5 +4,5 @@ mod interop;
pub use eth1::Config as Eth1Config; pub use eth1::Config as Eth1Config;
pub use eth1_genesis_service::{Eth1GenesisService, Statistics}; pub use eth1_genesis_service::{Eth1GenesisService, Statistics};
pub use interop::interop_genesis_state; pub use interop::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
pub use types::test_utils::generate_deterministic_keypairs; pub use types::test_utils::generate_deterministic_keypairs;

View File

@ -97,6 +97,7 @@ pub struct Config {
pub allow_origin: Option<String>, pub allow_origin: Option<String>,
pub serve_legacy_spec: bool, pub serve_legacy_spec: bool,
pub tls_config: Option<TlsConfig>, pub tls_config: Option<TlsConfig>,
pub allow_sync_stalled: bool,
} }
impl Default for Config { impl Default for Config {
@ -108,6 +109,7 @@ impl Default for Config {
allow_origin: None, allow_origin: None,
serve_legacy_spec: true, serve_legacy_spec: true,
tls_config: None, tls_config: None,
allow_sync_stalled: false,
} }
} }
} }
@ -237,6 +239,7 @@ pub fn serve<T: BeaconChainTypes>(
shutdown: impl Future<Output = ()> + Send + Sync + 'static, shutdown: impl Future<Output = ()> + Send + Sync + 'static,
) -> Result<HttpServer, Error> { ) -> Result<HttpServer, Error> {
let config = ctx.config.clone(); let config = ctx.config.clone();
let allow_sync_stalled = config.allow_sync_stalled;
let log = ctx.log.clone(); let log = ctx.log.clone();
// Configure CORS. // Configure CORS.
@ -338,44 +341,49 @@ pub fn serve<T: BeaconChainTypes>(
}); });
// Create a `warp` filter that rejects request whilst the node is syncing. // Create a `warp` filter that rejects request whilst the node is syncing.
let not_while_syncing_filter = warp::any() let not_while_syncing_filter =
.and(network_globals.clone()) warp::any()
.and(chain_filter.clone()) .and(network_globals.clone())
.and_then( .and(chain_filter.clone())
|network_globals: Arc<NetworkGlobals<T::EthSpec>>, chain: Arc<BeaconChain<T>>| async move { .and_then(
match *network_globals.sync_state.read() { move |network_globals: Arc<NetworkGlobals<T::EthSpec>>,
SyncState::SyncingFinalized { .. } => { chain: Arc<BeaconChain<T>>| async move {
let head_slot = chain.best_slot().map_err(warp_utils::reject::beacon_chain_error)?; match *network_globals.sync_state.read() {
SyncState::SyncingFinalized { .. } => {
let head_slot = chain
.best_slot()
.map_err(warp_utils::reject::beacon_chain_error)?;
let current_slot = chain let current_slot =
.slot_clock chain.slot_clock.now_or_genesis().ok_or_else(|| {
.now_or_genesis() warp_utils::reject::custom_server_error(
.ok_or_else(|| { "unable to read slot clock".to_string(),
warp_utils::reject::custom_server_error( )
"unable to read slot clock".to_string(), })?;
)
})?;
let tolerance = SYNC_TOLERANCE_EPOCHS * T::EthSpec::slots_per_epoch(); let tolerance = SYNC_TOLERANCE_EPOCHS * T::EthSpec::slots_per_epoch();
if head_slot + tolerance >= current_slot { if head_slot + tolerance >= current_slot {
Ok(()) Ok(())
} else { } else {
Err(warp_utils::reject::not_synced(format!( Err(warp_utils::reject::not_synced(format!(
"head slot is {}, current slot is {}", "head slot is {}, current slot is {}",
head_slot, current_slot head_slot, current_slot
))) )))
}
} }
SyncState::SyncingHead { .. }
| SyncState::SyncTransition
| SyncState::BackFillSyncing { .. } => Ok(()),
SyncState::Synced => Ok(()),
SyncState::Stalled if allow_sync_stalled => Ok(()),
SyncState::Stalled => Err(warp_utils::reject::not_synced(
"sync is stalled".to_string(),
)),
} }
SyncState::SyncingHead { .. } | SyncState::SyncTransition | SyncState::BackFillSyncing { .. } => Ok(()), },
SyncState::Synced => Ok(()), )
SyncState::Stalled => Err(warp_utils::reject::not_synced( .untuple_one();
"sync is stalled".to_string(),
)),
}
},
)
.untuple_one();
// Create a `warp` filter that provides access to the logger. // Create a `warp` filter that provides access to the logger.
let inner_ctx = ctx.clone(); let inner_ctx = ctx.clone();

View File

@ -133,6 +133,7 @@ pub async fn create_api_server<T: BeaconChainTypes>(
allow_origin: None, allow_origin: None,
serve_legacy_spec: true, serve_legacy_spec: true,
tls_config: None, tls_config: None,
allow_sync_stalled: false,
}, },
chain: Some(chain.clone()), chain: Some(chain.clone()),
network_tx: Some(network_tx), network_tx: Some(network_tx),

View File

@ -5,7 +5,7 @@ use beacon_chain::{
BeaconChain, BeaconChain,
}; };
use futures::prelude::*; use futures::prelude::*;
use genesis::{generate_deterministic_keypairs, interop_genesis_state}; use genesis::{generate_deterministic_keypairs, interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lighthouse_network::NetworkConfig; use lighthouse_network::NetworkConfig;
use slog::Logger; use slog::Logger;
@ -16,8 +16,8 @@ use std::time::{Duration, SystemTime};
use store::config::StoreConfig; use store::config::StoreConfig;
use store::{HotColdDB, MemoryStore}; use store::{HotColdDB, MemoryStore};
use types::{ use types::{
CommitteeIndex, Epoch, EthSpec, MainnetEthSpec, Slot, SubnetId, SyncCommitteeSubscription, CommitteeIndex, Epoch, EthSpec, Hash256, MainnetEthSpec, Slot, SubnetId,
SyncSubnetId, ValidatorSubscription, SyncCommitteeSubscription, SyncSubnetId, ValidatorSubscription,
}; };
const SLOT_DURATION_MILLIS: u64 = 400; const SLOT_DURATION_MILLIS: u64 = 400;
@ -52,8 +52,13 @@ impl TestBeaconChain {
.custom_spec(spec.clone()) .custom_spec(spec.clone())
.store(Arc::new(store)) .store(Arc::new(store))
.genesis_state( .genesis_state(
interop_genesis_state::<MainnetEthSpec>(&keypairs, 0, &spec) interop_genesis_state::<MainnetEthSpec>(
.expect("should generate interop state"), &keypairs,
0,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
&spec,
)
.expect("should generate interop state"),
) )
.expect("should build state using recent genesis") .expect("should build state using recent genesis")
.dummy_eth1_backend() .dummy_eth1_backend()

View File

@ -240,6 +240,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
over TLS. Must not be password-protected.") over TLS. Must not be password-protected.")
.takes_value(true) .takes_value(true)
) )
.arg(
Arg::with_name("http-allow-sync-stalled")
.long("http-allow-sync-stalled")
.help("Forces the HTTP to indicate that the node is synced when sync is actually \
stalled. This is useful for very small testnets. TESTING ONLY. DO NOT USE ON \
MAINNET.")
)
/* Prometheus metrics HTTP server related arguments */ /* Prometheus metrics HTTP server related arguments */
.arg( .arg(
Arg::with_name("metrics") Arg::with_name("metrics")

View File

@ -131,6 +131,10 @@ pub fn get_config<E: EthSpec>(
}); });
} }
if cli_args.is_present("http-allow-sync-stalled") {
client_config.http_api.allow_sync_stalled = true;
}
/* /*
* Prometheus metrics HTTP server * Prometheus metrics HTTP server
*/ */

View File

@ -3,6 +3,7 @@ use crate::test_utils::*;
use crate::test_utils::{SeedableRng, XorShiftRng}; use crate::test_utils::{SeedableRng, XorShiftRng};
use beacon_chain::test_utils::{ use beacon_chain::test_utils::{
interop_genesis_state, test_spec, BeaconChainHarness, EphemeralHarnessType, interop_genesis_state, test_spec, BeaconChainHarness, EphemeralHarnessType,
DEFAULT_ETH1_BLOCK_HASH,
}; };
use beacon_chain::types::{ use beacon_chain::types::{
test_utils::TestRandom, BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateError, test_utils::TestRandom, BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateError,
@ -557,7 +558,13 @@ fn tree_hash_cache_linear_history_long_skip() {
let spec = &test_spec::<MinimalEthSpec>(); let spec = &test_spec::<MinimalEthSpec>();
// This state has a cache that advances normally each slot. // This state has a cache that advances normally each slot.
let mut state: BeaconState<MinimalEthSpec> = interop_genesis_state(&keypairs, 0, spec).unwrap(); let mut state: BeaconState<MinimalEthSpec> = interop_genesis_state(
&keypairs,
0,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
spec,
)
.unwrap();
state.update_tree_hash_cache().unwrap(); state.update_tree_hash_cache().unwrap();

View File

@ -1,11 +1,11 @@
use clap::ArgMatches; use clap::ArgMatches;
use clap_utils::parse_ssz_optional; use clap_utils::parse_ssz_optional;
use eth2_network_config::Eth2NetworkConfig; use eth2_network_config::Eth2NetworkConfig;
use genesis::interop_genesis_state; use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
use ssz::Encode; use ssz::Encode;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use types::{test_utils::generate_deterministic_keypairs, EthSpec}; use types::{test_utils::generate_deterministic_keypairs, EthSpec, Hash256};
pub fn run<T: EthSpec>(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), String> { pub fn run<T: EthSpec>(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), String> {
let validator_count = matches let validator_count = matches
@ -34,7 +34,12 @@ pub fn run<T: EthSpec>(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(),
} }
let keypairs = generate_deterministic_keypairs(validator_count); let keypairs = generate_deterministic_keypairs(validator_count);
let genesis_state = interop_genesis_state::<T>(&keypairs, genesis_time, &spec)?; let genesis_state = interop_genesis_state::<T>(
&keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
&spec,
)?;
eth2_network_config.genesis_state_bytes = Some(genesis_state.as_ssz_bytes()); eth2_network_config.genesis_state_bytes = Some(genesis_state.as_ssz_bytes());
eth2_network_config.force_write_to_file(testnet_dir)?; eth2_network_config.force_write_to_file(testnet_dir)?;

View File

@ -284,6 +284,14 @@ fn main() {
.takes_value(false) .takes_value(false)
.help("Overwrites any previous testnet configurations"), .help("Overwrites any previous testnet configurations"),
) )
.arg(
Arg::with_name("interop-genesis-state")
.long("interop-genesis-state")
.takes_value(false)
.help(
"If present, a interop-style genesis.ssz file will be generated.",
),
)
.arg( .arg(
Arg::with_name("min-genesis-time") Arg::with_name("min-genesis-time")
.long("min-genesis-time") .long("min-genesis-time")
@ -402,6 +410,36 @@ fn main() {
"The epoch at which to enable the Altair hard fork", "The epoch at which to enable the Altair hard fork",
), ),
) )
.arg(
Arg::with_name("merge-fork-epoch")
.long("merge-fork-epoch")
.value_name("EPOCH")
.takes_value(true)
.help(
"The epoch at which to enable the Merge hard fork",
),
)
.arg(
Arg::with_name("eth1-block-hash")
.long("eth1-block-hash")
.value_name("BLOCK_HASH")
.takes_value(true)
.help("The eth1 block hash used when generating a genesis state."),
)
.arg(
Arg::with_name("validator-count")
.long("validator-count")
.value_name("INTEGER")
.takes_value(true)
.help("The number of validators when generating a genesis state."),
)
.arg(
Arg::with_name("genesis-time")
.long("genesis-time")
.value_name("INTEGER")
.takes_value(true)
.help("The genesis time when generating a genesis state."),
)
) )
.subcommand( .subcommand(
SubCommand::with_name("check-deposit-data") SubCommand::with_name("check-deposit-data")

View File

@ -1,8 +1,11 @@
use clap::ArgMatches; use clap::ArgMatches;
use clap_utils::{parse_optional, parse_required, parse_ssz_optional}; use clap_utils::{parse_optional, parse_required, parse_ssz_optional};
use eth2_network_config::Eth2NetworkConfig; use eth2_network_config::Eth2NetworkConfig;
use genesis::interop_genesis_state;
use ssz::Encode;
use std::path::PathBuf; use std::path::PathBuf;
use types::{Address, Config, EthSpec}; use std::time::{SystemTime, UNIX_EPOCH};
use types::{test_utils::generate_deterministic_keypairs, Address, Config, EthSpec};
pub fn run<T: EthSpec>(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Result<(), String> { pub fn run<T: EthSpec>(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Result<(), String> {
let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?; let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?;
@ -54,10 +57,35 @@ pub fn run<T: EthSpec>(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Resul
spec.altair_fork_epoch = Some(fork_epoch); spec.altair_fork_epoch = Some(fork_epoch);
} }
if let Some(fork_epoch) = parse_optional(matches, "merge-fork-epoch")? {
spec.merge_fork_epoch = Some(fork_epoch);
}
let genesis_state_bytes = if matches.is_present("interop-genesis-state") {
let eth1_block_hash = parse_required(matches, "eth1-block-hash")?;
let validator_count = parse_required(matches, "validator-count")?;
let genesis_time = if let Some(time) = parse_optional(matches, "genesis-time")? {
time
} else {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|e| format!("Unable to get time: {:?}", e))?
.as_secs()
};
let keypairs = generate_deterministic_keypairs(validator_count);
let genesis_state =
interop_genesis_state::<T>(&keypairs, genesis_time, eth1_block_hash, &spec)?;
Some(genesis_state.as_ssz_bytes())
} else {
None
};
let testnet = Eth2NetworkConfig { let testnet = Eth2NetworkConfig {
deposit_contract_deploy_block, deposit_contract_deploy_block,
boot_enr: Some(vec![]), boot_enr: Some(vec![]),
genesis_state_bytes: None, genesis_state_bytes,
config: Config::from_chain_spec::<T>(&spec), config: Config::from_chain_spec::<T>(&spec),
}; };