Skip to content

refactor(lstar): fold triplicated staleness filter into the extractor#972

Merged
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:audit/arch-08-fold-staleness-filter
Jun 12, 2026
Merged

refactor(lstar): fold triplicated staleness filter into the extractor#972
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:audit/arch-08-fold-staleness-filter

Conversation

@tcoratger

Copy link
Copy Markdown
Collaborator

What

The fork-choice staleness predicate head.slot > latest_finalized.slot was applied at three separate call sites, each pre-filtering a payload pool with an identical dict comprehension before handing it to the latest-vote extractor:

  • block weight computation (counted pool)
  • head recomputation (counted pool)
  • safe-target computation (pending pool)

This fold moves the predicate inside the extractor, so the cutoff exists in exactly one place. Each call site now passes its pool directly plus the finalized slot.

Why it is behavior-preserving

The extractor walks aggregated_payloads.items() in dict insertion order. Pre-filtering with a comprehension preserves the insertion order of the surviving keys; skipping inside the loop visits the same keys in the same order and continues on the filtered-out ones. The set of (attestation_data, proofs) pairs processed, and their order, is identical. The "first vote seen wins on equal slots" LMD tie-break therefore resolves identically.

Test-framework checker

The fork-choice test-store checker (store_checks.py) deliberately inspects raw pool content before pruning, so it must remain unfiltered. Routing it through the now-filtered extractor changed its semantics (it dropped a validator whose head sits at the finalized slot, breaking test_finalization_prunes_stale_aggregated_payloads). It now uses an inlined unfiltered latest-vote loop, preserving its original behavior with no dependency on the filtered extractor.

Equivalence verification

  • Regenerated the full tests/consensus/lstar/fork_choice suite (110 cases) before and after the change into separate output directories.
  • diff -rq of the two fixture trees reports no differences (byte-identical).
  • The built-in two-seed determinism check (--check-determinism) passes in both runs, confirming LMD ordering is unaffected.
  • just check passes (ruff, format, ty, codespell, mdformat).

🤖 Generated with Claude Code

The head.slot > latest_finalized.slot staleness predicate was applied at
three call sites that each pre-filtered a payload pool before handing it to
the latest-vote extractor. Move the predicate inside the extractor so the
cutoff lives in exactly one place, and pass each pool directly.

Iteration order and results are unchanged: the surviving keys appear in the
same insertion order whether filtered before the loop or skipped inside it,
so the first-seen-wins LMD tie-break is preserved.

The fork-choice test-store checker inspects raw pool content before pruning
and must stay unfiltered, so it gets an inlined unfiltered latest-vote loop
instead of routing through the now-filtered extractor.

Verified byte-identical: regenerated the full tests/consensus/lstar/fork_choice
suite before and after the change and diffed the fixtures with no differences,
with the two-seed determinism check passing in both runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 81db0da into leanEthereum:main Jun 12, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant