From a70a120d55421a885590754004e114b7e514c007 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 1 Jun 2026 06:34:05 +0200 Subject: [PATCH] Fix infinite retry loop on blob/column processing failure in lookup sync The data (blob/column) request was rebuilt with a fresh `SingleLookupRequestState` (failed_processing = 0) after every processing failure, so `make_request`'s `failed_attempts() >= MAX_ATTEMPTS` bound never accumulated and the lookup re-downloaded/re-processed a permanently-invalid sidecar forever (observed as an OOM/hang under real crypto in `crypto_on_fail_with_bad_blob_*`). Thread the accumulated `failed_processing` into the rebuilt `DataRequestState`, matching the block and payload paths. Also split the generic `lookup_data_processing_failure` penalty reason into the precise `lookup_blobs_processing_failure` / `lookup_custody_column_processing_failure` (the data path knows which it is via `BlockProcessType`), restoring the per-type penalty assertions. Verified under the CI command (real crypto): FORK_NAME=electra ... crypto_on_fail_with_bad_blob_* -> pass FORK_NAME=fulu ... crypto_on_fail_with_bad_column_* -> pass --- .../network_beacon_processor/sync_methods.rs | 10 ++++----- .../sync/block_lookups/single_block_lookup.rs | 21 +++++++++++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/beacon_node/network/src/network_beacon_processor/sync_methods.rs b/beacon_node/network/src/network_beacon_processor/sync_methods.rs index a9058a5cb8..0c3006b713 100644 --- a/beacon_node/network/src/network_beacon_processor/sync_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/sync_methods.rs @@ -1110,15 +1110,14 @@ fn classify_processing_result( return no_penalty("execution_payload"); } BlockError::ParentUnknown { .. } => return no_penalty("parent_unknown"), - // Bad-column attribution: only meaningful for the data path, but classify uniformly — - // block-side processing won't produce this variant. + // Bad-column attribution: penalize the custody peer that served the invalid column. BlockError::AvailabilityCheck(AvailabilityCheckError::InvalidColumn((Some(idx), _))) => { return BlockProcessingResult::Error { penalty: Some(( PeerAction::MidToleranceError, WhichPeerToPenalize::CustodyPeerForColumn(*idx), )), - reason: "lookup_data_processing_failure", + reason: "lookup_custody_column_processing_failure", }; } _ => {} @@ -1127,9 +1126,8 @@ fn classify_processing_result( // Attributable to the block peer (which is also the data peer pre-Gloas). let reason = match process_type { BlockProcessType::SingleBlock { .. } => "lookup_block_processing_failure", - BlockProcessType::SingleBlob { .. } | BlockProcessType::SingleCustodyColumn(_) => { - "lookup_data_processing_failure" - } + BlockProcessType::SingleBlob { .. } => "lookup_blobs_processing_failure", + BlockProcessType::SingleCustodyColumn(_) => "lookup_custody_column_processing_failure", // Payload envelopes flow through classify_envelope_result; this branch shouldn't fire, // but produce a sensible reason in case it ever does. BlockProcessType::SinglePayloadEnvelope(_) => "lookup_envelope_processing_failure", 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 6765601863..090b7f0ddc 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 @@ -475,7 +475,13 @@ impl PayloadRequestState { } impl DataRequestState { - fn new(slot: Slot, block_root: Hash256, expected_blobs: usize, spec: &ChainSpec) -> Self { + fn new( + slot: Slot, + block_root: Hash256, + expected_blobs: usize, + failed_processing: u8, + spec: &ChainSpec, + ) -> Self { let block_fork = spec.fork_name_at_slot::(slot); match block_fork { @@ -487,7 +493,9 @@ impl DataRequestState { Self::Downloading(DataDownload::Blobs { block_root, expected_blobs, - state: SingleLookupRequestState::new(), + state: SingleLookupRequestState::new_with_processing_failures( + failed_processing, + ), }) } else { Self::Complete @@ -497,7 +505,9 @@ impl DataRequestState { if expected_blobs > 0 { Self::Downloading(DataDownload::Columns { block_root, - state: SingleLookupRequestState::new(), + state: SingleLookupRequestState::new_with_processing_failures( + failed_processing, + ), }) } else { Self::Complete @@ -507,7 +517,9 @@ impl DataRequestState { if expected_blobs > 0 { Self::Downloading(DataDownload::Columns { block_root, - state: SingleLookupRequestState::new(), + state: SingleLookupRequestState::new_with_processing_failures( + failed_processing, + ), }) // Gloas: data peers start at 0, populated when children arrive } else { @@ -811,6 +823,7 @@ impl SingleBlockLookup { block.slot(), self.block_root, block.num_expected_blobs(), + self.failed_processing, cx.spec(), ), });