Skip to content

Fix envelope proposal - #8006

Merged
arnetheduck merged 39 commits into
unstablefrom
sam/pQH
May 1, 2026
Merged

arnetheduck merged 39 commits into
unstablefrom
sam/pQH

Conversation

@ahshum
ahshum requested a review from Tomi-3-0 February 21, 2026 13:12
@github-actions

github-actions Bot commented Feb 21, 2026 •

Copy link
Copy Markdown

Unit Test Results

       12 files  ±0    2 832 suites  ±0   1h 27m 53s ⏱️ - 3m 50s
15 921 tests ±0  14 356 ✔️ ±0  1 565 💤 ±0  0 ❌ ±0 
76 692 runs  ±0  74 952 ✔️ ±0  1 740 💤 ±0  0 ❌ ±0 

Results for commit efc5cb1. ± Comparison against base commit 7b6487d.

♻️ This comment has been updated with latest results.


discard await node.router.routeExecutionPayloadEnvelope(
signedEnvelope, checkValidator = false)
let res = await node.router.routeExecutionPayloadEnvelope(

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.

why is this a separate method? can it not be a part of routeSignedBeaconBlock? i'm just trying to understand

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.

@tersec

tersec commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

Updating to an unstable release incorporating

looks likely to fix the jenkins/nimbus-eth2/linux/x86_64/nix CI failure.

@Tomi-3-0
Tomi-3-0 requested a review from agnxsh February 26, 2026 21:48
return head

envelope.state_root = hash_tree_root(
node.dag.clearanceState.forky(consensusFork).data)

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.

clearanceState is a ForkedHashedBeaconState, which means one shouldn't have to recompute the hash; it's cached:

ForkedHashedBeaconState* = object
case kind*: ConsensusFork
of ConsensusFork.Phase0: phase0Data*: phase0.HashedBeaconState
of ConsensusFork.Altair: altairData*: altair.HashedBeaconState
of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.HashedBeaconState
of ConsensusFork.Capella: capellaData*: capella.HashedBeaconState
of ConsensusFork.Deneb: denebData*: deneb.HashedBeaconState
of ConsensusFork.Electra: electraData*: electra.HashedBeaconState
of ConsensusFork.Fulu: fuluData*: fulu.HashedBeaconState
of ConsensusFork.Gloas: gloasData*: gloas.HashedBeaconState

and
HashedBeaconState* = object
data*: BeaconState
root*: Eth2Digest # hash_tree_root(data)

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 main reason is with verify = false, state.root wouldn't be updated due to

if verify:
state.root = hash_tree_root(state.data)
if envelope.state_root != state.root:
return err("process_execution_payload: state root mismatch")

And follows the spec, envelope.state_root is created by using process_execution_payload with verify=false so I believe I have to manually compute the state root after that.

template rollbackState() =
assign(node.dag.clearanceState, node.dag.headState)

process_execution_payload(

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.

This seems a bit odd to verify here, in general, because this whole handleProposal()/proposeBlock()/proposeBlockAux() callstack only triggers for BN-controlled validators, while as much as possible should be architected to support (even though, yes, Nimbus doesn't have the Beacon API support in place yet, it will have to by hoodi/sepolia testnet shipping) the VC/Beacon REST API approach as symmetrically/identically as possible, so they're guaranteed the same logic and have as much as feasible the same codepaths.

This is why routeFoo() combined with block_processor centralizes things: it means that both beacon_validators and the rpc/rest_foo.nim can call routeFoo() and get the same checks, the same codepaths. As is, to the extent this process_execution_payload() call is important, it's not reached at all here a REST version of Gloas block proposal.

I'd note that similarly the code above delegates to routeSignedBeaconBlock() to handle everything involved e.g.,

proc routeSignedBeaconBlock*(
router: ref MessageRouter,
blck: ForkySignedBeaconBlock,
someSidecarsOpt: SomeSidecarsToRoute,
checkValidator: bool
): Future[RouteBlockResult] {.async: (raises: [CancelledError]).} =
# 1. Validate
? router.validateRouteBlock(blck, checkValidator)
# 2. Publish block
await router.publishRouteBlock(blck)
# 3. Publish sidecars
when someSidecarsOpt is NoSidecarsAtFork:
const finalSidecars = noSidecars
else:
let finalSidecars = await publishSidecars(router, blck, someSidecarsOpt)
# 4. Add block to DAG
return await router.addRoutedBlock(blck, finalSidecars)

and then all these callers (aside from the exported definition) run the same validation:

$ rg routeSignedBeaconBlock
beacon_chain/validators/message_router_mev.nim
133:      (await node.router.routeSignedBeaconBlock(
136:        return err("routeSignedBeaconBlock error") # Errors logged in router

beacon_chain/validators/message_router.nim
293:proc routeSignedBeaconBlock*(

beacon_chain/validators/beacon_validators.nim
593:      node.router.routeSignedBeaconBlock(signedBlock, sidecarsOpt,

beacon_chain/rpc/rest_beacon_api.nim
1052:            await node.router.routeSignedBeaconBlock(
1059:            await node.router.routeSignedBeaconBlock(
1064:            await node.router.routeSignedBeaconBlock(
1069:            await node.router.routeSignedBeaconBlock(
1207:            await node.router.routeSignedBeaconBlock(

with no code duplication and no possibility for different validation variations to drift apart, because they all live in that one routeSignedBeaconBlock.

So whatever validation needs to happen should ideally be part of routeExecutionPayloadEnvelope().

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.

Good shout. Will address this.

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.

Instead of message router, I move the logic to makeExecutionPayloadEnvelope which I think it would also help on testing.

ahshum added 3 commits March 3, 2026 09:47
Comment thread beacon_chain/validators/message_router.nim Outdated
ahshum added a commit that referenced this pull request Mar 18, 2026
Comment thread beacon_chain/validators/message_router.nim Outdated
Comment thread beacon_chain/validators/block_payloads.nim Outdated
Comment thread beacon_chain/validators/beacon_validators.nim Outdated
Comment thread beacon_chain/validators/beacon_validators.nim Outdated
Comment thread beacon_chain/validators/beacon_validators.nim Outdated
@ahshum
ahshum marked this pull request as draft April 24, 2026 11:41
@ahshum
ahshum marked this pull request as ready for review May 1, 2026 07:30
if signatureRes.isErr:
error "Failed to sign sign execution payload envelope",
slot, validator = shortLog(validator), err = signatureRes.error
return newBlockRef.get()

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.

Suggested change
return newBlockRef.get()

this looks redundant?

Comment thread beacon_chain/validators/message_router.nim Outdated
@arnetheduck
arnetheduck enabled auto-merge (squash) May 1, 2026 08:59
@arnetheduck
arnetheduck merged commit 9ed16a2 into unstable May 1, 2026
11 checks passed
@arnetheduck
arnetheduck deleted the sam/pQH branch May 1, 2026 10:16
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.

5 participants