From 2f525c687eb46a45fd3b17d09c94180b5e7c9e5c Mon Sep 17 00:00:00 2001 From: Pablo Deymonnaz Date: Tue, 17 Mar 2026 12:47:15 -0300 Subject: [PATCH] Cap ancestor chain walk depth to prevent infinite block re-processing loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When fallback pruning deletes states for already-processed blocks (STATES_TO_KEEP=900 is much smaller than BLOCKS_TO_KEEP=21600), the pending block chain walk can reach protected checkpoints (justified/finalized) whose states survive pruning. This triggers a massive cascade that re-processes hundreds of old blocks whose states are immediately re-pruned — creating an infinite loop. Limit the walk to MAX_ANCESTOR_WALK_SLOTS (512) slots behind the current head. Since 512 < STATES_TO_KEEP (900), any cascade stays within the state retention window and states won't be immediately pruned. Blocks beyond the limit fall through to a network request instead. Observed in devnet4 where all three ethlambda nodes entered this loop at slot ~15276, generating ~3.5GB of logs each while finalization was stalled. --- crates/blockchain/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/blockchain/src/lib.rs b/crates/blockchain/src/lib.rs index 872e6aa..dd84b05 100644 --- a/crates/blockchain/src/lib.rs +++ b/crates/blockchain/src/lib.rs @@ -314,6 +314,12 @@ impl BlockChainServer { let parent_root = signed_block.message.block.parent_root; let proposer = signed_block.message.block.proposer_index; + // Never process blocks at or below the finalized slot — they are + // already part of the canonical chain and cannot affect fork choice. + if slot <= self.store.latest_finalized().slot { + return; + } + // Check if parent state exists before attempting to process if !self.store.has_state(&parent_root) { info!(%slot, %parent_root, %block_root, "Block parent missing, storing as pending");