Gloas spec v1.7.0-alpha.5 and beacon_chain tests (#8998)

Fix database pruning post-Gloas


  - Fix DB pruning logic (and state summaries DAG)
- Get the `beacon_chain` tests running with `FORK_NAME=gloas` 🎉


Co-Authored-By: Michael Sproul <michael@sigmaprime.io>

Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>

Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>

Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com>

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>
This commit is contained in:
Michael Sproul
2026-04-21 16:29:15 +10:00
committed by GitHub
parent c028bac28d
commit cf3d5e285e
82 changed files with 1513 additions and 1391 deletions

View File

@@ -1,11 +1,6 @@
use crate::{
BlockProcessingError, BlockSignatureStrategy, ConsensusContext, SlotProcessingError,
VerifyBlockRoot, VerifySignatures,
envelope_processing::{
EnvelopeProcessingError, VerifyStateRoot, process_execution_payload_envelope,
},
per_block_processing,
per_epoch_processing::EpochProcessingSummary,
VerifyBlockRoot, per_block_processing, per_epoch_processing::EpochProcessingSummary,
per_slot_processing,
};
use itertools::Itertools;
@@ -13,7 +8,7 @@ use std::iter::Peekable;
use std::marker::PhantomData;
use types::{
BeaconState, BeaconStateError, BlindedPayload, ChainSpec, EthSpec, Hash256, SignedBeaconBlock,
SignedExecutionPayloadEnvelope, Slot, execution::StatePayloadStatus,
Slot,
};
pub type PreBlockHook<'a, E, Error> = Box<
@@ -29,7 +24,7 @@ pub type PostSlotHook<'a, E, Error> = Box<
>;
pub type StateRootIterDefault<Error> = std::iter::Empty<Result<(Hash256, Slot), Error>>;
/// Efficiently apply blocks and payloads to a state while configuring various parameters.
/// Efficiently apply blocks to a state while configuring various parameters.
///
/// Usage follows a builder pattern.
pub struct BlockReplayer<
@@ -46,21 +41,8 @@ pub struct BlockReplayer<
post_block_hook: Option<PostBlockHook<'a, Spec, Error>>,
pre_slot_hook: Option<PreSlotHook<'a, Spec, Error>>,
post_slot_hook: Option<PostSlotHook<'a, Spec, Error>>,
/// Iterator over state roots for all *block* states.
///
/// Pre-Gloas, this is all states. Post-Gloas, this is *just* the states corresponding to beacon
/// blocks. For states corresponding to payloads, we read the state root from the payload
/// envelope.
// TODO(gloas): this concept might need adjusting when we implement the cold DB.
pub(crate) state_root_iter: Option<Peekable<StateRootIter>>,
state_root_miss: bool,
/// The payload status of the state desired as the end result of block replay.
///
/// This dictates whether a payload should be applied after applying the last block.
///
/// Prior to Gloas, this should always be set to `StatePayloadStatus::Pending` to indicate
/// that no envelope needs to be applied.
desired_state_payload_status: StatePayloadStatus,
_phantom: PhantomData<Error>,
}
@@ -68,12 +50,7 @@ pub struct BlockReplayer<
pub enum BlockReplayError {
SlotProcessing(SlotProcessingError),
BlockProcessing(BlockProcessingError),
EnvelopeProcessing(EnvelopeProcessingError),
BeaconState(BeaconStateError),
/// A payload envelope for this `slot` was required but not provided.
MissingPayloadEnvelope {
slot: Slot,
},
}
impl From<SlotProcessingError> for BlockReplayError {
@@ -88,12 +65,6 @@ impl From<BlockProcessingError> for BlockReplayError {
}
}
impl From<EnvelopeProcessingError> for BlockReplayError {
fn from(e: EnvelopeProcessingError) -> Self {
Self::EnvelopeProcessing(e)
}
}
impl From<BeaconStateError> for BlockReplayError {
fn from(e: BeaconStateError) -> Self {
Self::BeaconState(e)
@@ -125,7 +96,6 @@ where
post_slot_hook: None,
state_root_iter: None,
state_root_miss: false,
desired_state_payload_status: StatePayloadStatus::Pending,
_phantom: PhantomData,
}
}
@@ -191,14 +161,6 @@ where
self
}
/// Set the desired payload status of the state reached by replay.
///
/// This determines whether to apply a payload after applying the last block.
pub fn desired_state_payload_status(mut self, payload_status: StatePayloadStatus) -> Self {
self.desired_state_payload_status = payload_status;
self
}
/// Compute the state root for `self.state` as efficiently as possible.
///
/// This function MUST only be called when `self.state` is a post-state, i.e. it MUST not be
@@ -246,38 +208,6 @@ where
Ok(state_root)
}
/// Apply an execution payload envelope to `self.state`.
///
/// The `block_state_root` MUST be the `state_root` of the most recently applied block.
///
/// Returns the `state_root` of `self.state` after payload application.
fn apply_payload_envelope(
&mut self,
envelope: &SignedExecutionPayloadEnvelope<E>,
block_state_root: Hash256,
) -> Result<Hash256, Error> {
// TODO(gloas): bulk signature verification could be relevant here?
let verify_payload_signatures =
if let BlockSignatureStrategy::NoVerification = self.block_sig_strategy {
VerifySignatures::False
} else {
VerifySignatures::True
};
// TODO(gloas): state root verif enabled during initial prototyping
let verify_state_root = VerifyStateRoot::True;
process_execution_payload_envelope(
&mut self.state,
Some(block_state_root),
envelope,
verify_payload_signatures,
verify_state_root,
self.spec,
)
.map_err(BlockReplayError::from)?;
Ok(envelope.message.state_root)
}
/// Apply `blocks` atop `self.state`, taking care of slot processing.
///
/// If `target_slot` is provided then the state will be advanced through to `target_slot`
@@ -285,21 +215,8 @@ where
pub fn apply_blocks(
mut self,
blocks: Vec<SignedBeaconBlock<E, BlindedPayload<E>>>,
payload_envelopes: Vec<SignedExecutionPayloadEnvelope<E>>,
target_slot: Option<Slot>,
) -> Result<Self, Error> {
let mut envelopes_iter = payload_envelopes.into_iter();
let mut next_envelope_at_slot = |slot| {
if let Some(envelope) = envelopes_iter.next()
&& envelope.message.slot == slot
{
Ok(envelope)
} else {
Err(BlockReplayError::MissingPayloadEnvelope { slot })
}
};
for (i, block) in blocks.iter().enumerate() {
// Allow one additional block at the start which is only used for its state root.
if i == 0 && block.slot() <= self.state.slot() {
@@ -307,36 +224,7 @@ where
}
while self.state.slot() < block.slot() {
let mut state_root = self.get_state_root(&blocks, i)?;
// Apply the payload for the *previous* block if the bid in the current block
// indicates that the parent is full (and it hasn't already been applied).
state_root = if block.fork_name_unchecked().gloas_enabled()
&& self.state.slot() == self.state.latest_block_header().slot
&& self.state.payload_status() == StatePayloadStatus::Pending
{
let latest_bid_block_hash = self
.state
.latest_execution_payload_bid()
.map_err(BlockReplayError::from)?
.block_hash;
// Similar to `is_parent_block_full`, but reading the block hash from the
// not-yet-applied `block`. The slot 0 case covers genesis (no block replay reqd).
if self.state.slot() != 0 && block.is_parent_block_full(latest_bid_block_hash) {
let envelope = next_envelope_at_slot(self.state.slot())?;
// State root for the next slot processing is now the envelope's state root.
self.apply_payload_envelope(&envelope, state_root)?
} else {
// Empty payload at this slot, the state root is unchanged from when the
// beacon block was applied.
state_root
}
} else {
// Pre-Gloas or at skipped slots post-Gloas, the state root of the parent state
// is always the output from `self.get_state_root`.
state_root
};
let state_root = self.get_state_root(&blocks, i)?;
if let Some(ref mut pre_slot_hook) = self.pre_slot_hook {
pre_slot_hook(state_root, &mut self.state)?;
@@ -380,24 +268,9 @@ where
}
}
// Apply the last payload if desired.
let mut opt_state_root = if let StatePayloadStatus::Full = self.desired_state_payload_status
&& let Some(last_block) = blocks.last()
{
let envelope = next_envelope_at_slot(self.state.slot())?;
Some(self.apply_payload_envelope(&envelope, last_block.state_root())?)
} else {
None
};
if let Some(target_slot) = target_slot {
while self.state.slot() < target_slot {
// Read state root from `opt_state_root` if a payload was just applied.
let state_root = if let Some(root) = opt_state_root.take() {
root
} else {
self.get_state_root(&blocks, blocks.len())?
};
let state_root = self.get_state_root(&blocks, blocks.len())?;
if let Some(ref mut pre_slot_hook) = self.pre_slot_hook {
pre_slot_hook(state_root, &mut self.state)?;

View File

@@ -1,15 +1,10 @@
use crate::BlockProcessingError;
use crate::VerifySignatures;
use crate::per_block_processing::compute_timestamp_at_slot;
use crate::per_block_processing::process_operations::{
process_consolidation_requests, process_deposit_requests_post_gloas,
process_withdrawal_requests,
};
use safe_arith::{ArithError, SafeArith};
use safe_arith::ArithError;
use tree_hash::TreeHash;
use types::{
BeaconState, BeaconStateError, BuilderIndex, BuilderPendingPayment, ChainSpec, EthSpec,
ExecutionBlockHash, Hash256, SignedExecutionPayloadEnvelope, Slot,
BeaconState, BeaconStateError, BuilderIndex, ChainSpec, EthSpec, ExecutionBlockHash, Hash256,
SignedExecutionPayloadEnvelope, Slot,
};
macro_rules! envelope_verify {
@@ -20,29 +15,11 @@ macro_rules! envelope_verify {
};
}
/// The strategy to be used when validating the payloads state root.
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(PartialEq, Clone, Copy)]
pub enum VerifyStateRoot {
/// Validate state root.
True,
/// Do not validate state root. Use with caution.
/// This should only be used when first constructing the payload envelope.
False,
}
impl VerifyStateRoot {
pub fn is_true(self) -> bool {
self == VerifyStateRoot::True
}
}
#[derive(Debug, Clone)]
pub enum EnvelopeProcessingError {
/// Bad Signature
BadSignature,
BeaconStateError(BeaconStateError),
BlockProcessingError(BlockProcessingError),
ArithError(ArithError),
/// Envelope doesn't match latest beacon block header
LatestBlockHeaderMismatch {
@@ -89,15 +66,11 @@ pub enum EnvelopeProcessingError {
state: u64,
envelope: u64,
},
// Invalid state root
InvalidStateRoot {
state: Hash256,
// The execution requests root doesn't match the committed bid
ExecutionRequestsRootMismatch {
committed_bid: Hash256,
envelope: Hash256,
},
// BitFieldError
BitFieldError(ssz::BitfieldError),
// Some kind of error calculating the builder payment index
BuilderPaymentIndexOutOfBounds(usize),
/// The envelope was deemed invalid by the execution engine.
ExecutionInvalid,
}
@@ -108,50 +81,44 @@ impl From<BeaconStateError> for EnvelopeProcessingError {
}
}
impl From<BlockProcessingError> for EnvelopeProcessingError {
fn from(e: BlockProcessingError) -> Self {
EnvelopeProcessingError::BlockProcessingError(e)
}
}
impl From<ArithError> for EnvelopeProcessingError {
fn from(e: ArithError) -> Self {
EnvelopeProcessingError::ArithError(e)
}
}
/// Processes a `SignedExecutionPayloadEnvelope`
/// Verifies a `SignedExecutionPayloadEnvelope` against the beacon state.
///
/// This function does all the state modifications inside `process_execution_payload()`
pub fn process_execution_payload_envelope<E: EthSpec>(
state: &mut BeaconState<E>,
parent_state_root: Option<Hash256>,
/// This function performs pure verification with no state mutation. The execution requests
/// from the envelope are deferred to be processed in the next block via
/// `process_parent_execution_payload`.
///
/// `block_state_root` should be the post-block state root (used to fill in the block header
/// for beacon_block_root verification). If `None`, the latest_block_header must already have
/// its state_root filled in.
pub fn verify_execution_payload_envelope<E: EthSpec>(
state: &BeaconState<E>,
signed_envelope: &SignedExecutionPayloadEnvelope<E>,
verify_signatures: VerifySignatures,
verify_state_root: VerifyStateRoot,
block_state_root: Hash256,
spec: &ChainSpec,
) -> Result<(), EnvelopeProcessingError> {
if verify_signatures.is_true() {
// Verify Signed Envelope Signature
if !signed_envelope.verify_signature_with_state(state, spec)? {
return Err(EnvelopeProcessingError::BadSignature);
}
if verify_signatures.is_true() && !signed_envelope.verify_signature_with_state(state, spec)? {
return Err(EnvelopeProcessingError::BadSignature);
}
let envelope = &signed_envelope.message;
let payload = &envelope.payload;
let execution_requests = &envelope.execution_requests;
// Cache latest block header state root
if state.latest_block_header().state_root == Hash256::default() {
let previous_state_root = parent_state_root
.map(Ok)
.unwrap_or_else(|| state.canonical_root())?;
state.latest_block_header_mut().state_root = previous_state_root;
// Verify consistency with the beacon block.
// Use a copy of the header with state_root filled in, matching the spec's approach.
let mut header = state.latest_block_header().clone();
if header.state_root == Hash256::default() {
// The caller must provide the post-block state root so we can compute
// the block header root without mutating state.
header.state_root = block_state_root;
}
// Verify consistency with the beacon block
let latest_block_header_root = state.latest_block_header().tree_hash_root();
let latest_block_header_root = header.tree_hash_root();
envelope_verify!(
envelope.beacon_block_root == latest_block_header_root,
EnvelopeProcessingError::LatestBlockHeaderMismatch {
@@ -160,9 +127,9 @@ pub fn process_execution_payload_envelope<E: EthSpec>(
}
);
envelope_verify!(
envelope.slot == state.slot(),
envelope.slot() == state.slot(),
EnvelopeProcessingError::SlotMismatch {
envelope_slot: envelope.slot,
envelope_slot: envelope.slot(),
parent_state_slot: state.slot(),
}
);
@@ -238,59 +205,17 @@ pub fn process_execution_payload_envelope<E: EthSpec>(
}
);
// Verify execution requests root matches committed bid
let execution_requests_root = envelope.execution_requests.tree_hash_root();
envelope_verify!(
execution_requests_root == committed_bid.execution_requests_root,
EnvelopeProcessingError::ExecutionRequestsRootMismatch {
committed_bid: committed_bid.execution_requests_root,
envelope: execution_requests_root,
}
);
// TODO(gloas): newPayload happens here in the spec, ensure we wire that up correctly
process_deposit_requests_post_gloas(state, &execution_requests.deposits, spec)?;
process_withdrawal_requests(state, &execution_requests.withdrawals, spec)?;
process_consolidation_requests(state, &execution_requests.consolidations, spec)?;
// Queue the builder payment
let payment_index = E::slots_per_epoch()
.safe_add(state.slot().as_u64().safe_rem(E::slots_per_epoch())?)?
as usize;
let payment_mut = state
.builder_pending_payments_mut()?
.get_mut(payment_index)
.ok_or(EnvelopeProcessingError::BuilderPaymentIndexOutOfBounds(
payment_index,
))?;
// We have re-ordered the blanking out of the pending payment to avoid a double-lookup.
// This is semantically equivalent to the ordering used by the spec because we have taken a
// clone of the payment prior to doing the write.
let payment_withdrawal = payment_mut.withdrawal.clone();
*payment_mut = BuilderPendingPayment::default();
let amount = payment_withdrawal.amount;
if amount > 0 {
state
.builder_pending_withdrawals_mut()?
.push(payment_withdrawal)
.map_err(|e| EnvelopeProcessingError::BeaconStateError(e.into()))?;
}
// Cache the execution payload hash
let availability_index = state
.slot()
.as_usize()
.safe_rem(E::slots_per_historical_root())?;
state
.execution_payload_availability_mut()?
.set(availability_index, true)
.map_err(EnvelopeProcessingError::BitFieldError)?;
*state.latest_block_hash_mut()? = payload.block_hash;
if verify_state_root.is_true() {
// Verify the state root
let state_root = state.canonical_root()?;
envelope_verify!(
envelope.state_root == state_root,
EnvelopeProcessingError::InvalidStateRoot {
state: state_root,
envelope: envelope.state_root,
}
);
}
Ok(())
}

View File

@@ -167,9 +167,21 @@ pub fn initialize_beacon_state_from_eth1<E: EthSpec>(
// Remove intermediate Fulu fork from `state.fork`.
state.fork_mut().previous_version = spec.gloas_fork_version;
// Override latest execution payload header.
// Here's where we *would* clone the header but there is no header here so..
// TODO(EIP7732): check this
// 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 latest_block_header to reflect the Gloas genesis block body which contains
// the EL genesis hash in the signed_execution_payload_bid. This is needed because
// BeaconState::new() created the header from BeaconBlock::empty() which has zero bid
// fields, but the spec requires the genesis block's bid to contain the EL block hash
// and the tree hash root of empty ExecutionRequests.
let block = genesis_block(&state, spec)?;
state.latest_block_header_mut().body_root = block.body_root();
}
// Now that we have our validators, initialize the caches (including the committees)
@@ -181,6 +193,28 @@ pub fn initialize_beacon_state_from_eth1<E: EthSpec>(
Ok(state)
}
/// Create an unsigned genesis `BeaconBlock` whose body matches the genesis state.
///
/// For Gloas, the block's `signed_execution_payload_bid` is populated from the state's
/// `latest_execution_payload_bid` so that the body root is consistent with
/// `state.latest_block_header.body_root`.
///
/// The returned block has `state_root == Hash256::ZERO`; callers that need the real
/// state root should set it themselves.
pub fn genesis_block<E: EthSpec>(
genesis_state: &BeaconState<E>,
spec: &ChainSpec,
) -> Result<BeaconBlock<E>, BeaconStateError> {
let mut block = BeaconBlock::empty(spec);
if let Ok(block) = block.as_gloas_mut() {
let state_bid = genesis_state.latest_execution_payload_bid()?;
let bid = &mut block.body.signed_execution_payload_bid.message;
bid.block_hash = state_bid.block_hash;
bid.execution_requests_root = state_bid.execution_requests_root;
}
Ok(block)
}
/// Determine whether a candidate genesis state is suitable for starting the chain.
pub fn is_valid_genesis_state<E: EthSpec>(state: &BeaconState<E>, spec: &ChainSpec) -> bool {
state

View File

@@ -120,7 +120,7 @@ pub fn per_block_processing<E: EthSpec, Payload: AbstractExecPayload<E>>(
let block = signed_block.message();
// Verify that the `SignedBeaconBlock` instantiation matches the fork at `signed_block.slot()`.
signed_block
let fork_name = signed_block
.fork_name(spec)
.map_err(BlockProcessingError::InconsistentBlockFork)?;
@@ -129,6 +129,11 @@ pub fn per_block_processing<E: EthSpec, Payload: AbstractExecPayload<E>>(
.fork_name(spec)
.map_err(BlockProcessingError::InconsistentStateFork)?;
// Process deferred execution requests from the parent's envelope.
if fork_name.gloas_enabled() {
process_parent_execution_payload(state, block, spec)?;
}
// Build epoch cache if it hasn't already been built, or if it is no longer valid
initialize_epoch_cache(state, spec)?;
initialize_progressive_balances_cache(state, spec)?;
@@ -531,6 +536,139 @@ pub fn compute_timestamp_at_slot<E: EthSpec>(
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
}
/// Process the parent block's deferred execution payload effects.
///
/// This implements the spec's `process_parent_execution_payload` function, which validates
/// the parent execution requests and delegates to `apply_parent_execution_payload` if the
/// parent block was full. This is called at the beginning of block processing, before
/// `process_block_header`.
///
/// `process_parent_execution_payload` must be called before `process_execution_payload_bid`
/// (which overwrites `state.latest_execution_payload_bid`).
pub fn process_parent_execution_payload<E: EthSpec, Payload: AbstractExecPayload<E>>(
state: &mut BeaconState<E>,
block: BeaconBlockRef<'_, E, Payload>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
let bid_parent_block_hash = block
.body()
.signed_execution_payload_bid()?
.message
.parent_block_hash;
let parent_bid = state.latest_execution_payload_bid()?.clone();
let requests = block.body().parent_execution_requests()?;
let is_genesis_block = parent_bid.block_hash == ExecutionBlockHash::zero();
let is_parent_block_empty = bid_parent_block_hash != parent_bid.block_hash;
if is_genesis_block || is_parent_block_empty {
// Parent was EMPTY -- no execution requests expected
block_verify!(
*requests == ExecutionRequests::default(),
BlockProcessingError::NonEmptyParentExecutionRequests
);
return Ok(());
}
// Parent was FULL -- verify the bid commitment and apply the payload
let requests_root = requests.tree_hash_root();
block_verify!(
requests_root == parent_bid.execution_requests_root,
BlockProcessingError::ExecutionRequestsRootMismatch {
expected: parent_bid.execution_requests_root,
found: requests_root,
}
);
apply_parent_execution_payload(state, &parent_bid, requests, spec)
}
/// Apply the parent execution payload's deferred effects to the state.
///
/// This implements the spec's `apply_parent_execution_payload` function:
/// 1. Processes deposits, withdrawals, and consolidations from execution requests
/// 2. Queues the builder pending payment from the parent's committed bid
/// 3. Updates `execution_payload_availability` and `latest_block_hash`
pub fn apply_parent_execution_payload<E: EthSpec>(
state: &mut BeaconState<E>,
parent_bid: &ExecutionPayloadBid<E>,
requests: &ExecutionRequests<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
let parent_slot = parent_bid.slot;
let parent_epoch = parent_slot.epoch(E::slots_per_epoch());
// Process execution requests from the parent's payload
process_operations::process_deposit_requests_post_gloas(state, &requests.deposits, spec)?;
process_operations::process_withdrawal_requests(state, &requests.withdrawals, spec)?;
process_operations::process_consolidation_requests(state, &requests.consolidations, spec)?;
// Queue the builder payment
if parent_epoch == state.current_epoch() {
let payment_index = E::slots_per_epoch()
.safe_add(parent_slot.as_u64().safe_rem(E::slots_per_epoch())?)?
as usize;
settle_builder_payment(state, payment_index)?;
} else if parent_epoch == state.previous_epoch() {
let payment_index = parent_slot.as_u64().safe_rem(E::slots_per_epoch())? as usize;
settle_builder_payment(state, payment_index)?;
} else if parent_bid.value > 0 {
// Parent is older than previous epoch -- payment entry has already been
// settled or evicted by process_builder_pending_payments at epoch boundaries.
// Append the withdrawal directly from the bid.
state
.builder_pending_withdrawals_mut()?
.push(BuilderPendingWithdrawal {
fee_recipient: parent_bid.fee_recipient,
amount: parent_bid.value,
builder_index: parent_bid.builder_index,
})
.map_err(|e| BlockProcessingError::BeaconStateError(e.into()))?;
}
// Update execution payload availability for the parent slot
let availability_index = parent_slot
.as_usize()
.safe_rem(E::slots_per_historical_root())?;
state
.execution_payload_availability_mut()?
.set(availability_index, true)
.map_err(BlockProcessingError::BitfieldError)?;
// Update latest_block_hash to the parent bid's block_hash
*state.latest_block_hash_mut()? = parent_bid.block_hash;
Ok(())
}
/// Spec: `settle_builder_payment`.
///
/// Moves a pending payment from `builder_pending_payments[payment_index]` into
/// `builder_pending_withdrawals`, then clears the slot.
pub fn settle_builder_payment<E: EthSpec>(
state: &mut BeaconState<E>,
payment_index: usize,
) -> Result<(), BlockProcessingError> {
let payment_mut = state
.builder_pending_payments_mut()?
.get_mut(payment_index)
.ok_or(BlockProcessingError::BuilderPaymentIndexOutOfBounds(
payment_index,
))?;
let withdrawal = payment_mut.withdrawal.clone();
*payment_mut = BuilderPendingPayment::default();
if withdrawal.amount > 0 {
state
.builder_pending_withdrawals_mut()?
.push(withdrawal)
.map_err(|e| BlockProcessingError::BeaconStateError(e.into()))?;
}
Ok(())
}
pub fn process_execution_payload_bid<E: EthSpec, Payload: AbstractExecPayload<E>>(
state: &mut BeaconState<E>,
block: BeaconBlockRef<'_, E, Payload>,

View File

@@ -108,6 +108,13 @@ pub enum BlockProcessingError {
},
/// Builder payment index out of bounds (Gloas)
BuilderPaymentIndexOutOfBounds(usize),
/// The parent execution requests root doesn't match the committed bid
ExecutionRequestsRootMismatch {
expected: Hash256,
found: Hash256,
},
/// Parent was not full but non-empty execution requests were provided
NonEmptyParentExecutionRequests,
}
impl From<BeaconStateError> for BlockProcessingError {

View File

@@ -1014,7 +1014,7 @@ async fn block_replayer_peeking_state_roots() {
let block_replayer = BlockReplayer::new(parent_state, &harness.chain.spec)
.state_root_iter(state_root_iter.into_iter())
.no_signature_verification()
.apply_blocks(vec![target_block], vec![], None)
.apply_blocks(vec![target_block], None)
.unwrap();
assert_eq!(

View File

@@ -9,8 +9,8 @@ use safe_arith::{SafeArith, SafeArithIter};
use tree_hash::TreeHash;
use types::{
AbstractExecPayload, BeaconState, BeaconStateError, ChainSpec, EthSpec, ExecPayload,
ExpectedWithdrawals, ExpectedWithdrawalsCapella, ExpectedWithdrawalsElectra,
ExpectedWithdrawalsGloas, Validator, Withdrawal, Withdrawals,
ExecutionBlockHash, ExpectedWithdrawals, ExpectedWithdrawalsCapella,
ExpectedWithdrawalsElectra, ExpectedWithdrawalsGloas, Validator, Withdrawal, Withdrawals,
};
/// Compute the next batch of withdrawals which should be included in a block.
@@ -494,7 +494,11 @@ pub mod gloas {
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
if !state.is_parent_block_full() {
// Return early if the parent block is empty.
let is_genesis_block = *state.latest_block_hash()? == ExecutionBlockHash::default();
let is_parent_block_empty =
*state.latest_block_hash()? != state.latest_execution_payload_bid()?.block_hash;
if is_genesis_block || is_parent_block_empty {
return Ok(());
}

View File

@@ -7,10 +7,12 @@ use ssz_types::BitVector;
use ssz_types::FixedVector;
use std::collections::HashSet;
use std::mem;
use tree_hash::TreeHash;
use typenum::Unsigned;
use types::{
BeaconState, BeaconStateError as Error, BeaconStateGloas, BuilderPendingPayment, ChainSpec,
DepositData, EthSpec, ExecutionPayloadBid, Fork, is_builder_withdrawal_credential,
DepositData, EthSpec, ExecutionPayloadBid, ExecutionRequests, Fork,
is_builder_withdrawal_credential,
};
/// Transform a `Fulu` state into a `Gloas` state.
@@ -78,6 +80,7 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
// Execution Bid
latest_execution_payload_bid: ExecutionPayloadBid {
block_hash: pre.latest_execution_payload_header.block_hash,
execution_requests_root: ExecutionRequests::<E>::default().tree_hash_root(),
..Default::default()
},
// Capella