Skip to content

Reimplement proposer boost changes#10368

Merged
tbenr merged 6 commits intoConsensys:masterfrom
tbenr:reimplement-proposer-boost-changes
Feb 17, 2026
Merged

Reimplement proposer boost changes#10368
tbenr merged 6 commits intoConsensys:masterfrom
tbenr:reimplement-proposer-boost-changes

Conversation

@tbenr
Copy link
Copy Markdown
Contributor

@tbenr tbenr commented Feb 12, 2026

fixes #10320

Documentation

  • I thought about documentation and added the doc-change-required label to this PR if updates are required.

Changelog

  • I thought about adding a changelog entry, and added one if I deemed necessary.

Note

Medium Risk
Touches fork-choice proposer boost selection and proposer resolution logic, which can affect head selection and reorg behavior; changes are scoped but consensus-critical.

Overview
Reworks fork-choice proposer boost handling to match updated consensus-spec behavior: proposer boost root is now only set for the first timely block, and additionally checks the block’s proposer matches the expected proposer at the store’s current slot (falling back to setting boost when head state is unavailable for backwards compatibility).

Adds Spec.getProposerIndexAtSlot (with canCalculateProposerIndexAtSlot) to safely resolve proposers across slots/epochs, including Fulu lookahead, and updates accessors/tests accordingly. Also stops excluding previously-failing fork-choice pyspec tests and exposes late-block detection via RecentChainData.isBlockLate.

Written by Cursor Bugbot for commit 440f9bc. This will update automatically on new commits. Configure here.

try {
return block.getProposerIndex().intValue()
== spec.getProposerIndexAtSlot(headState, currentSlot);
} catch (final SlotProcessingException | EpochProcessingException ex) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Late imports can get wrong proposer boost

Medium Severity

shouldUpdateProposerBoostRoot no longer checks that block.getSlot() equals the store’s current slot. If block import finishes after a slot transition, a previously timely block can still pass isBlockLate, and if proposer indices coincidentally match, it can set proposerBoostRoot for the wrong slot.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the bot is right, this check has been moved in the spec and we need to implement more of the changes

Copy link
Copy Markdown
Contributor Author

@tbenr tbenr Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, actually the check is covered by setBlockTimelinessFromArrivalTime, which implements record_block_timeliness from the spec, where that check now is placed.

* @param state a beacon state at or before the requested slot
* @param slot the slot to get the proposer index for (must be >= state.slot)
*/
public int getProposerIndexAtSlot(final BeaconState state, final UInt64 slot)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does make me awkward putting this in our bucket that has no better options, i'd rather that this was at least in SpecVersion i think...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about it. we have to call processSlot, which is in Spec, and can potentially land on a different fork so at least we lookup a consistent specVersion in getBeaconProposerIndex.

Essentially here we are dealing with state slot and target slot, potentially on two different milestones.

Copy link
Copy Markdown
Contributor

@rolfyone rolfyone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tbenr tbenr enabled auto-merge (squash) February 17, 2026 09:46
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

"Can't progress head state from slot %s to slot %s",
headState.getSlot(), currentSlot),
ex);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unhandled exceptions in proposer boost check crash block import

Medium Severity

shouldUpdateProposerBoostRoot wraps SlotProcessingException/EpochProcessingException from getProposerIndexAtSlot in a RuntimeException and re-throws it, which fails the entire block import. The old shouldApplyProposerBoost could never throw. Additionally, the checkArgument inside getProposerIndexAtSlot can throw IllegalArgumentException (if headState.getSlot() > currentSlot) which isn't caught at all. The established codebase pattern (e.g., LateBlockReorgLogic.shouldOverrideFcuCheckProposerPreState) catches these exceptions gracefully and returns a safe default. Within the same method, other edge cases (missing chain head, unresolved state future) already return true as a fallback — the slot processing failure case is inconsistently left to throw.

Additional Locations (1)

Fix in Cursor Fix in Web

@tbenr tbenr merged commit 4ac360d into Consensys:master Feb 17, 2026
59 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Feb 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposer Boost Changes

2 participants