From a303e4f9b9bd9415acefa914bea9194a77f2dee9 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Fri, 13 Mar 2026 12:10:22 +0000 Subject: [PATCH 1/2] fix: correct pre-Deneb attestation propagation slot range boundary slotWithPastTolerance floors the disparity-adjusted time to a slot, which is one slot too strict at the exact boundary where compute_time_at_slot(slot + range + 1) + MAXIMUM_GOSSIP_CLOCK_DISPARITY equals current_time_ms. Use isCurrentSlotGivenGossipDisparity to extend the lower bound by one slot only within the disparity window. Caught by consensus-specs gossip validation reference tests --- .../beacon-node/src/chain/validation/attestation.ts | 10 +++++++--- .../beacon-node/test/spec/utils/specTestIterator.ts | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/beacon-node/src/chain/validation/attestation.ts b/packages/beacon-node/src/chain/validation/attestation.ts index 046c482d01ee..d410469ed1c6 100644 --- a/packages/beacon-node/src/chain/validation/attestation.ts +++ b/packages/beacon-node/src/chain/validation/attestation.ts @@ -604,10 +604,14 @@ export function verifyPropagationSlotRange(fork: ForkName, chain: IBeaconChain, // // see: https://github.com/ethereum/consensus-specs/pull/3360 if (ForkSeq[fork] < ForkSeq.deneb) { + const currentSlot = chain.clock.currentSlot; + const withinPastDisparity = currentSlot > 0 && chain.clock.isCurrentSlotGivenGossipDisparity(currentSlot - 1); const earliestPermissibleSlot = Math.max( - // slot with past tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY - chain.clock.slotWithPastTolerance(chain.config.MAXIMUM_GOSSIP_CLOCK_DISPARITY / 1000) - - chain.config.ATTESTATION_PROPAGATION_SLOT_RANGE, + // Pre-Deneb propagation is time-bounded: an attestation remains valid at the exact old + // boundary `compute_time_at_slot(slot + range + 1) + MAXIMUM_GOSSIP_CLOCK_DISPARITY`. + // Model that boundary by extending the lower slot bound by one additional slot only while + // the clock still considers the previous slot current given gossip disparity. + currentSlot - chain.config.ATTESTATION_PROPAGATION_SLOT_RANGE - (withinPastDisparity ? 1 : 0), 0 ); diff --git a/packages/beacon-node/test/spec/utils/specTestIterator.ts b/packages/beacon-node/test/spec/utils/specTestIterator.ts index 9e72a5f06ca3..cac1e6921b5a 100644 --- a/packages/beacon-node/test/spec/utils/specTestIterator.ts +++ b/packages/beacon-node/test/spec/utils/specTestIterator.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import {describe, it} from "vitest"; -import {ForkName} from "@lodestar/params"; +import {ForkName, forkAll} from "@lodestar/params"; import {describeDirectorySpecTest} from "@lodestar/spec-test-util"; import {RunnerType, TestRunner} from "./types.js"; @@ -59,7 +59,7 @@ const coveredTestRunners = [ // ], // ``` export const defaultSkipOpts: SkipOpts = { - skippedForks: ["eip7805"], + skippedForks: ["eip7805", "heze"], skippedTestSuites: [ // Merge transition tests are skipped because we no longer support performing the merge transition. // All networks have already completed the merge, so this code path is no longer needed. From 77cee14fac0c0bea42d61b316af74fe88becdb6a Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Fri, 13 Mar 2026 12:12:58 +0000 Subject: [PATCH 2/2] Remove unused import --- packages/beacon-node/test/spec/utils/specTestIterator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/beacon-node/test/spec/utils/specTestIterator.ts b/packages/beacon-node/test/spec/utils/specTestIterator.ts index cac1e6921b5a..384eadf81b0b 100644 --- a/packages/beacon-node/test/spec/utils/specTestIterator.ts +++ b/packages/beacon-node/test/spec/utils/specTestIterator.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import {describe, it} from "vitest"; -import {ForkName, forkAll} from "@lodestar/params"; +import {ForkName} from "@lodestar/params"; import {describeDirectorySpecTest} from "@lodestar/spec-test-util"; import {RunnerType, TestRunner} from "./types.js";