Skip to content

feat(lstar): add tiered block-production strategy as a selectable alternative#1149

Open
MegaRedHand wants to merge 8 commits into
leanEthereum:mainfrom
lambdaclass:feat/alternative-tiered-block-production
Open

feat(lstar): add tiered block-production strategy as a selectable alternative#1149
MegaRedHand wants to merge 8 commits into
leanEthereum:mainfrom
lambdaclass:feat/alternative-tiered-block-production

Conversation

@MegaRedHand

@MegaRedHand MegaRedHand commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What

Adds a second proposer-side block-building algorithm for the lstar fork, kept alongside the existing one as an interchangeable mixin.

Mixin File Selection policy
BlockProductionMixin (default) block_production.py round-based fixed-point: re-scan until a pass adds nothing
TieredBlockProductionMixin (new) block_production_tiered.py tiered greedy scorer: finalize > justify > build, projected incrementally

Both share the build_block contract declared on LstarSpecBase, so a fork composes exactly one.

How to switch

The default stays the round-based selection. To select the tiered scorer, swap a single import in spec.py:

# from lean_spec.spec.forks.lstar.block_production import BlockProductionMixin
from lean_spec.spec.forks.lstar.block_production_tiered import (
    TieredBlockProductionMixin as BlockProductionMixin,
)

then regenerate:

uv run fill --fork=lstar --clean -n auto

Test changes: fixtures generate under either mixin

Two fork-choice fillers used to assert the simple builder's exact block body. The two builders include a different number of votes in those scenarios, so generation aborted when the tiered builder was wired in. Both fillers now generate cleanly under either mixin without any test depending on which mixin is active (verified: 540 vectors pass and the determinism check passes, under both).

Each filler is made mixin-independent in the way that fits its scenario:

  • test_produce_block_enforces_max_attestations_data_limit — switched to a single-voter, justifiable-target scenario. No vote reaches the two-thirds supermajority, so no entry justifies its target and the entry cap alone bounds the body. Both builders therefore fill to exactly the limit, and the scenario asserts that count for both. (This is the more robust setup originally written for feat: tiered attestation scoring #791; it changes this one committed vector.)

  • test_justified_divergence_self_heals_in_next_block — the divergence here is real and same-scenario: the fixed-point builder keeps a vote already recorded on-chain, the tiered builder drops it as adding no new voters. That body difference is a proposer-strategy choice, not a consensus outcome, so it is no longer asserted. The test instead asserts the justified slot, which is the strategy-independent witness of the self-heal: the chain can only reach justified slot 1 once the block incorporates the slot-1 votes.

The committed vectors are generated by the default (simple) strategy.

Notes

…ernative

The proposer can fill a block by two interchangeable algorithms that share
the build-block contract. The default round-based fixed-point selection stays
the active strategy; a tiered greedy scorer that ranks finalize over justify
over build is added as a dormant alternative.

Both live as separate mixins so a fork composes exactly one. Selecting the
tiered scorer is a one-line import swap in the fork facade, after which the two
block-production test vectors must be regenerated. Keeping the default
unchanged leaves all committed vectors byte-identical.
Two fork-choice fillers asserted the simple builder's exact block body, so
generation aborted when the tiered builder was wired in. Both now succeed
whichever block-production mixin the fork composes.

The max-attestations filler adopts a single-voter, justifiable-target scenario
where the entry cap alone bounds the body, so both builders fill to exactly the
limit. The self-heals filler keeps one scenario but selects the divergent block
body by the active strategy: the fixed-point builder keeps a redundant vote, the
tiered builder drops it.
The filler branched its block-body assertion on which block-production mixin
the fork composed, coupling a test vector to a proposer-strategy choice.

Drop the body-count assertion and the strategy probe. The justified slot is the
strategy-independent witness of the self-heal: reaching slot 1 is only possible
once the block incorporates the slot-1 votes. The exact body composition is a
proposer choice, not a consensus outcome, so it is no longer asserted.
A fork composes exactly one block-production mixin, and the tiered strategy is
selected by swapping a single import in the fork facade. By default it has no
call site, so vulture reports it as an unused class. It is a real, selectable
strategy, not dead code, so whitelist it like the other statically invisible
uses.
@MegaRedHand MegaRedHand force-pushed the feat/alternative-tiered-block-production branch from c2f6d26 to 01e2d62 Compare June 17, 2026 19:05
@MegaRedHand

Copy link
Copy Markdown
Contributor Author

Note: the dead-code CI check will be red on this PR until #1150 merges.

#1150 fixes a pre-existing stale vulture_whitelist.py on main (the hash_tree_root handlers were renamed _htr_* -> _hash_tree_root_* in #1124 without updating the whitelist). That fix has been kept out of this PR to keep it focused. Once #1150 lands on main, merging main here clears the dead-code failure; the only whitelist change this PR then carries is the TieredBlockProductionMixin entry.

Comment thread src/lean_spec/spec/forks/lstar/block_production_tiered.py Outdated
Comment thread src/lean_spec/spec/forks/lstar/block_production_tiered.py Outdated
…d scoring

The tiered greedy builder broke ties on the smallest target and attestation
slot. Prefer the largest instead so the projected justified slot advances as
close to the tip as possible, shortening recovery from a justification or
finalization stall (mirrors the Zeam devnet4 fix).

The threshold-crossing tiers (finalize, justify) now rank a larger target slot
above new-voter coverage, since they justify regardless; the build tier keeps
coverage first since it only adds marginal voters toward the threshold.

Claude-Session: https://claude.ai/code/session_01WHwxcstQNAwYfYfaMmF7a2
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