Skip to content

fix(validator): vote for the justified checkpoint when the target walk lands behind it - #1207

Open
adust09 wants to merge 1 commit into
leanEthereum:mainfrom
adust09:fix/clamp-attestation-target-to-source
Open

fix(validator): vote for the justified checkpoint when the target walk lands behind it#1207
adust09 wants to merge 1 commit into
leanEthereum:mainfrom
adust09:fix/clamp-attestation-target-to-source

Conversation

@adust09

@adust09 adust09 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Description

produce_attestation_data asserted source.slot <= target.slot. The assert is reachable: the target walk is bounded by the safe target and the finalized checkpoint, never by the head's justified one, so on a sparse chain the justifiability walk can select a block behind the source.

#1206 has the head chain from a three-node devnet that does it (62 -> 59 -> 51 -> 50 -> 49 -> 45 -> 42, finalized 42, justified 49: the walk lands on 45).

Every lean client already clamps the target up to the justified checkpoint in this case, outside the spec (survey in #1206). This makes the spec say so: when the source is after the target, the vote's target is the justified checkpoint itself. It is on the head chain and justifiable by construction, and the vote keeps its weight in fork choice instead of being dropped.

The clamp sits in produce_attestation_data, not in get_attestation_target, so the target walk and the fixtures that check it through attestation_target_slot are unchanged.

Tests: tests/spec/forks/lstar/test_validator_duties.py builds that chain on a genesis store. The first test fails on main at the assert and passes here; the second checks a target at or after the source is left as the walk selected it.

Closes #1206

…k lands behind it

produce_attestation_data asserted source.slot <= target.slot. The target
walk is bounded by the safe target and the finalized checkpoint, never by
the head's justified one, so on a sparse chain the justifiability walk can
select a block behind the source and the assert fires. A three-node devnet
produced the head chain 62 -> 59 -> 51 -> 50 -> 49 -> 45 -> 42 with
finalized 42 and justified 49: the lookback reaches 50, neither 50 nor 49
is justifiable after 42, and the walk lands on 45.

Every lean client already clamps the target up to the justified checkpoint
here, outside the spec. Make the spec say so: when the source is after the
target, the vote's target is the justified checkpoint itself. The target
walk and the fixtures that check it are unchanged.

Closes leanEthereum#1206

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

produce_attestation_data asserts source <= target, but the target walk can land behind the head's justified checkpoint

1 participant