diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index d70561db9ba..5fb572d15ca 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -46,8 +46,8 @@ use tracing::{debug, error, info, warn}; use tree_hash::TreeHash; use types::data::CustodyIndex; use types::{ - BeaconState, BlobSidecarList, ChainSpec, ColumnIndex, DataColumnSidecarList, Epoch, EthSpec, - Hash256, SignedBeaconBlock, Slot, + BeaconBlock, BeaconState, BlobSidecarList, ChainSpec, ColumnIndex, DataColumnSidecarList, + Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot, }; /// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing @@ -1177,9 +1177,19 @@ fn make_genesis_block( genesis_state: &mut BeaconState, spec: &ChainSpec, ) -> Result, String> { + // For Gloas, genesis_block() populates the bid in the block body. However, if + // the genesis state was produced by an external tool (e.g. ethereum-genesis-generator), + // its latest_block_header.body_root may correspond to an empty block. In that case, + // use an empty block so the stored block root matches what fork choice derives from + // the state's latest_block_header. let mut block = genesis_block(genesis_state, spec) .map_err(|e| format!("Error building genesis block: {:?}", e))?; + let state_body_root = genesis_state.latest_block_header().body_root; + if state_body_root != block.body_root() { + block = BeaconBlock::empty(spec); + } + *block.state_root_mut() = genesis_state .update_tree_hash_cache() .map_err(|e| format!("Error hashing genesis state: {:?}", e))?; diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 46541e03263..c643ad56e34 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -167,12 +167,19 @@ pub fn initialize_beacon_state_from_eth1( // Remove intermediate Fulu fork from `state.fork`. state.fork_mut().previous_version = spec.gloas_fork_version; + // The genesis block's bid must have block_hash = 0x00 per spec (empty payload). // Retain the EL genesis hash in latest_block_hash and parent_block_hash so the // first post-genesis proposer can build on the correct EL head. let el_genesis_hash = state.latest_execution_payload_bid()?.block_hash; let bid = state.latest_execution_payload_bid_mut()?; bid.parent_block_hash = el_genesis_hash; bid.block_hash = ExecutionBlockHash::default(); + + // Update the `latest_block_header.body_root` so that it matches the body of the + // Gloas genesis block, which embeds `state.latest_execution_payload_bid` in its + // `signed_execution_payload_bid` field (see `genesis_block`). + let genesis_body_root = genesis_block(&state, spec)?.body_root(); + state.latest_block_header_mut().body_root = genesis_body_root; } // Now that we have our validators, initialize the caches (including the committees) @@ -184,16 +191,25 @@ pub fn initialize_beacon_state_from_eth1( Ok(state) } -/// Create an unsigned genesis `BeaconBlock` matching the genesis state. +/// Create an unsigned genesis `BeaconBlock`. +/// +/// Per spec, the genesis block body is empty (all default fields) except for Gloas, +/// where `body.signed_execution_payload_bid.message` is initialised from +/// `state.latest_execution_payload_bid` so that the first post-genesis proposer can +/// build on the correct execution layer head. /// -/// Per spec, the genesis block body is empty (all default fields). -/// `state.latest_block_header.body_root` is set from `BeaconBlock::empty()`, -/// so this function must return the same empty block to keep roots consistent. +/// `state.latest_block_header.body_root` is set from this same block's body, so the +/// two must stay in sync. pub fn genesis_block( - _genesis_state: &BeaconState, + state: &BeaconState, spec: &ChainSpec, ) -> Result, BeaconStateError> { - Ok(BeaconBlock::empty(spec)) + let mut block = BeaconBlock::empty(spec); + if let BeaconBlock::Gloas(ref mut gloas_block) = block { + let bid = state.latest_execution_payload_bid()?.clone(); + gloas_block.body.signed_execution_payload_bid.message = bid; + } + Ok(block) } /// Determine whether a candidate genesis state is suitable for starting the chain.