Skip to content

fix: align SSE parent root with predicted proposer head#9251

Closed
lodekeeper wants to merge 1 commit intoChainSafe:unstablefrom
lodekeeper:follow-up/sse-parent-root-alignment
Closed

fix: align SSE parent root with predicted proposer head#9251
lodekeeper wants to merge 1 commit intoChainSafe:unstablefrom
lodekeeper:follow-up/sse-parent-root-alignment

Conversation

@lodekeeper
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #9209. Aligns the parentBlockRoot on the payload_attributes SSE event with updatedHeadRoot, so the event emits a consistent (prepareState, parentBlockRoot) pair when the predicted proposer differs from the canonical head.

Current behavior on unstable:

const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
  prepareState: updatedPrepareState, // predicted-proposer state
  prepareSlot,
  parentBlockRoot: fromHex(headRoot), // canonical head root
  ...
});

When updatedHeadRoot !== headRoot (predicted proposer path in ePBS), prepareState reflects the updated head while parentBlockRoot still points at the original head. Downstream SSE consumers (builders, relays) see a mismatch between the state's slot/parent and the advertised parentBlockRoot.

This PR changes parentBlockRoot: fromHex(headRoot)parentBlockRoot: fromHex(updatedHeadRoot) so both fields come from the same predicted head.

Context

While investigating the v1.42.0 "Withdrawal mismatch at index=0" regression, I found this inconsistency neighboring the SSE emit path. It was deliberately left out of #9209 (which converged on keeping the original headRoot there). The fix here is a separate latent-semantics alignment, independent of the actual mismatch root cause (addressed in #9246).

Happy to close this if the current SSE semantics are intentional and external consumers rely on headRoot specifically.

Test plan

  • New unit: gloas - should emit payloadAttributes with parentBeaconBlockRoot aligned to updatedHeadRoot in prepareNextSlot.test.ts
  • New unit: getPayloadAttributesForSSE.test.ts covering the extend-vs-fresh-withdrawal path
  • Existing prepareNextSlot tests green (10/10)
  • Lint + type-check clean

AI assistance

Drafted and validated with AI assistance.

@lodekeeper lodekeeper requested a review from a team as a code owner April 21, 2026 18:06
prepareState: updatedPrepareState,
prepareSlot,
parentBlockRoot: fromHex(headRoot),
parentBlockRoot: fromHex(updatedHeadRoot),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

no, we decided to keep headRoot in the other PR cc @lodekeeper

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.

Ack — missed that decision. Closing the PR.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request updates the prepareNextSlot logic to use updatedHeadRoot as the parentBlockRoot when retrieving payload attributes for SSE, ensuring consistency during predicted proposer-boost reorgs. Additionally, comprehensive unit tests were added for the 'gloas' fork to verify the handling of fresh versus stale withdrawals and the alignment of block roots. I have no feedback to provide as the existing review comment was explanatory and did not identify any issues.

@lodekeeper
Copy link
Copy Markdown
Contributor Author

Closing per @nflaig's review — keeping headRoot per the decision in #9209.

@lodekeeper lodekeeper closed this Apr 21, 2026
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