diff --git a/Cargo.lock b/Cargo.lock index 1e1f938703c..2f59d206fb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3031,6 +3031,7 @@ dependencies = [ "eth2_ssz", "eth2_wallet", "genesis", + "int_to_bytes", "lighthouse_version", "log", "sensitive_url", diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index 8bec780dc14..da3ff9beee1 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -928,6 +928,7 @@ mod test { &generate_deterministic_keypairs(validator_count), genesis_time, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, &spec, ) .expect("should create interop genesis state"); @@ -997,6 +998,7 @@ mod test { &keypairs, genesis_time, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, spec, ) .expect("should build state"); diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 78df2b38c8d..cb8680c4301 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -204,6 +204,7 @@ impl Builder> { &validator_keypairs, HARNESS_GENESIS_TIME, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, builder.get_spec(), ) .expect("should generate interop state"); @@ -229,6 +230,7 @@ impl Builder> { &validator_keypairs, HARNESS_GENESIS_TIME, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, builder.get_spec(), ) .expect("should generate interop state"); diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index a9a8701f50c..51a503cbd16 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -234,6 +234,7 @@ where &keypairs, genesis_time, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, &spec, )?; builder.genesis_state(genesis_state).map(|v| (v, None))? diff --git a/beacon_node/genesis/src/eth1_genesis_service.rs b/beacon_node/genesis/src/eth1_genesis_service.rs index 8a5bbd0b16b..aac13a324fc 100644 --- a/beacon_node/genesis/src/eth1_genesis_service.rs +++ b/beacon_node/genesis/src/eth1_genesis_service.rs @@ -373,6 +373,7 @@ impl Eth1GenesisService { eth1_block.hash, eth1_block.timestamp, genesis_deposits(deposit_logs, spec)?, + None, spec, ) .map_err(|e| format!("Unable to initialize genesis state: {:?}", e))?; diff --git a/beacon_node/genesis/src/interop.rs b/beacon_node/genesis/src/interop.rs index 42b7dd51664..d8c25baec80 100644 --- a/beacon_node/genesis/src/interop.rs +++ b/beacon_node/genesis/src/interop.rs @@ -3,7 +3,10 @@ use eth2_hashing::hash; use rayon::prelude::*; use ssz::Encode; use state_processing::initialize_beacon_state_from_eth1; -use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature}; +use types::{ + BeaconState, ChainSpec, DepositData, EthSpec, ExecutionPayloadHeader, Hash256, Keypair, + PublicKey, Signature, +}; pub const DEFAULT_ETH1_BLOCK_HASH: &[u8] = &[0x42; 32]; @@ -15,9 +18,9 @@ pub fn interop_genesis_state( keypairs: &[Keypair], genesis_time: u64, eth1_block_hash: Hash256, + execution_payload_header: Option>, spec: &ChainSpec, ) -> Result, String> { - let eth1_block_hash = eth1_block_hash; let eth1_timestamp = 2_u64.pow(40); let amount = spec.max_effective_balance; @@ -47,6 +50,7 @@ pub fn interop_genesis_state( eth1_block_hash, eth1_timestamp, genesis_deposits(datas, spec)?, + execution_payload_header, spec, ) .map_err(|e| format!("Unable to initialize genesis state: {:?}", e))?; @@ -80,6 +84,7 @@ mod test { &keypairs, genesis_time, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, spec, ) .expect("should build state"); diff --git a/beacon_node/network/src/subnet_service/tests/mod.rs b/beacon_node/network/src/subnet_service/tests/mod.rs index d5f92a53672..27dbb5eff48 100644 --- a/beacon_node/network/src/subnet_service/tests/mod.rs +++ b/beacon_node/network/src/subnet_service/tests/mod.rs @@ -56,6 +56,7 @@ impl TestBeaconChain { &keypairs, 0, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, &spec, ) .expect("should generate interop state"), diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index b5a3c24e707..1bb88c84d16 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -5,7 +5,6 @@ use crate::common::DepositDataTree; use crate::upgrade::{upgrade_to_altair, upgrade_to_merge}; use safe_arith::{ArithError, SafeArith}; use tree_hash::TreeHash; -use types::consts::merge_testing::{GENESIS_BASE_FEE_PER_GAS, GENESIS_GAS_LIMIT}; use types::DEPOSIT_TREE_DEPTH; use types::*; @@ -14,6 +13,7 @@ pub fn initialize_beacon_state_from_eth1( eth1_block_hash: Hash256, eth1_timestamp: u64, deposits: Vec, + execution_payload_header: Option>, spec: &ChainSpec, ) -> Result, BlockProcessingError> { let genesis_time = eth2_genesis_time(eth1_timestamp, spec)?; @@ -52,6 +52,8 @@ pub fn initialize_beacon_state_from_eth1( .map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch()) { upgrade_to_altair(&mut state, spec)?; + + state.fork_mut().previous_version = spec.altair_fork_version; } // Similarly, perform an upgrade to the merge if configured from genesis. @@ -62,18 +64,12 @@ pub fn initialize_beacon_state_from_eth1( upgrade_to_merge(&mut state, spec)?; // Remove intermediate Altair fork from `state.fork`. - state.fork_mut().previous_version = spec.genesis_fork_version; + state.fork_mut().previous_version = spec.merge_fork_version; // Override latest execution payload header. // See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/merge/beacon-chain.md#testing - *state.latest_execution_payload_header_mut()? = ExecutionPayloadHeader { - block_hash: eth1_block_hash, - timestamp: eth1_timestamp, - random: eth1_block_hash, - gas_limit: GENESIS_GAS_LIMIT, - base_fee_per_gas: GENESIS_BASE_FEE_PER_GAS, - ..ExecutionPayloadHeader::default() - }; + *state.latest_execution_payload_header_mut()? = + execution_payload_header.unwrap_or_default(); } // Now that we have our validators, initialize the caches (including the committees) diff --git a/consensus/types/src/beacon_state/tests.rs b/consensus/types/src/beacon_state/tests.rs index ffe04969c1a..b88b49e1a39 100644 --- a/consensus/types/src/beacon_state/tests.rs +++ b/consensus/types/src/beacon_state/tests.rs @@ -562,6 +562,7 @@ fn tree_hash_cache_linear_history_long_skip() { &keypairs, 0, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, spec, ) .unwrap(); diff --git a/consensus/types/src/consts.rs b/consensus/types/src/consts.rs index 6088086ca53..04e8e60ee55 100644 --- a/consensus/types/src/consts.rs +++ b/consensus/types/src/consts.rs @@ -19,13 +19,3 @@ pub mod altair { pub const NUM_FLAG_INDICES: usize = 3; } - -pub mod merge_testing { - use ethereum_types::H256; - pub const GENESIS_GAS_LIMIT: u64 = 30_000_000; - pub const GENESIS_BASE_FEE_PER_GAS: H256 = H256([ - 0x00, 0xca, 0x9a, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]); -} diff --git a/lcli/Cargo.toml b/lcli/Cargo.toml index 964354d2b2d..1f2ea7e57c7 100644 --- a/lcli/Cargo.toml +++ b/lcli/Cargo.toml @@ -19,6 +19,7 @@ serde_json = "1.0.66" env_logger = "0.9.0" types = { path = "../consensus/types" } state_processing = { path = "../consensus/state_processing" } +int_to_bytes = { path = "../consensus/int_to_bytes" } eth2_ssz = "0.4.0" environment = { path = "../lighthouse/environment" } eth2_network_config = { path = "../common/eth2_network_config" } diff --git a/lcli/src/create_payload_header.rs b/lcli/src/create_payload_header.rs new file mode 100644 index 00000000000..31157d4b345 --- /dev/null +++ b/lcli/src/create_payload_header.rs @@ -0,0 +1,39 @@ +use bls::Hash256; +use clap::ArgMatches; +use clap_utils::{parse_optional, parse_required}; +use int_to_bytes::int_to_bytes32; +use ssz::Encode; +use std::fs::File; +use std::io::Write; +use std::time::{SystemTime, UNIX_EPOCH}; +use types::{EthSpec, ExecutionPayloadHeader}; + +pub fn run(matches: &ArgMatches) -> Result<(), String> { + let eth1_block_hash = parse_required(matches, "execution-block-hash")?; + let genesis_time = parse_optional(matches, "genesis-time")?.unwrap_or( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| format!("Unable to get time: {:?}", e))? + .as_secs(), + ); + let base_fee_per_gas = Hash256::from_slice(&int_to_bytes32(parse_required( + matches, + "base-fee-per-gas", + )?)); + let gas_limit = parse_required(matches, "gas-limit")?; + let file_name = matches.value_of("file").ok_or("No file supplied")?; + + let execution_payload_header: ExecutionPayloadHeader = ExecutionPayloadHeader { + gas_limit, + base_fee_per_gas, + timestamp: genesis_time, + block_hash: eth1_block_hash, + random: eth1_block_hash, + ..ExecutionPayloadHeader::default() + }; + let mut file = File::create(file_name).map_err(|_| "Unable to create file".to_string())?; + let bytes = execution_payload_header.as_ssz_bytes(); + file.write_all(bytes.as_slice()) + .map_err(|_| "Unable to write to file".to_string())?; + Ok(()) +} diff --git a/lcli/src/interop_genesis.rs b/lcli/src/interop_genesis.rs index 20e221fb9e5..57a5ba00988 100644 --- a/lcli/src/interop_genesis.rs +++ b/lcli/src/interop_genesis.rs @@ -38,6 +38,7 @@ pub fn run(testnet_dir: PathBuf, matches: &ArgMatches) -> Result<(), &keypairs, genesis_time, Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH), + None, &spec, )?; diff --git a/lcli/src/main.rs b/lcli/src/main.rs index d6dc5a7f22f..279d4b485b5 100644 --- a/lcli/src/main.rs +++ b/lcli/src/main.rs @@ -2,6 +2,7 @@ extern crate log; mod change_genesis_time; mod check_deposit_data; +mod create_payload_header; mod deploy_deposit_contract; mod eth1_genesis; mod etl; @@ -271,6 +272,57 @@ fn main() { .help("The mnemonic for key derivation."), ), ) + .subcommand( + SubCommand::with_name("create-payload-header") + .about("Generates an SSZ file containing bytes for an `ExecutionPayloadHeader`. \ + Useful as input for `lcli new-testnet --execution-payload-header FILE`. ") + .arg( + Arg::with_name("execution-block-hash") + .long("execution-block-hash") + .value_name("BLOCK_HASH") + .takes_value(true) + .help("The block hash used when generating an execution payload. This \ + value is used for `execution_payload_header.block_hash` as well as \ + `execution_payload_header.random`") + .required(true) + .default_value( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ) + .arg( + Arg::with_name("genesis-time") + .long("genesis-time") + .value_name("INTEGER") + .takes_value(true) + .help("The genesis time when generating an execution payload.") + ) + .arg( + Arg::with_name("base-fee-per-gas") + .long("base-fee-per-gas") + .value_name("INTEGER") + .takes_value(true) + .help("The base fee per gas field in the execution payload generated.") + .required(true) + .default_value("1000000000"), + ) + .arg( + Arg::with_name("gas-limit") + .long("gas-limit") + .value_name("INTEGER") + .takes_value(true) + .help("The gas limit field in the execution payload generated.") + .required(true) + .default_value("30000000"), + ) + .arg( + Arg::with_name("file") + .long("file") + .value_name("FILE") + .takes_value(true) + .required(true) + .help("Output file"), + ) + ) .subcommand( SubCommand::with_name("new-testnet") .about( @@ -426,6 +478,15 @@ fn main() { .takes_value(true) .help("The eth1 block hash used when generating a genesis state."), ) + .arg( + Arg::with_name("execution-payload-header") + .long("execution-payload-header") + .value_name("FILE") + .takes_value(true) + .required(false) + .help("Path to file containing `ExecutionPayloadHeader` SSZ bytes to be \ + used in the genesis state."), + ) .arg( Arg::with_name("validator-count") .long("validator-count") @@ -653,6 +714,8 @@ fn run( change_genesis_time::run::(testnet_dir, matches) .map_err(|e| format!("Failed to run change-genesis-time command: {}", e)) } + ("create-payload-header", Some(matches)) => create_payload_header::run::(matches) + .map_err(|e| format!("Failed to run create-payload-header command: {}", e)), ("replace-state-pubkeys", Some(matches)) => { replace_state_pubkeys::run::(testnet_dir, matches) .map_err(|e| format!("Failed to run replace-state-pubkeys command: {}", e)) diff --git a/lcli/src/new_testnet.rs b/lcli/src/new_testnet.rs index 8cea19d05ce..630d65963a0 100644 --- a/lcli/src/new_testnet.rs +++ b/lcli/src/new_testnet.rs @@ -2,10 +2,15 @@ use clap::ArgMatches; use clap_utils::{parse_optional, parse_required, parse_ssz_optional}; use eth2_network_config::Eth2NetworkConfig; use genesis::interop_genesis_state; +use ssz::Decode; use ssz::Encode; +use std::fs::File; +use std::io::Read; use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; -use types::{test_utils::generate_deterministic_keypairs, Address, Config, EthSpec}; +use types::{ + test_utils::generate_deterministic_keypairs, Address, Config, EthSpec, ExecutionPayloadHeader, +}; pub fn run(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Result<(), String> { let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?; @@ -62,20 +67,51 @@ pub fn run(testnet_dir_path: PathBuf, matches: &ArgMatches) -> Resul } 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 + let execution_payload_header: Option> = + parse_optional(matches, "execution-payload-header")? + .map(|filename: String| { + let mut bytes = vec![]; + let mut file = File::open(filename.as_str()) + .map_err(|e| format!("Unable to open {}: {}", filename, e))?; + file.read_to_end(&mut bytes) + .map_err(|e| format!("Unable to read {}: {}", filename, e))?; + ExecutionPayloadHeader::::from_ssz_bytes(bytes.as_slice()) + .map_err(|e| format!("SSZ decode failed: {:?}", e)) + }) + .transpose()?; + + let (eth1_block_hash, genesis_time) = if let Some(payload) = + execution_payload_header.as_ref() + { + let eth1_block_hash = + parse_optional(matches, "eth1-block-hash")?.unwrap_or(payload.block_hash); + let genesis_time = + parse_optional(matches, "genesis-time")?.unwrap_or(payload.timestamp); + (eth1_block_hash, genesis_time) } else { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|e| format!("Unable to get time: {:?}", e))? - .as_secs() + let eth1_block_hash = parse_required(matches, "eth1-block-hash").map_err(|_| { + "One of `--execution-payload-header` or `--eth1-block-hash` must be set".to_string() + })?; + let genesis_time = parse_optional(matches, "genesis-time")?.unwrap_or( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| format!("Unable to get time: {:?}", e))? + .as_secs(), + ); + (eth1_block_hash, genesis_time) }; + let validator_count = parse_required(matches, "validator-count")?; + let keypairs = generate_deterministic_keypairs(validator_count); - let genesis_state = - interop_genesis_state::(&keypairs, genesis_time, eth1_block_hash, &spec)?; + + let genesis_state = interop_genesis_state::( + &keypairs, + genesis_time, + eth1_block_hash, + execution_payload_header, + &spec, + )?; Some(genesis_state.as_ssz_bytes()) } else { diff --git a/testing/ef_tests/Makefile b/testing/ef_tests/Makefile index 772afea3aeb..f56cc38cafc 100644 --- a/testing/ef_tests/Makefile +++ b/testing/ef_tests/Makefile @@ -1,4 +1,4 @@ -TESTS_TAG := v1.1.0 +TESTS_TAG := v1.1.1 TESTS = general minimal mainnet TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS)) diff --git a/testing/ef_tests/src/cases/genesis_initialization.rs b/testing/ef_tests/src/cases/genesis_initialization.rs index 2a9323c96a2..dc139ac0b9f 100644 --- a/testing/ef_tests/src/cases/genesis_initialization.rs +++ b/testing/ef_tests/src/cases/genesis_initialization.rs @@ -4,11 +4,12 @@ use crate::decode::{ssz_decode_file, ssz_decode_state, yaml_decode_file}; use serde_derive::Deserialize; use state_processing::initialize_beacon_state_from_eth1; use std::path::PathBuf; -use types::{BeaconState, Deposit, EthSpec, ForkName, Hash256}; +use types::{BeaconState, Deposit, EthSpec, ExecutionPayloadHeader, ForkName, Hash256}; #[derive(Debug, Clone, Deserialize)] struct Metadata { deposits_count: usize, + execution_payload_header: Option, } #[derive(Debug, Clone, Deserialize)] @@ -24,6 +25,7 @@ pub struct GenesisInitialization { pub eth1_block_hash: Hash256, pub eth1_timestamp: u64, pub deposits: Vec, + pub execution_payload_header: Option>, pub state: Option>, } @@ -34,6 +36,14 @@ impl LoadCase for GenesisInitialization { eth1_timestamp, } = yaml_decode_file(&path.join("eth1.yaml"))?; let meta: Metadata = yaml_decode_file(&path.join("meta.yaml"))?; + let execution_payload_header: Option> = + if meta.execution_payload_header.unwrap_or(false) { + Some(ssz_decode_file( + &path.join("execution_payload_header.ssz_snappy"), + )?) + } else { + None + }; let deposits: Vec = (0..meta.deposits_count) .map(|i| { let filename = format!("deposits_{}.ssz_snappy", i); @@ -48,6 +58,7 @@ impl LoadCase for GenesisInitialization { eth1_block_hash, eth1_timestamp, deposits, + execution_payload_header, state: Some(state), }) } @@ -66,6 +77,7 @@ impl Case for GenesisInitialization { self.eth1_block_hash, self.eth1_timestamp, self.deposits.clone(), + self.execution_payload_header.clone(), spec, );