Skip to content

test(fc): add test for head-selection#590

Merged
tcoratger merged 5 commits intoleanEthereum:mainfrom
akronim26:head-selection
Apr 11, 2026
Merged

test(fc): add test for head-selection#590
tcoratger merged 5 commits intoleanEthereum:mainfrom
akronim26:head-selection

Conversation

@akronim26
Copy link
Copy Markdown
Contributor

🗒️ Description

This PR introduces test_fork_from_before_finalization_not_considered to the fork choice test suite to verify a critical LMD-GHOST safety property: blocks belonging to a fork that originates before the latest finalized slot are ignored by the head selection.

🔗 Related Issues or PRs

Closes #582

✅ 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.

@tcoratger
Copy link
Copy Markdown
Collaborator

@akronim26 Can you solve conflicts?

@akronim26
Copy link
Copy Markdown
Contributor Author

@akronim26 Can you solve conflicts?

Done!

tcoratger and others added 2 commits April 11, 2026 19:02
…tion

- Add intermediate StoreChecks on every block (justified/finalized
  progression visible step by step)
- Add head_root_label="block_5" to pin exact head identity
- Add latest_justified_slot=Slot(4) to explain the mechanism: LMD-GHOST
  starts from the justified root (block_4), making the dead fork
  unreachable
- Rewrite docstring: ASCII diagram of the fork, bullet-point
  justification chain, clear explanation of the unreachability mechanism
- Rewrite inline comments: section headers, concrete finalization
  arithmetic, structured labels

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eight

Rewrite the test to actually prove finalization constrains fork choice.
The previous version had a zero-weight dead fork that would lose purely
on weight comparison, making the test pass for the wrong reason.

New design: 8 validators.
- Canonical chain (V0-V5, 6/8): justifies slots 1-4, finalizes slot 3
- Dead fork from block_2 with 2 blocks (dead_6, dead_7)
- dead_7 carries attestations from V3-V7 (5/8) targeting dead_6
- V3-V5's dead fork attestations (slot 7) override their canonical
  ones (slot 5), giving the dead fork MORE total weight
- 5 attesters stay below 2/3 threshold (3*5=15 < 2*8=16), so no
  justification on the dead fork

Result: dead fork has weight 5 vs canonical's 0 from the justified root,
yet the head stays at block_5 because LMD-GHOST starts from block_4
(justified root) and the dead fork branches from block_2 (unreachable).

Also fix framework bug: StoreChecks.validate_against_store() was missing
the handler for latest_finalized_root_label. The field was declared on
the model but silently ignored during validation. Added the handler
following the same pattern as latest_justified_root_label.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 8b460b6 into leanEthereum:main Apr 11, 2026
13 checks passed
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.

test(fc): fork from before finalization is not considered by head selection

2 participants