Skip to content

fix: enforce attestation checkpoint ancestry#833

Open
latifkasuli wants to merge 1 commit into
leanEthereum:mainfrom
latifkasuli:fix/attestation-checkpoint-ancestry
Open

fix: enforce attestation checkpoint ancestry#833
latifkasuli wants to merge 1 commit into
leanEthereum:mainfrom
latifkasuli:fix/attestation-checkpoint-ancestry

Conversation

@latifkasuli
Copy link
Copy Markdown
Contributor

@latifkasuli latifkasuli commented Jun 4, 2026

Summary

Reject attestations whose checkpoints are slot-ordered but not on the same chain.

This enforces the topology implied by source <= target <= head in two places:

  • gossip validation now checks source -> target -> head ancestry through the fork-choice store's block tree
  • state transition chain matching now verifies the head checkpoint root against the canonical historical chain, not only source and target

Root Cause

validate_attestation() checked that source, target, and head blocks were known and that their checkpoint slots matched the block slots. It did not check that target was actually an ancestor of head, or that source was an ancestor of target.

Because fork choice weights by attestation_data.head.root, accepting a known but sibling head lets a validator present a canonical source -> target FFG vote while injecting LMD-GHOST weight into a non-canonical branch. _accumulate_ancestor_weights() walks upward from the attested head and gives weight to that branch's ancestors, so this is a fork-choice vote-splitting vector.

attestation_data_matches_chain() is the state-transition complement. Justification is still keyed on target.root, so a sibling head does not justify a non-canonical target. Checking head there keeps counted votes internally consistent: no decoupled FFG vote on the canonical chain with an LMD head on a sibling fork.

The two checks use different reference frames intentionally: gossip validation has the fork-choice store tree available, while state transition validates against the linear canonical historical_block_hashes view.

Validation

  • uv run pytest tests/lean_spec/spec/forks/lstar/forkchoice tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py tests/lean_spec/spec/forks/lstar/test_block_production.py -q --no-cov
  • uv run --group test fill tests/consensus/lstar/fc/test_gossip_attestation_validation.py tests/consensus/lstar/fc/test_gossip_aggregated_attestation_validation.py tests/consensus/lstar/state_transition/test_justification.py --fork=Lstar --clean -q
  • uv run --group lint ruff check src/lean_spec/spec/forks/lstar/fork_choice.py src/lean_spec/spec/forks/lstar/state_transition.py tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py
  • uv run --group lint ruff format --check src/lean_spec/spec/forks/lstar/fork_choice.py src/lean_spec/spec/forks/lstar/state_transition.py tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py
  • uv run --group lint ty check src/lean_spec/spec/forks/lstar/fork_choice.py src/lean_spec/spec/forks/lstar/state_transition.py tests/lean_spec/spec/forks/lstar/forkchoice/test_validate_attestation.py tests/lean_spec/spec/forks/lstar/state/test_state_process_attestations.py
  • git diff --check

@latifkasuli latifkasuli marked this pull request as ready for review June 4, 2026 10:57
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