Skip to content

forkchoice: seed anchor checkpoints from the anchor slot#677

Merged
tcoratger merged 1 commit intoleanEthereum:mainfrom
lambdaclass:forkchoice/seed-anchor-checkpoints-from-anchor-slot
Apr 23, 2026
Merged

forkchoice: seed anchor checkpoints from the anchor slot#677
tcoratger merged 1 commit intoleanEthereum:mainfrom
lambdaclass:forkchoice/seed-anchor-checkpoints-from-anchor-slot

Conversation

@MegaRedHand
Copy link
Copy Markdown
Contributor

🗒️ Description

Store.from_anchor previously built the store's justified/finalized checkpoints by copying the slot from the anchor state's embedded latest_justified/latest_finalized (which can be strictly less than the anchor slot) while overriding only the root to the anchor block root. That produced a slot/root-inconsistent checkpoint: the slot pointed at pre-anchor history while the root identified the anchor block itself.

This PR changes Store.from_anchor to use the anchor block as the justified/finalized checkpoints of the Store, following the beacon chain specs.

Also updates the checkpoint-sync tests and a misleading inline comment that described the previous (pre-anchor-slot) behavior.

🔗 Related Issues or PRs

✅ Checklist

  • Ran tox checks to avoid unnecessary CI fails:
    uvx tox
  • Considered adding appropriate tests for the changes.
  • Considered updating the online docs in the ./docs/ directory.

Store.from_anchor previously built the store's justified/finalized
checkpoints by copying the slot from the anchor state's embedded
latest_justified/latest_finalized (which can be strictly less than the
anchor slot) while overriding only the root to the anchor block root.
That produced a slot/root-inconsistent checkpoint: the slot pointed at
pre-anchor history while the root identified the anchor block itself.

Seed both checkpoints from the anchor itself: slot = anchor.slot,
root = anchor_root. The anchor state's embedded checkpoints are
intentionally ignored, matching the "store treats the anchor as the new
genesis" framing already used at the checkpoint-sync entry point.

Genesis initialization is unaffected: at genesis the anchor state's
embedded checkpoints already sit at slot 0 = anchor.slot, so the new
seeding produces identical values.

Also updates the checkpoint-sync tests and a misleading inline comment
that described the previous (pre-anchor-slot) behavior.
@MegaRedHand
Copy link
Copy Markdown
Contributor Author

I believe the CI failure is unrelated

@tcoratger tcoratger merged commit bb60178 into leanEthereum:main Apr 23, 2026
12 of 13 checks passed
@MegaRedHand MegaRedHand deleted the forkchoice/seed-anchor-checkpoints-from-anchor-slot branch April 23, 2026 14:29
march-fatcat pushed a commit to nleanEth/nlean that referenced this pull request Apr 23, 2026
Ports leanEthereum/leanSpec#677 into nlean's checkpoint-sync anchor
creation. CreateCheckpointHeadState used to build the justified and
finalized checkpoints by copying the slot from the anchor state's
embedded latest_justified / latest_finalized while overriding only the
root to the anchor block root. The two fields went out of sync: the
slot still pointed at pre-anchor history while the root identified the
anchor block itself.

Externally the inconsistency surfaced on leanpoint as "finalized slot
> justified slot" once the checkpoint-synced node advanced past its
anchor — leanpoint reads the state.slot field from /states/finalized
and compared it with the justified checkpoint JSON.

Seed both checkpoints from the anchor block directly, matching the
beacon chain fork-choice spec the leanSpec PR references.
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.

2 participants