Skip to content

forkchoice: skip STARK aggregate verify on proofs the filler just produced#679

Closed
tcoratger wants to merge 1 commit intoleanEthereum:mainfrom
tcoratger:forkchoice-skip-stark-verify-on-fresh-proofs
Closed

forkchoice: skip STARK aggregate verify on proofs the filler just produced#679
tcoratger wants to merge 1 commit intoleanEthereum:mainfrom
tcoratger:forkchoice-skip-stark-verify-on-fresh-proofs

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Aggregated STARK proofs built by the fixture filler were re-verified by the fork-choice Store on every block and every gossip aggregated attestation — pure duplicated work, since client teams rerun verification when they consume the fixtures.

Thread an opt-in trust flag from the filler down to the two STARK-verify call sites so fresh, filler-produced proofs skip verification while leaving every other code path (client consumers, unit tests, the verify_signatures fixture format) untouched.

  • SignedBlock.verify_signatures(..., skip_aggregate_verify=False) — new kw-only flag. Skips the per-attestation STARK aggregate verify. Proposer XMSS verify and structural checks still run.
  • Store.on_block(..., trust_proofs=False) — forwards to verify_signatures as skip_aggregate_verify.
  • Store.on_gossip_aggregated_attestation(..., trust_proof=False) — gates only the proof.verify(...) call. validate_attestation and validator-index bounds checks still run.
  • Fork-choice filler (packages/testing/src/consensus_testing/test_fixtures/fork_choice.py) mirrors the existing valid_signature flag on each spec: valid_signature=True → trust, valid_signature=False → still verify (so explicit invalid-proof tests keep rejecting).

Measured impact

Single-process test-scheme fill of tests/consensus/devnet/:

metric before after
wall time 113.59 s 77.10 s (-32%)
verify_aggregated_signatures calls 611 1
verify_aggregated_signatures total 28.5 s ~0 s

The one remaining verify call is the explicit-invalid test that needs the verifier to run. Prod mode (larger STARK params) benefits proportionally.

What does not change

  • AggregatedSignatureProof.verify itself — stays pure; the caller decides whether to call it.
  • fill.py CLI, filler.py pytest plugin, State.state_transition, State.build_block.
  • The verify_signatures_test fixture — calls verify_signatures(...) with the default False, so that fixture format (which exists precisely to exercise the verifier) still runs the full STARK verify.
  • Any test under tests/consensus/devnet/. No test was edited.
  • Fixture JSON layout and field contents (STARK proof bytes already differ run-to-run because the prover is non-deterministic).

Test plan

  • uvx tox -e all-checks clean (ruff, ty, codespell, mdformat).
  • uvx tox -e pytest — 3251 passed, 91.74% coverage.
  • End-to-end fill on tests/consensus/devnet/fc/ + tests/consensus/devnet/verify_signatures/ — 87/87 passed. Explicit-invalid specs (tests/consensus/devnet/fc/test_gossip_attestation_validation.py, tests/consensus/devnet/verify_signatures/test_invalid_signatures.py) still reject as expected.
  • Profiling rerun confirms the expected drop in verify_aggregated_signatures count.
  • CI prod-vectors job runtime after merge (expected to drop proportionally from the ~50 min baseline).

New unit coverage

Added 4 minimal tests, folded into existing test files rather than new standalone modules:

  • tests/lean_spec/subspecs/containers/block/test_verify_signatures_skip.py (new file, only because containers/block/ had no existing unit tests yet) — 2 tests: skip_aggregate_verify=True bypasses the STARK verifier; proposer-signature verify still runs when the flag is True.
  • tests/lean_spec/subspecs/forkchoice/test_store_attestations.py (existing) — 2 new tests: on_block(..., trust_proofs=True) and on_gossip_aggregated_attestation(..., trust_proof=True) both skip the STARK verifier.

🤖 Generated with Claude Code

…duced

During fixture generation the filler creates aggregated proofs itself, so
re-verifying them inside the fork-choice Store is duplicated work that
clients run anyway when they consume fixtures. Thread an explicit opt-in
flag from the filler down to the two STARK-verify call sites:

- skip_aggregate_verify on SignedBlock.verify_signatures (keeps structural
  checks and proposer-signature verify).
- trust_proofs on Store.on_block (forwards to verify_signatures).
- trust_proof on Store.on_gossip_aggregated_attestation.

The fork-choice filler mirrors each spec's valid_signature flag, so tests
with valid_signature=False still drive the verifier and keep rejecting.

Measured on a single-process test-scheme fill of tests/consensus/devnet/:
wall time 113 s -> 77 s (-32%); verify_aggregated_signatures count
611 -> 1. Prod mode benefits proportionally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger closed this Apr 24, 2026
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