From 180aee1fdc6ad07afa258874ed15ff6de94002c4 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Mon, 15 Jun 2026 13:21:01 +0300 Subject: [PATCH] Allow for reimporting block+envelope in certain cases --- beacon_node/beacon_chain/src/beacon_chain.rs | 14 +++++- .../beacon_chain/src/block_verification.rs | 23 ++++----- .../src/block_verification_types.rs | 11 ++++- .../payload_envelope_verification/import.rs | 8 ---- .../src/payload_envelope_verification/mod.rs | 48 ++++++++++++++++--- .../gossip_methods.rs | 1 - 6 files changed, 77 insertions(+), 28 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index a7f1a7cfcd..9de17c9ed9 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -64,6 +64,7 @@ use crate::payload_attestation_verification::VerifiedPayloadAttestationMessage; use crate::payload_bid_verification::payload_bid_cache::GossipVerifiedPayloadBidCache; #[cfg(not(test))] use crate::payload_envelope_streamer::{EnvelopeRequestSource, launch_payload_envelope_stream}; +use crate::payload_envelope_verification::check_envelope_relevancy; use crate::pending_payload_cache::PendingPayloadCache; use crate::pending_payload_cache::{ Availability as PayloadAvailability, @@ -3000,7 +3001,18 @@ impl BeaconChain { } } - match check_block_relevancy(block.as_block(), block_root, self) { + let is_envelope_relevant = if let Some(envelope) = block.as_envelope() { + // If the envelope is relevant we skip the duplicate import check in + // `check_block_relevancy` + match check_envelope_relevancy(block.as_block(), envelope, self) { + Ok(_) => true, + Err(_) => false, + } + } else { + false + }; + + match check_block_relevancy(block.as_block(), block_root, is_envelope_relevant, self) { // If the block is relevant, add it to the filtered chain segment. Ok(_) => filtered_chain_segment.push((block_root, block)), // If the block is already known, simply ignore this block. diff --git a/beacon_node/beacon_chain/src/block_verification.rs b/beacon_node/beacon_chain/src/block_verification.rs index 6b1ac3b033..4caa934d83 100644 --- a/beacon_node/beacon_chain/src/block_verification.rs +++ b/beacon_node/beacon_chain/src/block_verification.rs @@ -97,11 +97,10 @@ use store::{Error as DBError, KeyValueStore}; use strum::{AsRefStr, IntoStaticStr}; use task_executor::JoinHandle; use tracing::{Instrument, Span, debug, debug_span, error, info_span, instrument}; -use types::ExecutionBlockHash; use types::{ BeaconBlockRef, BeaconState, BeaconStateError, BlobsList, ChainSpec, DataColumnSidecarList, - Epoch, EthSpec, FullPayload, Hash256, InconsistentFork, KzgProofs, RelativeEpoch, - SignedBeaconBlock, SignedBeaconBlockHeader, Slot, data::DataColumnSidecarError, + Epoch, EthSpec, ExecutionBlockHash, FullPayload, Hash256, InconsistentFork, KzgProofs, + RelativeEpoch, SignedBeaconBlock, SignedBeaconBlockHeader, Slot, data::DataColumnSidecarError, }; /// Maximum block slot number. Block with slots bigger than this constant will NOT be processed. @@ -1350,7 +1349,7 @@ impl IntoExecutionPendingBlock for RangeSyncBlock Result, BlockSlashInfo> { // Perform an early check to prevent wasting time on irrelevant blocks. let header = self.signed_block_header(); - let block_root = check_block_relevancy(self.as_block(), block_root, chain) + let block_root = check_block_relevancy(self.as_block(), block_root, false, chain) .map_err(|e| BlockSlashInfo::SignatureNotChecked(header.clone(), e))?; let (available_block, _envelope) = self.into_available_block().map_err(|e| { @@ -1394,7 +1393,7 @@ impl IntoExecutionPendingBlock for LookupBlock Result, BlockSlashInfo> { // Perform an early check to prevent wasting time on irrelevant blocks. - let block_root = check_block_relevancy(self.as_block(), block_root, chain) + let block_root = check_block_relevancy(self.as_block(), block_root, false, chain) .map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?; let maybe_available_block = MaybeAvailableBlock::AvailabilityPending { @@ -1467,7 +1466,7 @@ impl ExecutionPendingBlock { /* * Perform cursory checks to see if the block is even worth processing. */ - check_block_relevancy(block.as_block(), block_root, chain)?; + check_block_relevancy(block.as_block(), block_root, false, chain)?; // Define a future that will verify the execution payload with an execution engine. // @@ -1851,6 +1850,7 @@ pub fn check_block_is_finalized_checkpoint_or_descendant< pub fn check_block_relevancy( signed_block: &SignedBeaconBlock, block_root: Hash256, + skip_import_check: bool, chain: &BeaconChain, ) -> Result { let block = signed_block.message(); @@ -1880,11 +1880,12 @@ pub fn check_block_relevancy( check_block_against_finalized_slot(block, block_root, chain)?; // Check if the block is already known. We know it is post-finalization, so it is - // sufficient to check the fork choice. - if chain - .canonical_head - .fork_choice_read_lock() - .contains_block(&block_root) + // sufficient to check the fork choice. This check can optionally be skipped. + if skip_import_check + && chain + .canonical_head + .fork_choice_read_lock() + .contains_block(&block_root) { return Err(BlockError::DuplicateFullyImported(block_root)); } diff --git a/beacon_node/beacon_chain/src/block_verification_types.rs b/beacon_node/beacon_chain/src/block_verification_types.rs index 18e95f58f3..8abed19814 100644 --- a/beacon_node/beacon_chain/src/block_verification_types.rs +++ b/beacon_node/beacon_chain/src/block_verification_types.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use types::data::BlobIdentifier; use types::{ BeaconBlockRef, BeaconState, BlindedPayload, ChainSpec, Epoch, EthSpec, Hash256, - SignedBeaconBlock, SignedBeaconBlockHeader, Slot, + SignedBeaconBlock, SignedBeaconBlockHeader, SignedExecutionPayloadEnvelope, Slot, }; /// A wrapper around a `SignedBeaconBlock`. This varaint is constructed @@ -111,6 +111,15 @@ impl RangeSyncBlock { .filter(|columns| !columns.is_empty()), } } + + pub fn as_envelope(&self) -> Option<&SignedExecutionPayloadEnvelope> { + match self { + RangeSyncBlock::Base(_) => None, + RangeSyncBlock::Gloas { envelope, .. } => { + envelope.as_ref().map(|e| e.envelope().as_ref()) + } + } + } } impl RangeSyncBlock { diff --git a/beacon_node/beacon_chain/src/payload_envelope_verification/import.rs b/beacon_node/beacon_chain/src/payload_envelope_verification/import.rs index 00806f0e17..5e90667a16 100644 --- a/beacon_node/beacon_chain/src/payload_envelope_verification/import.rs +++ b/beacon_node/beacon_chain/src/payload_envelope_verification/import.rs @@ -173,14 +173,6 @@ impl BeaconChain { .map_err(BeaconChainError::TokioJoin)? .ok_or(BeaconChainError::RuntimeShutdown)??; - // TODO(gloas): optimistic sync is not supported for Gloas, maybe we could re-add it - if payload_verification_outcome - .payload_verification_status - .is_optimistic() - { - return Err(EnvelopeError::OptimisticSyncNotSupported { block_root }); - } - Ok(AvailabilityPendingExecutedEnvelope::new( signed_envelope, block_root, diff --git a/beacon_node/beacon_chain/src/payload_envelope_verification/mod.rs b/beacon_node/beacon_chain/src/payload_envelope_verification/mod.rs index a0d34949c6..6f24744e2c 100644 --- a/beacon_node/beacon_chain/src/payload_envelope_verification/mod.rs +++ b/beacon_node/beacon_chain/src/payload_envelope_verification/mod.rs @@ -25,12 +25,13 @@ use strum::AsRefStr; use tracing::instrument; use types::{ BeaconState, BeaconStateError, DataColumnSidecarList, EthSpec, ExecutionBlockHash, - ExecutionPayloadEnvelope, Hash256, SignedExecutionPayloadEnvelope, Slot, + ExecutionPayloadEnvelope, Hash256, SignedBeaconBlock, SignedExecutionPayloadEnvelope, Slot, }; use crate::{ - BeaconChainError, BeaconChainTypes, BeaconStore, BlockError, ExecutionPayloadError, - PayloadVerificationError, PayloadVerificationOutcome, + BeaconChain, BeaconChainError, BeaconChainTypes, BeaconStore, BlockError, + ExecutionPayloadError, PayloadVerificationError, PayloadVerificationOutcome, + payload_envelope_verification::gossip_verified_envelope::verify_envelope_consistency, }; pub mod execution_pending_envelope; @@ -166,8 +167,6 @@ pub enum EnvelopeError { EnvelopeProcessingError(EnvelopeProcessingError), /// Error verifying the execution payload ExecutionPayloadError(ExecutionPayloadError), - /// Optimistic sync is not supported for Gloas payload envelopes. - OptimisticSyncNotSupported { block_root: Hash256 }, /// The envelope's beacon block was not present in fork choice at import time. /// /// Unlike [`EnvelopeError::BlockRootUnknown`] (raised during gossip verification, where the @@ -199,7 +198,6 @@ impl EnvelopeError { | EnvelopeError::PriorToFinalization { .. } | EnvelopeError::BeaconChainError(_) | EnvelopeError::BeaconStateError(_) - | EnvelopeError::OptimisticSyncNotSupported { .. } | EnvelopeError::BlockRootNotInForkChoice(_) | EnvelopeError::InternalError(_) => false, } @@ -292,3 +290,41 @@ pub(crate) fn load_snapshot_from_state_root( beacon_block_root, }) } + +/// Performs simple, cheap checks to ensure that the envelope is relevant to be imported. +/// +/// `Ok(block_root` is returned if the envelope passes these checks and should progress with +/// verification. +/// +/// Returns an error if the envelope is not relevant or if an error occurs during a verification step. +pub fn check_envelope_relevancy( + block: &SignedBeaconBlock, + signed_envelope: &SignedExecutionPayloadEnvelope, + chain: &BeaconChain, +) -> Result { + let envelope = &signed_envelope.message; + let Ok(bid) = block.message().body().signed_execution_payload_bid() else { + return Err(EnvelopeError::InternalError( + "Block is pre-gloas".to_string(), + )); + }; + + let latest_finalized_slot = chain + .canonical_head + .cached_head() + .finalized_checkpoint() + .epoch + .start_slot(T::EthSpec::slots_per_epoch()); + + verify_envelope_consistency(envelope, block, &bid.message, latest_finalized_slot)?; + + if chain + .canonical_head + .fork_choice_read_lock() + .is_payload_received(&envelope.beacon_block_root) + { + return Ok(false); + } + + Ok(true) +} diff --git a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs index 98c143eaeb..8b9db8f04c 100644 --- a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs @@ -3715,7 +3715,6 @@ impl NetworkBeaconProcessor { | EnvelopeError::BeaconStateError(_) // The following variants are produced during envelope import, not gossip // verification, so they cannot be reached here. Ignore them to be safe. - | EnvelopeError::OptimisticSyncNotSupported { .. } | EnvelopeError::BlockRootNotInForkChoice(_) | EnvelopeError::InternalError(_) => { self.propagate_validation_result(