From 2749e18d0e35e6f148642623327acac5a7066658 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Thu, 9 Apr 2026 03:44:19 +0900 Subject: [PATCH] Gloas serve post block state for finalized/justified state requests (#9092) Co-Authored-By: Eitan Seri- Levi Co-Authored-By: Pawan Dhananjay --- beacon_node/http_api/src/block_id.rs | 10 +++--- beacon_node/http_api/src/state_id.rs | 51 ++++++++++++++++++++++------ common/eth2/src/types.rs | 8 +++++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/beacon_node/http_api/src/block_id.rs b/beacon_node/http_api/src/block_id.rs index e6b1ed0879..f4645f1304 100644 --- a/beacon_node/http_api/src/block_id.rs +++ b/beacon_node/http_api/src/block_id.rs @@ -1,5 +1,5 @@ use crate::version::inconsistent_fork_rejection; -use crate::{ExecutionOptimistic, state_id::checkpoint_slot_and_execution_optimistic}; +use crate::{ExecutionOptimistic, state_id::checkpoint_block_and_execution_optimistic}; use beacon_chain::kzg_utils::reconstruct_blobs; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped}; use eth2::beacon_response::{ExecutionOptimisticFinalizedMetadata, UnversionedResponse}; @@ -60,15 +60,15 @@ impl BlockId { CoreBlockId::Finalized => { let finalized_checkpoint = chain.canonical_head.cached_head().finalized_checkpoint(); - let (_slot, execution_optimistic) = - checkpoint_slot_and_execution_optimistic(chain, finalized_checkpoint)?; + let (_block, execution_optimistic) = + checkpoint_block_and_execution_optimistic(chain, finalized_checkpoint)?; Ok((finalized_checkpoint.root, execution_optimistic, true)) } CoreBlockId::Justified => { let justified_checkpoint = chain.canonical_head.cached_head().justified_checkpoint(); - let (_slot, execution_optimistic) = - checkpoint_slot_and_execution_optimistic(chain, justified_checkpoint)?; + let (_block, execution_optimistic) = + checkpoint_block_and_execution_optimistic(chain, justified_checkpoint)?; Ok((justified_checkpoint.root, execution_optimistic, false)) } CoreBlockId::Slot(slot) => { diff --git a/beacon_node/http_api/src/state_id.rs b/beacon_node/http_api/src/state_id.rs index 13fb9b2c58..ce18388926 100644 --- a/beacon_node/http_api/src/state_id.rs +++ b/beacon_node/http_api/src/state_id.rs @@ -2,6 +2,7 @@ use crate::ExecutionOptimistic; use crate::metrics; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes}; use eth2::types::StateId as CoreStateId; +use proto_array::Block; use std::fmt; use std::str::FromStr; use types::{BeaconState, Checkpoint, EthSpec, Fork, Hash256, Slot}; @@ -19,6 +20,8 @@ impl StateId { Self(CoreStateId::Slot(slot)) } + // TODO(gloas) add tests for finalized and justified checkpoint states to ensure + // we return the post block state for gloas /// Return the state root identified by `self`. pub fn root( &self, @@ -41,15 +44,41 @@ impl StateId { CoreStateId::Finalized => { let finalized_checkpoint = chain.canonical_head.cached_head().finalized_checkpoint(); - let (slot, execution_optimistic) = - checkpoint_slot_and_execution_optimistic(chain, finalized_checkpoint)?; + + let slot = finalized_checkpoint + .epoch + .start_slot(T::EthSpec::slots_per_epoch()); + let (block, execution_optimistic) = + checkpoint_block_and_execution_optimistic(chain, finalized_checkpoint)?; + + if chain + .spec + .fork_name_at_slot::(block.slot) + .gloas_enabled() + { + return Ok((block.state_root, execution_optimistic, true)); + } + (slot, execution_optimistic, true) } CoreStateId::Justified => { let justified_checkpoint = chain.canonical_head.cached_head().justified_checkpoint(); - let (slot, execution_optimistic) = - checkpoint_slot_and_execution_optimistic(chain, justified_checkpoint)?; + + let slot = justified_checkpoint + .epoch + .start_slot(T::EthSpec::slots_per_epoch()); + let (block, execution_optimistic) = + checkpoint_block_and_execution_optimistic(chain, justified_checkpoint)?; + + if chain + .spec + .fork_name_at_slot::(block.slot) + .gloas_enabled() + { + return Ok((block.state_root, execution_optimistic, false)); + } + (slot, execution_optimistic, false) } CoreStateId::Slot(slot) => ( @@ -254,13 +283,11 @@ impl fmt::Display for StateId { } } -/// Returns the first slot of the checkpoint's `epoch` and the execution status of the checkpoint's -/// `root`. -pub fn checkpoint_slot_and_execution_optimistic( +/// Returns checkpoint block and the execution status of the checkpoint's `root`. +pub fn checkpoint_block_and_execution_optimistic( chain: &BeaconChain, checkpoint: Checkpoint, -) -> Result<(Slot, ExecutionOptimistic), warp::reject::Rejection> { - let slot = checkpoint.epoch.start_slot(T::EthSpec::slots_per_epoch()); +) -> Result<(Block, ExecutionOptimistic), warp::reject::Rejection> { let fork_choice = chain.canonical_head.fork_choice_read_lock(); let finalized_checkpoint = fork_choice.cached_fork_choice_view().finalized_checkpoint; @@ -277,5 +304,9 @@ pub fn checkpoint_slot_and_execution_optimistic( .map_err(BeaconChainError::ForkChoiceError) .map_err(warp_utils::reject::unhandled_error)?; - Ok((slot, execution_optimistic)) + let block = fork_choice.get_block(&checkpoint.root).ok_or_else(|| { + warp_utils::reject::custom_not_found(format!("Block {:?} not found", checkpoint.root)) + })?; + + Ok((block, execution_optimistic)) } diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 54e9c98b5b..e85565c580 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -125,7 +125,15 @@ impl fmt::Display for BlockId { pub enum StateId { Head, Genesis, + /// Pre-gloas the finalized state is the checkpoint block state + /// advanced to the epoch boundary. + /// Post-gloas this state is always the checkpoint post-block state and is not advanced + /// to the epoch boundary. Finalized, + /// Pre-gloas the justified state is the checkpoint block state + /// advanced to the epoch boundary. + /// Post-gloas this state is always the checkpoint post-block state and is not advanced + /// to the epoch boundary. Justified, Slot(Slot), Root(Hash256),