Reimplement proposer boost changes#10368
Conversation
| try { | ||
| return block.getProposerIndex().intValue() | ||
| == spec.getProposerIndexAtSlot(headState, currentSlot); | ||
| } catch (final SlotProcessingException | EpochProcessingException ex) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
the bot is right, this check has been moved in the spec and we need to implement more of the changes
There was a problem hiding this comment.
No, actually the check is covered by setBlockTimelinessFromArrivalTime, which implements record_block_timeliness from the spec, where that check now is placed.
...m/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoice.java
Show resolved
Hide resolved
| * @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) |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
| "Can't progress head state from slot %s to slot %s", | ||
| headState.getSlot(), currentSlot), | ||
| ex); | ||
| } |
There was a problem hiding this comment.
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.


fixes #10320
Documentation
doc-change-requiredlabel to this PR if updates are required.Changelog
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(withcanCalculateProposerIndexAtSlot) 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 viaRecentChainData.isBlockLate.Written by Cursor Bugbot for commit 440f9bc. This will update automatically on new commits. Configure here.