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 1845866e4a..f6cd7143a8 100644 --- a/beacon_node/beacon_chain/src/payload_envelope_verification/import.rs +++ b/beacon_node/beacon_chain/src/payload_envelope_verification/import.rs @@ -103,14 +103,18 @@ impl BeaconChain { ExecutedEnvelope::AvailabilityPending { signed_envelope, import_data, - payload_verification_outcome, + payload_verification_outcome: _, } => { - self.import_pending_execution_payload_envelope( - signed_envelope, - import_data, - payload_verification_outcome, - ) - .await + // The envelope has been executed but data columns have not yet arrived. + // Do not import — return MissingComponents so callers know to fetch columns. + // TODO(gloas): once an envelope DA checker exists, cache the envelope here + // (analogous to `data_availability_checker.put_executed_block`) so that + // import is driven automatically when columns arrive. + let slot = signed_envelope.slot(); + let block_root = import_data.block_root; + Ok(AvailabilityProcessingStatus::MissingComponents( + slot, block_root, + )) } } }; 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 1fd5195dd3..b0a6e51704 100644 --- a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs @@ -4007,8 +4007,11 @@ impl NetworkBeaconProcessor { block_root: *block_root, }); } - Ok(AvailabilityProcessingStatus::MissingComponents(_, _)) => { - // Nothing to do + Ok(AvailabilityProcessingStatus::MissingComponents(_slot, _block_root)) => { + // TODO(gloas): wire this into the envelope DA checker once it exists, analogous to + // how `process_availability` drives block import once blobs/columns arrive. Until + // then gossip envelopes with missing columns will be stuck until columns arrive via + // gossip or engineGetBlobs. } Err(e) => match e { EnvelopeError::ExecutionPayloadError(epe) if !epe.penalize_peer() => {} diff --git a/beacon_node/network/src/sync/block_lookups/mod.rs b/beacon_node/network/src/sync/block_lookups/mod.rs index 32fcefc501..bb003dc222 100644 --- a/beacon_node/network/src/sync/block_lookups/mod.rs +++ b/beacon_node/network/src/sync/block_lookups/mod.rs @@ -54,6 +54,7 @@ use types::{EthSpec, SignedBeaconBlock}; pub mod common; pub mod parent_chain; mod single_block_lookup; +mod single_envelope_lookup; /// The maximum depth we will search for a parent block. In principle we should have sync'd any /// canonical chain to its head once the peer connects. A chain should not appear where it's depth @@ -645,6 +646,31 @@ impl BlockLookups { self.on_processing_result_inner::>(id, result, cx) } BlockProcessType::SinglePayloadEnvelope { id, block_root } => { + // When envelope processing returns `MissingComponents`, the envelope has been + // executed but data columns are not yet available. Transition the lookup to fetch + // custody columns instead of retrying the envelope or erroring. + if matches!( + &result, + BlockProcessingResult::Ok( + AvailabilityProcessingStatus::MissingComponents { .. } + ) + ) && let Some(lookup) = self.single_block_lookups.get_mut(&id) + && lookup.transition_envelope_to_custody() + { + debug!( + ?block_root, + "Envelope processed, transitioning to custody column lookup" + ); + let lookup_result = lookup.continue_requests(cx); + self.on_lookup_result( + id, + lookup_result, + "envelope_to_custody_transition", + cx, + ); + return; + } + let result = self .on_processing_result_inner::>(id, result, cx); // On successful envelope import, unblock child lookups waiting for this envelope diff --git a/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs b/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs index 3277ad9687..cdcb574219 100644 --- a/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs +++ b/beacon_node/network/src/sync/block_lookups/single_block_lookup.rs @@ -78,8 +78,8 @@ pub struct SingleBlockLookup { /// than the lifetime of a custody request. #[educe(Debug(method(fmt_peer_set_as_len)))] peers: Arc>>, - block_root: Hash256, - awaiting_parent: Option, + pub(super) block_root: Hash256, + pub(super) awaiting_parent: Option, created: Instant, pub(crate) span: Span, } @@ -120,21 +120,6 @@ impl SingleBlockLookup { } } - /// Create an envelope-only lookup. The block is already imported, we just need the envelope. - pub fn new_envelope_only(block_root: Hash256, peers: &[PeerId], id: Id) -> Self { - let mut lookup = Self::new(block_root, peers, id, None); - // Block is already imported, mark as completed - lookup - .block_request_state - .state - .on_completed_request("block already imported") - .expect("block state starts as AwaitingDownload"); - lookup.component_requests = - ComponentRequests::ActiveEnvelopeRequest(EnvelopeRequestState::new(block_root)); - lookup - } - - /// Reset the status of all internal requests pub fn reset_requests(&mut self) { self.block_request_state = BlockRequestState::new(self.block_root); match &self.component_requests { @@ -174,24 +159,11 @@ impl SingleBlockLookup { } } - /// Returns the parent root if awaiting a parent envelope. - pub fn awaiting_parent_envelope(&self) -> Option { - match self.awaiting_parent { - Some(AwaitingParent::Envelope(root)) => Some(root), - _ => None, - } - } - /// Mark this lookup as awaiting a parent block to be imported before processing. pub fn set_awaiting_parent(&mut self, parent_root: Hash256) { self.awaiting_parent = Some(AwaitingParent::Block(parent_root)); } - /// Mark this lookup as awaiting a parent envelope to be imported before processing. - pub fn set_awaiting_parent_envelope(&mut self, parent_root: Hash256) { - self.awaiting_parent = Some(AwaitingParent::Envelope(parent_root)); - } - /// Mark this lookup as no longer awaiting any parent. pub fn resolve_awaiting_parent(&mut self) { self.awaiting_parent = None; diff --git a/beacon_node/network/src/sync/block_lookups/single_envelope_lookup.rs b/beacon_node/network/src/sync/block_lookups/single_envelope_lookup.rs new file mode 100644 index 0000000000..88fa042439 --- /dev/null +++ b/beacon_node/network/src/sync/block_lookups/single_envelope_lookup.rs @@ -0,0 +1,62 @@ +//! Envelope-specific extensions to `SingleBlockLookup`. +//! +//! Envelope-only lookups are created when a block's parent is known and imported but its +//! execution payload envelope has not yet been received. The block download step is skipped +//! (marked complete immediately), and only the envelope — and possibly subsequent custody +//! columns — are fetched. + +use super::single_block_lookup::{ + AwaitingParent, ComponentRequests, CustodyRequestState, EnvelopeRequestState, SingleBlockLookup, +}; +use beacon_chain::BeaconChainTypes; +use lighthouse_network::PeerId; +use lighthouse_network::service::api_types::Id; +use store::Hash256; + +impl SingleBlockLookup { + /// Create an envelope-only lookup. The block is already imported; only the envelope (and + /// potentially custody columns) need to be fetched. + pub fn new_envelope_only(block_root: Hash256, peers: &[PeerId], id: Id) -> Self { + let mut lookup = Self::new(block_root, peers, id, None); + // Block is already imported — advance past the download step immediately. + lookup + .block_request_state + .state + .on_completed_request("block already imported") + .expect("block state starts as AwaitingDownload"); + lookup.component_requests = + ComponentRequests::ActiveEnvelopeRequest(EnvelopeRequestState::new(block_root)); + lookup + } + + /// Transition from `ActiveEnvelopeRequest` to `ActiveCustodyRequest`. + /// + /// Called when envelope processing returns `MissingComponents`: the envelope has been executed + /// but data columns have not yet arrived and must be fetched separately. + /// Returns `true` if the transition was made, `false` if state was not an envelope request. + pub fn transition_envelope_to_custody(&mut self) -> bool { + if matches!( + self.component_requests, + ComponentRequests::ActiveEnvelopeRequest(_) + ) { + self.component_requests = + ComponentRequests::ActiveCustodyRequest(CustodyRequestState::new(self.block_root)); + true + } else { + false + } + } + + /// Returns the parent root if this lookup is awaiting a parent envelope. + pub fn awaiting_parent_envelope(&self) -> Option { + match self.awaiting_parent { + Some(AwaitingParent::Envelope(root)) => Some(root), + _ => None, + } + } + + /// Mark this lookup as awaiting a parent envelope before processing can resume. + pub fn set_awaiting_parent_envelope(&mut self, parent_root: Hash256) { + self.awaiting_parent = Some(AwaitingParent::Envelope(parent_root)); + } +}