Update database and block replayer to handle payload envelopes (#8886)

Closes:

- https://github.com/sigp/lighthouse/issues/8869


  - Update `BlockReplayer` to support replay of execution payload envelopes.
- Update `HotColdDB` to load payload envelopes and feed them to the `BlockReplayer` for both hot + cold states. However the cold DB code is not fully working yet (see: https://github.com/sigp/lighthouse/issues/8958).
- Add `StatePayloadStatus` to allow callers to specify whether they want a state with a payload applied, or not.
- Fix the state cache to key by `StatePayloadStatus`.
- Lots of fixes to block production and block processing regarding state management.
- Initial test harness support for producing+processing Gloas blocks+envelopes
- A few new tests to cover Gloas DB operations


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

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>

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

Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com>

Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
Michael Sproul
2026-03-12 10:06:25 +11:00
committed by GitHub
parent 6350a27031
commit bff72a920d
30 changed files with 1243 additions and 84 deletions

View File

@@ -14,6 +14,7 @@ use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use crate::{
ExecutionBlockHash,
block::{
BLOB_KZG_COMMITMENTS_INDEX, BeaconBlock, BeaconBlockAltair, BeaconBlockBase,
BeaconBlockBellatrix, BeaconBlockBodyBellatrix, BeaconBlockBodyCapella,
@@ -365,6 +366,32 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
format_kzg_commitments(commitments.as_ref())
}
/// Convenience accessor for the block's bid's `block_hash`.
///
/// This method returns an error prior to Gloas.
pub fn payload_bid_block_hash(&self) -> Result<ExecutionBlockHash, BeaconStateError> {
self.message()
.body()
.signed_execution_payload_bid()
.map(|bid| bid.message.block_hash)
}
/// Check if the `parent_hash` in this block's `signed_payload_bid` matches `parent_block_hash`.
///
/// This function is useful post-Gloas for determining if the parent block is full, *without*
/// necessarily needing access to a beacon state. The passed in `parent_block_hash` MUST be the
/// `block_hash` from the parent beacon block's bid. If the parent beacon state is available
/// this can alternatively be fetched from `state.latest_payload_bid`.
///
/// This function returns `false` for all blocks prior to Gloas.
pub fn is_parent_block_full(&self, parent_block_hash: ExecutionBlockHash) -> bool {
let Ok(signed_payload_bid) = self.message().body().signed_execution_payload_bid() else {
// Prior to Gloas.
return false;
};
signed_payload_bid.message.parent_block_hash == parent_block_hash
}
}
// We can convert pre-Bellatrix blocks without payloads into blocks with payloads.

View File

@@ -12,6 +12,7 @@ mod payload;
mod signed_bls_to_execution_change;
mod signed_execution_payload_bid;
mod signed_execution_payload_envelope;
mod state_payload_status;
pub use bls_to_execution_change::BlsToExecutionChange;
pub use eth1_data::Eth1Data;
@@ -41,3 +42,4 @@ pub use payload::{
pub use signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use signed_execution_payload_bid::SignedExecutionPayloadBid;
pub use signed_execution_payload_envelope::SignedExecutionPayloadEnvelope;
pub use state_payload_status::StatePayloadStatus;

View File

@@ -0,0 +1,18 @@
use serde::{Deserialize, Serialize};
/// Payload status as it applies to a `BeaconState` post-Gloas.
///
/// A state can either be a post-state for a block (in which case we call it `Pending`) or a
/// payload envelope (`Full`). When handling states it is often necessary to know which of these
/// two variants is required.
///
/// Note that states at skipped slots could be either `Pending` or `Full`, depending on whether
/// the payload for the most-recently applied block was also applied.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum StatePayloadStatus {
/// For states produced by `process_block` executed on a `BeaconBlock`.
Pending,
/// For states produced by `process_execution_payload` on a `ExecutionPayloadEnvelope`.
Full,
}

View File

@@ -36,7 +36,7 @@ use crate::{
execution::{
Eth1Data, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella,
ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut,
ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, StatePayloadStatus,
},
fork::{Fork, ForkName, ForkVersionDecode, InconsistentFork, map_fork_name},
light_client::consts::{
@@ -1266,6 +1266,24 @@ impl<E: EthSpec> BeaconState<E> {
}
}
/// Determine the payload status of this state.
///
/// Prior to Gloas this is always `Pending`.
///
/// Post-Gloas, the definition of the `StatePayloadStatus` is:
///
/// - `Full` if this state is the result of envelope processing.
/// - `Pending` if this state is the result of block processing.
pub fn payload_status(&self) -> StatePayloadStatus {
if !self.fork_name_unchecked().gloas_enabled() {
StatePayloadStatus::Pending
} else if self.is_parent_block_full() {
StatePayloadStatus::Full
} else {
StatePayloadStatus::Pending
}
}
/// Return `true` if the validator who produced `slot_signature` is eligible to aggregate.
///
/// Spec v0.12.1