Skip to content

fix: self-built execution payload envelope processing - #7987

Closed
Tomi-3-0 wants to merge 9 commits into
unstablefrom
65E
Closed

Tomi-3-0 wants to merge 9 commits into
unstablefrom
65E

Conversation

@Tomi-3-0

Copy link
Copy Markdown
Contributor

When a validator proposes a Gloas block and builds the execution payload locally (self-build), the envelope's state_root field is set at the creation time. This is the root after the beacon state root after process_execution_payload_bid runs but before process_execution_payload runs.

When addHeadExecutionPayload later calls process_execution_payload the additional state transitions inside process_execution_payload updates the latest_block_hash. The resulting state root no longer matches the state_root stored in the envelope that causes verification to fail with :

process_execution_payload: state root mismatch

this passes the verify=false to process_execution_payload for self-built envelopes

@Tomi-3-0
Tomi-3-0 requested review from ahshum and tersec and removed request for ahshum February 17, 2026 11:56
@github-actions

github-actions Bot commented Feb 17, 2026 •

Copy link
Copy Markdown

Unit Test Results

       12 files  ±0    2 444 suites  ±0   47m 23s ⏱️ - 8m 35s
12 893 tests ±0  12 346 ✔️ ±0  547 💤 ±0  0 ❌ ±0 
65 264 runs  ±0  64 554 ✔️ ±0  710 💤 ±0  0 ❌ ±0 

Results for commit 3bf4001. ± Comparison against base commit 93668bc.

♻️ This comment has been updated with latest results.

Comment thread beacon_chain/validators/beacon_validators.nim Outdated
Comment thread beacon_chain/validators/beacon_validators.nim Outdated
discard await node.router.routeExecutionPayloadEnvelope(
signedEnvelope, checkValidator = false)
# Send payload to local EL before broadcasting
payloadStatus = await node.elManager.newExecutionPayload(

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.

Generally, we don't necessarily want this in the critical path for getting the payload out at all, i.e. right now this sequences events as:

  1. gossip-broadcast block
  2. newPayload the envelope/payload
  3. gossip-broadcast the payload

in that order.

Effectively, if the payload isn't accepted, that's a bug, either in Nimbus or in the EL, but it's better to try and get the payload out than to wait on the EL to do so in case. If it's not valid, it's not as if there's some fallback here. Better to just broadcast.

newExecutionPayload can have a timeout of 8s. Nimbus should not wait 8s before proceeding with envelope broadcast, and probably the newExecutionPayload shouldn't occur in proposeBlockAux at all, or indeed beacon_validators.nim, but rather https://github.com/status-im/nimbus-eth2/blob/unstable/beacon_chain/gossip_processing/block_processor.nim

In particular, block_processor centralizes as much as feasible all block validation, from untrusted -> trusted. So what should happen is that we broadcast/route the envelope, and the routing also triggers the block processor to assess the changed block + envelope.

# for self-builds since the envelope's state_root is
# computed before `process_execution_payload` runs,
# so it wouldn't match the post-envelope state root
discard node[].dag.addHeadExecutionPayload(

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.

Is this special case necessary? I'd note that there's no addHeadBlock here either, broadly for similar reasons as the newPayload call: that's the domain of block_processor/block_clearance. routeSignedBeaconBlock and routeExecutionPayloadEnvelope alone should ensure block_processor/block_clearance pick this up.

Comment on lines +517 to 530
let isSelfBuild = signedEnvelope.message.builder_index ==
BUILDER_INDEX_SELF_BUILD

# Verify with state transition function.
process_execution_payload(
dag.cfg,
dag.clearanceState.forky(consensusFork),
signedEnvelope,
func(_: deneb.ExecutionPayload): bool = true,
cache,
verify = not isSelfBuild
).isOkOr:
assign(dag.clearanceState, dag.headState)
info "Envelope transition failed", msg = error

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.

When envelope is self-build, I believe they could be from ourselves or others. And in devnet-0, all blocks should be self-build. It seems we would not be able to verify with clearance state effectively?

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.

I believe "self-build" in this context refers to an envelope that was built locally by this node (i.e., we were the proposer and constructed the payload ourselves)

@ahshum ahshum Feb 20, 2026 •

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.

Yes so my question is - how do we set verify = true for the received envelopes that are self-build by others?

@Tomi-3-0
Tomi-3-0 marked this pull request as draft February 19, 2026 23:18
Comment thread beacon_chain/gossip_processing/block_processor.nim Outdated
@Tomi-3-0
Tomi-3-0 marked this pull request as ready for review February 20, 2026 13:09
@Tomi-3-0
Tomi-3-0 marked this pull request as draft February 20, 2026 14:44
@tersec

tersec commented Feb 20, 2026

Copy link
Copy Markdown
Contributor

Merge conflict in beacon_chain/gossip_processing/block_processor.nim.

@Tomi-3-0 Tomi-3-0 closed this Feb 21, 2026
@Tomi-3-0

Tomi-3-0 commented Feb 21, 2026 •

Copy link
Copy Markdown
Contributor Author

this: #8006 and this: #7748 adresses the purpose of this PR

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.

3 participants