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

@@ -41,7 +41,7 @@ pub const BID_VALUE_SELF_BUILD: u64 = 0;
pub const EXECUTION_PAYMENT_TRUSTLESS_BUILD: u64 = 0;
type ConsensusBlockValue = u64;
type BlockProductionResult<E> = (BeaconBlock<E, FullPayload<E>>, ConsensusBlockValue);
type BlockProductionResult<E> = (BeaconBlock<E>, BeaconState<E>, ConsensusBlockValue);
pub type PreparePayloadResult<E> = Result<BlockProposalContentsGloas<E>, BlockProductionError>;
pub type PreparePayloadHandle<E> = JoinHandle<Option<PreparePayloadResult<E>>>;
@@ -425,6 +425,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
))
}
/// Complete a block by computing its state root, and
///
/// Return `(block, pending_state, block_value)` where:
///
/// - `pending_state` is the state post block application (prior to payload application)
/// - `block_value` is the consensus-layer rewards for `block`
#[allow(clippy::type_complexity)]
fn complete_partial_beacon_block_gloas(
&self,
@@ -433,7 +439,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
payload_data: Option<ExecutionPayloadData<T::EthSpec>>,
mut state: BeaconState<T::EthSpec>,
verification: ProduceBlockVerification,
) -> Result<(BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>, u64), BlockProductionError> {
) -> Result<BlockProductionResult<T::EthSpec>, BlockProductionError> {
let PartialBeaconBlock {
slot,
proposer_index,
@@ -545,6 +551,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
drop(state_root_timer);
// Clone the Pending state (post-block, pre-envelope) for callers that need it.
let pending_state = state.clone();
let (mut block, _) = signed_beacon_block.deconstruct();
*block.state_root_mut() = state_root;
@@ -605,7 +614,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
"Produced beacon block"
);
Ok((block, consensus_block_value))
Ok((block, pending_state, consensus_block_value))
}
// TODO(gloas) introduce `ProposerPreferences` so we can build out trustless

View File

@@ -3,7 +3,7 @@ use std::{sync::Arc, time::Duration};
use proto_array::ProposerHeadError;
use slot_clock::SlotClock;
use tracing::{debug, error, info, instrument, warn};
use types::{BeaconState, Hash256, Slot};
use types::{BeaconState, Hash256, Slot, StatePayloadStatus};
use crate::{
BeaconChain, BeaconChainTypes, BlockProductionError, StateSkipConfig,
@@ -37,8 +37,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
};
let (state, state_root_opt) = if head_slot < slot {
// Attempt an aggressive re-org if configured and the conditions are right.
if let Some((re_org_state, re_org_state_root)) =
self.get_state_for_re_org(slot, head_slot, head_block_root)
// TODO(gloas): re-enable reorgs
let gloas_enabled = self
.spec
.fork_name_at_slot::<T::EthSpec>(slot)
.gloas_enabled();
if !gloas_enabled
&& let Some((re_org_state, re_org_state_root)) =
self.get_state_for_re_org(slot, head_slot, head_block_root)
{
info!(
%slot,
@@ -49,9 +55,30 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} else {
// Fetch the head state advanced through to `slot`, which should be present in the
// state cache thanks to the state advance timer.
// TODO(gloas): need to fix this once fork choice understands payloads
// for now we just use the existence of the head's payload envelope to determine
// whether we should build atop it
let (payload_status, parent_state_root) = if gloas_enabled
&& let Ok(Some(envelope)) = self.store.get_payload_envelope(&head_block_root)
{
debug!(
%slot,
parent_state_root = ?envelope.message.state_root,
parent_block_root = ?head_block_root,
"Building Gloas block on full state"
);
(StatePayloadStatus::Full, envelope.message.state_root)
} else {
(StatePayloadStatus::Pending, head_state_root)
};
let (state_root, state) = self
.store
.get_advanced_hot_state(head_block_root, slot, head_state_root)
.get_advanced_hot_state(
head_block_root,
payload_status,
slot,
parent_state_root,
)
.map_err(BlockProductionError::FailedToLoadState)?
.ok_or(BlockProductionError::UnableToProduceAtSlot(slot))?;
(state, Some(state_root))
@@ -204,7 +231,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let (state_root, state) = self
.store
.get_advanced_hot_state_from_cache(re_org_parent_block, slot)
.get_advanced_hot_state_from_cache(
re_org_parent_block,
StatePayloadStatus::Pending,
slot,
)
.or_else(|| {
warn!(reason = "no state in cache", "Not attempting re-org");
None