Repository navigation
fix(heze): read inclusion lists under the real dependent root at epoch start - #692
Conversation
…h start At the first slot of every epoch after the first prune, a node saw zero inclusion lists for the previous slot. It built blocks with an empty inclusion_list_bits and judged payloads against an empty list. Lists are stored under the block at the epoch's shuffling dependent slot. For the last slot of an epoch, that block sits one slot below the block finality has just pruned the live chain down to. The walk over the live chain stops at its lowest block, so it answered with the finalized block and the lookup key missed. inclusion_list_dependent_root now keeps walking through the stored blocks, which are never pruned, once the index runs out. get_shuffling_dependent_root is unchanged, since its anchor behavior is pinned and its other caller only compares two results. A step onto a root the store does not hold ends the walk, which is never worse than the old answer.
🤖 Kimi Code ReviewI'll review this PR focusing on the SummaryThe PR fixes a bug where Critical Issues1. Logic Bug:
|
| Priority | Item | File | Action |
|---|---|---|---|
| Critical | 1 | inclusion_list.rs:5650 |
Verify and fix block_entry return type destructuring |
| Critical | 2 | inclusion_list.rs:5644 |
Add iteration limit to prevent infinite loops |
| High | 6 | fork_choice.rs, inclusion_list.rs |
Extract shared test helpers to test_utils module |
| Medium | 3 | inclusion_list.rs |
Document trust assumptions for stored block reads |
| Medium | 8 | inclusion_list.rs:5326 |
Replace magic numbers with computed constants |
| Low | 7 | inclusion_list.rs:5336 |
Improve root generation in tests |
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
🤖 Claude Code ReviewReview of PR 692: read inclusion lists under the real dependent rootI found no blocking issues. I read the diff and checked Correctness
Minor points (non-blocking)
The fix is targeted, the tests exercise the pruned and skipped-slot cases (the PR description says the first two fail without the fix), and the unpruned test confirms the answer is unchanged when no prune has happened. Looks good to merge. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewLooks good overall — the fix in
One small maintainability concern:
No obvious security or memory-safety issues in this diff, and I don’t see a consensus bug introduced by the new walk. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Motivation
On the heze-devnet-3 kurtosis devnet, ethlambda reads no inclusion lists at the first slot of every epoch:
inclusion_list_bits = 0x0000and the EL is asked to include no IL transactions (4 of 4 epoch-first blocks it proposed; lodestar, prysm and teku carry0xffffat the same slots).inclusion_list_transactions=0 inclusion_list_satisfied=true), 25 of 27 boundaries. Every other slot sees a non-empty set (832 of 832).So FOCIL enforcement is silently off on ethlambda for one slot in every epoch. Liveness and finality are unaffected.
Cause
(slot, dependent_root), wheredependent_rootis the block atcompute_shuffling_dependent_slot(epoch(slot)) = start(epoch - 1) - 1.LiveChainto the finalized slot.inclusion_list_dependent_rootwalked onlyLiveChain(get_shuffling_dependent_root), whose walk falls back to the lowest indexed block, i.e. the finalized block, so the key never matched.Devnet log, slot 192 (lists of slot 191 depend on block 127):
The same ordering (prune, then ~230 ms later the proposal) holds at every boundary ethlambda proposed. The two boundaries that did read lists (slots 64 and 96) precede the first prune.
Fix
inclusion_list_dependent_rootkeeps walking through the block table, which keeps every block, where theLiveChainwalk stops above the dependent slot. The common case still answers from the index alone; a boundary costs one block read.get_shuffling_dependent_rootitself is unchanged, since its proposer-boost caller only compares two of its answers.Both callers are fixed by this: block production (
heze_inclusion_lists) and envelope satisfaction (payload_inclusion_list_transactions).Tests
New unit tests in
inclusion_list::tests, on a store whose blocks go throughinsert_signed_block(so bothBlockHeadersandLiveChainhold them):the_dependent_root_below_a_pruned_live_chain_is_the_real_ancestorLiveChainpruned to 128a_skipped_dependent_slot_resolves_to_the_block_before_it_past_the_prunean_unpruned_live_chain_answers_as_the_index_walk_doesget_shuffling_dependent_rootThe first two fail without the fix.
fork_choice::testsexposes itsempty_storeandblockhelpers aspub(crate)for reuse.Ran locally:
cargo test -p ethlambda-state-transition --profile release-fast --lib(627 passed, 15 ignored),cargo clippy -p ethlambda-state-transition --all-targets -- -D warnings,cargo check -p ethlambda-rpc.Follow-up
The bug dates from the original EIP-7805 commit, so after this lands it gets merged forward into
heze-devnet-2andheze-devnet-3.