From a936a670ad8ad9b2d05bf2a5c32fb891b28d195a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:32:05 -0300 Subject: [PATCH] fix(blockchain): reject blocks too far in future before pending storage Adds a parent-independent future-slot check in `BlockChainServer::process_or_pend_block` so blocks whose start interval is more than `GOSSIP_DISPARITY_INTERVALS` ahead of local time are dropped before the parent-state lookup. Mirrors the bound used by `validate_attestation_data`: an interval-based margin (not a whole-slot one), since a slot-wide tolerance would let an adversary pre-publish next-slot blocks ahead of any honest proposer. Without this check, a peer could flood pending pre-genesis-future blocks that would (a) get persisted to RocksDB as pending and (b) trigger BlocksByRoot fan-out for fabricated parent roots. --- crates/blockchain/src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/blockchain/src/lib.rs b/crates/blockchain/src/lib.rs index 5e47f2e..e0df9f5 100644 --- a/crates/blockchain/src/lib.rs +++ b/crates/blockchain/src/lib.rs @@ -420,6 +420,27 @@ impl BlockChainServer { return; } + // Reject blocks whose slot has not started locally, mirroring the + // attestation time check in `validate_attestation_data`. The disparity + // bound is in intervals, not slots: a whole-slot margin would let an + // adversary pre-publish next-slot blocks ahead of any honest proposer. + // Catching this early also avoids persisting bogus future blocks to + // RocksDB and triggering BlocksByRoot fan-out for fabricated parents. + let block_start_interval = slot.saturating_mul(INTERVALS_PER_SLOT); + let store_time = self.store.time(); + if block_start_interval > store_time + GOSSIP_DISPARITY_INTERVALS { + warn!( + %slot, + store_time, + proposer, + block_root = %ShortRoot(&block_root.0), + parent_root = %ShortRoot(&parent_root.0), + "Rejecting block: slot is too far in future" + ); + self.discard_pending_subtree(block_root); + 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");