Skip to content

fix: vote for the justified checkpoint when the target walk lands behind it - #47

Merged
adust09 merged 1 commit into
developfrom
fix/44-clamp-attestation-target-20260912-1300
Sep 11, 2026
Merged

adust09 merged 1 commit into
developfrom
fix/44-clamp-attestation-target-20260912-1300

Conversation

@adust09

@adust09 adust09 commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

The attestation duty was skipped with SOURCE_AFTER_TARGET whenever the target walk selected a block behind the head's justified checkpoint. The walk is bounded by the safe target and the finalized checkpoint, never by the justified one, so a sparse chain reaches this: the three-node devnet5 run behind #42 had the head chain 62 → 59 → 51 → 50 → 49 → 45 → 42 with finalized 42 and justified 49. Three steps from 62 reach 50, neither 50 nor 49 is justifiable after 42, the walk stops at 45, and the validator's weight was missing from slots 16 and 64.

Every other lean client raises the target to the justified checkpoint here — ethlambda, ream, zeam, gean, lantern (qlean-mini bounds only the first walk). Upstream, leanSpec #1206 describes the reachable assert and leanSpec PR #1207 makes the spec say the same; the clamp there sits in produce_attestation_data, so get_attestation_target and the fixtures that check it are unchanged.

Changes

  • verity-chain attestation_data: when the source is after the target, the target is the justified checkpoint. The function is now infallible — the duty path had no other way to fail — and so is ChainView::attestation_data. RejectionReason::SourceAfterTarget stays for incoming attestation validation.
  • verity-validator: the attested debug log names the source next to the target, so a raised vote is visible in the log. DutyError::Rejected stays: block production still returns rejections through it.
  • Two unit tests in duties.rs build the devnet chain above on a genesis store. The first fails on develop (target 45 behind source 49) and passes here; the second checks a target at or after the source is left as the walk selected it.

Validation

  • cargo fmt --all --check, cargo clippy --locked --workspace --all-targets -- -D warnings, cargo test --locked --workspace, cargo deny --locked check, typos — all green.

Closes #44

🤖 Generated with Claude Code

…ind it

The attestation duty returned SourceAfterTarget, and the validator cast
nothing, whenever the target walk selected a block behind the head's
justified checkpoint. The walk is bounded by the safe target and the
finalized checkpoint, never by the justified one, so a sparse chain
reaches this: the three-node devnet5 run behind #42 had the head chain
62 -> 59 -> 51 -> 50 -> 49 -> 45 -> 42 with finalized 42 and justified 49,
and the validator's weight was missing from slots 16 and 64.

Every other lean client raises the target to the justified checkpoint
here (ethlambda, ream, zeam, gean, lantern), and leanSpec PR #1207
(issue #1206) makes the spec say the same. attestation_data now does so
and is infallible: the duty path had no other way to fail. The attested
log names the source next to the target so a raised vote is visible.
DutyError::Rejected stays for block production, which still returns
rejections.

Closes #44

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@adust09
adust09 merged commit a9f3365 into develop Sep 11, 2026
7 checks passed
@adust09
adust09 deleted the fix/44-clamp-attestation-target-20260912-1300 branch September 11, 2026 16:43
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.

Attestation target can walk behind the head's justified checkpoint; other clients clamp, Verity skips the vote

1 participant