Skip to content

feat: add stateless path to publishExecutionPayloadEnvelope#9401

Draft
ensi321 wants to merge 7 commits into
unstablefrom
nc/publish-envelope-with-blobs
Draft

feat: add stateless path to publishExecutionPayloadEnvelope#9401
ensi321 wants to merge 7 commits into
unstablefrom
nc/publish-envelope-with-blobs

Conversation

@ensi321
Copy link
Copy Markdown
Contributor

@ensi321 ensi321 commented May 23, 2026

External builders and stateless validator clients (multi-BN, DVT, failover) submit the envelope wrapped with blobs and KZG proofs per ethereum/beacon-APIs#580.

  • Add Gloas.SignedExecutionPayloadEnvelopeContents SSZ container
  • Extend POST /eth/v1/beacon/execution_payload_envelope to accept either shape (JSON discriminates by signed_execution_payload_envelope key; SSZ tries the wrapper first then falls back to the bare envelope) and forward an optional broadcast_validation query
  • Wire supplied blobs + kzg_proofs through getGloasDataColumnSidecars in the publish handler so data columns are derived and gossiped on the external-builder path

External builders and stateless validator clients (multi-BN, DVT,
failover) submit the envelope wrapped with blobs and KZG proofs per
beacon-APIs PR #580. Lodestar previously parsed the body as a bare
SignedExecutionPayloadEnvelope, so the wrapped request failed with
"JSON is not an array" and the resulting payloads were orphaned because
no data column sidecars were gossiped.

- Add Gloas.SignedExecutionPayloadEnvelopeContents SSZ container
- Extend POST /eth/v1/beacon/execution_payload_envelope to accept either
  shape (JSON discriminates by `signed_execution_payload_envelope` key;
  SSZ tries the wrapper first then falls back to the bare envelope) and
  forward an optional broadcast_validation query
- Wire supplied blobs + kzg_proofs through getGloasDataColumnSidecars
  in the publish handler so data columns are derived and gossiped on
  the external-builder path

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 implements the stateless mode for the publishExecutionPayloadEnvelope endpoint, allowing validator clients to supply blobs and KZG proofs alongside the execution payload envelope. Key changes include updating the API route definitions to handle the new SignedExecutionPayloadEnvelopeContents wrapper in both JSON and SSZ formats, adding a new test suite for these routes, and updating the beacon node implementation to process supplied blobs by computing cells and generating data column sidecars. Feedback suggests including the broadcastValidation parameter in the implementation for consistency with the API definition and switching to asynchronous cell computation to prevent blocking the event loop during intensive operations.

Comment thread packages/beacon-node/src/api/impl/beacon/blocks/index.ts Outdated
Comment thread packages/beacon-node/src/api/impl/beacon/blocks/index.ts
Match the route's exposed argument shape so the handler signature
remains stable once broadcast_validation enforcement is implemented.
Behavior unchanged (still defaults to gossip-level validation).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: b02abd7 Previous: 055b83c Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 881.13 us/op 1.2316 ms/op 0.72
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 37.631 us/op 39.964 us/op 0.94
BLS verify - blst 614.16 us/op 631.51 us/op 0.97
BLS verifyMultipleSignatures 3 - blst 1.2712 ms/op 1.3022 ms/op 0.98
BLS verifyMultipleSignatures 8 - blst 2.0161 ms/op 2.0754 ms/op 0.97
BLS verifyMultipleSignatures 32 - blst 6.2514 ms/op 6.4593 ms/op 0.97
BLS verifyMultipleSignatures 64 - blst 12.139 ms/op 12.760 ms/op 0.95
BLS verifyMultipleSignatures 128 - blst 23.971 ms/op 25.005 ms/op 0.96
BLS deserializing 10000 signatures 603.04 ms/op 622.44 ms/op 0.97
BLS deserializing 100000 signatures 6.0293 s/op 6.2463 s/op 0.97
BLS verifyMultipleSignatures - same message - 3 - blst 663.86 us/op 769.41 us/op 0.86
BLS verifyMultipleSignatures - same message - 8 - blst 786.40 us/op 817.55 us/op 0.96
BLS verifyMultipleSignatures - same message - 32 - blst 1.4894 ms/op 1.4765 ms/op 1.01
BLS verifyMultipleSignatures - same message - 64 - blst 2.2098 ms/op 2.2342 ms/op 0.99
BLS verifyMultipleSignatures - same message - 128 - blst 3.8016 ms/op 3.9335 ms/op 0.97
BLS aggregatePubkeys 32 - blst 16.650 us/op 17.595 us/op 0.95
BLS aggregatePubkeys 128 - blst 59.191 us/op 62.686 us/op 0.94
getSlashingsAndExits - default max 43.912 us/op 50.722 us/op 0.87
getSlashingsAndExits - 2k 327.61 us/op 413.55 us/op 0.79
proposeBlockBody type=full, size=empty 3.2410 ms/op 1.3840 ms/op 2.34
isKnown best case - 1 super set check 148.00 ns/op 183.00 ns/op 0.81
isKnown normal case - 2 super set checks 150.00 ns/op 176.00 ns/op 0.85
isKnown worse case - 16 super set checks 147.00 ns/op 181.00 ns/op 0.81
validate api signedAggregateAndProof - struct 1.3816 ms/op 1.4431 ms/op 0.96
validate gossip signedAggregateAndProof - struct 1.3889 ms/op 1.4392 ms/op 0.97
batch validate gossip attestation - vc 640000 - chunk 32 99.532 us/op 102.42 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 64 88.091 us/op 90.380 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 128 82.335 us/op 84.996 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 256 79.716 us/op 81.971 us/op 0.97
bytes32 toHexString 261.00 ns/op 293.00 ns/op 0.89
bytes32 Buffer.toString(hex) 153.00 ns/op 182.00 ns/op 0.84
bytes32 Buffer.toString(hex) from Uint8Array 203.00 ns/op 242.00 ns/op 0.84
bytes32 Buffer.toString(hex) + 0x 154.00 ns/op 186.00 ns/op 0.83
Return object 10000 times 0.19830 ns/op 0.21120 ns/op 0.94
Throw Error 10000 times 3.1061 us/op 3.1587 us/op 0.98
toHex 83.903 ns/op 85.446 ns/op 0.98
Buffer.from 76.622 ns/op 78.804 ns/op 0.97
shared Buffer 48.462 ns/op 49.846 ns/op 0.97
fastMsgIdFn sha256 / 200 bytes 1.3670 us/op 1.4250 us/op 0.96
fastMsgIdFn h32 xxhash / 200 bytes 140.00 ns/op 172.00 ns/op 0.81
fastMsgIdFn h64 xxhash / 200 bytes 182.00 ns/op 213.00 ns/op 0.85
fastMsgIdFn sha256 / 1000 bytes 4.4370 us/op 4.5640 us/op 0.97
fastMsgIdFn h32 xxhash / 1000 bytes 226.00 ns/op 257.00 ns/op 0.88
fastMsgIdFn h64 xxhash / 1000 bytes 229.00 ns/op 258.00 ns/op 0.89
fastMsgIdFn sha256 / 10000 bytes 39.155 us/op 40.351 us/op 0.97
fastMsgIdFn h32 xxhash / 10000 bytes 1.1860 us/op 1.2580 us/op 0.94
fastMsgIdFn h64 xxhash / 10000 bytes 760.00 ns/op 813.00 ns/op 0.93
send data - 1000 256B messages 4.4564 ms/op 4.2977 ms/op 1.04
send data - 1000 512B messages 4.2658 ms/op 4.5096 ms/op 0.95
send data - 1000 1024B messages 4.4884 ms/op 4.5447 ms/op 0.99
send data - 1000 1200B messages 5.1695 ms/op 4.9802 ms/op 1.04
send data - 1000 2048B messages 4.7618 ms/op 5.0919 ms/op 0.94
send data - 1000 4096B messages 5.7900 ms/op 6.2954 ms/op 0.92
send data - 1000 16384B messages 31.145 ms/op 36.593 ms/op 0.85
send data - 1000 65536B messages 235.09 ms/op 133.56 ms/op 1.76
enrSubnets - fastDeserialize 64 bits 658.00 ns/op 722.00 ns/op 0.91
enrSubnets - ssz BitVector 64 bits 248.00 ns/op 280.00 ns/op 0.89
enrSubnets - fastDeserialize 4 bits 101.00 ns/op 125.00 ns/op 0.81
enrSubnets - ssz BitVector 4 bits 249.00 ns/op 277.00 ns/op 0.90
prioritizePeers score -10:0 att 32-0.1 sync 2-0 190.45 us/op 199.66 us/op 0.95
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 218.81 us/op 239.16 us/op 0.91
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 320.51 us/op 338.31 us/op 0.95
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 567.73 us/op 592.93 us/op 0.96
prioritizePeers score 0:0 att 64-1 sync 4-1 667.22 us/op 688.19 us/op 0.97
array of 16000 items push then shift 1.1933 us/op 1.2257 us/op 0.97
LinkedList of 16000 items push then shift 7.1800 ns/op 7.2080 ns/op 1.00
array of 16000 items push then pop 62.367 ns/op 67.899 ns/op 0.92
LinkedList of 16000 items push then pop 5.7150 ns/op 5.7240 ns/op 1.00
array of 24000 items push then shift 1.7932 us/op 1.7490 us/op 1.03
LinkedList of 24000 items push then shift 7.3660 ns/op 7.0570 ns/op 1.04
array of 24000 items push then pop 91.631 ns/op 92.525 ns/op 0.99
LinkedList of 24000 items push then pop 5.9370 ns/op 5.7870 ns/op 1.03
intersect bitArray bitLen 8 4.6730 ns/op 4.5530 ns/op 1.03
intersect array and set length 8 28.786 ns/op 27.913 ns/op 1.03
intersect bitArray bitLen 128 23.506 ns/op 22.821 ns/op 1.03
intersect array and set length 128 481.26 ns/op 468.07 ns/op 1.03
bitArray.getTrueBitIndexes() bitLen 128 1.0010 us/op 950.00 ns/op 1.05
bitArray.getTrueBitIndexes() bitLen 248 1.7640 us/op 1.7010 us/op 1.04
bitArray.getTrueBitIndexes() bitLen 512 3.5620 us/op 3.4940 us/op 1.02
Full columns - reconstruct all 6 blobs 179.82 us/op 153.75 us/op 1.17
Full columns - reconstruct half of the blobs out of 6 95.902 us/op 92.647 us/op 1.04
Full columns - reconstruct single blob out of 6 32.953 us/op 45.862 us/op 0.72
Half columns - reconstruct all 6 blobs 383.04 ms/op 374.10 ms/op 1.02
Half columns - reconstruct half of the blobs out of 6 190.88 ms/op 186.64 ms/op 1.02
Half columns - reconstruct single blob out of 6 67.330 ms/op 67.360 ms/op 1.00
Full columns - reconstruct all 10 blobs 283.97 us/op 413.21 us/op 0.69
Full columns - reconstruct half of the blobs out of 10 149.17 us/op 145.45 us/op 1.03
Full columns - reconstruct single blob out of 10 30.320 us/op 44.638 us/op 0.68
Half columns - reconstruct all 10 blobs 605.95 ms/op 611.04 ms/op 0.99
Half columns - reconstruct half of the blobs out of 10 305.78 ms/op 308.57 ms/op 0.99
Half columns - reconstruct single blob out of 10 68.677 ms/op 65.880 ms/op 1.04
Full columns - reconstruct all 20 blobs 1.8372 ms/op 1.2830 ms/op 1.43
Full columns - reconstruct half of the blobs out of 20 242.40 us/op 399.63 us/op 0.61
Full columns - reconstruct single blob out of 20 30.916 us/op 46.565 us/op 0.66
Half columns - reconstruct all 20 blobs 1.1675 s/op 1.2174 s/op 0.96
Half columns - reconstruct half of the blobs out of 20 589.79 ms/op 620.07 ms/op 0.95
Half columns - reconstruct single blob out of 20 63.623 ms/op 67.167 ms/op 0.95
Set add up to 64 items then delete first 1.9431 us/op 2.0953 us/op 0.93
OrderedSet add up to 64 items then delete first 3.0768 us/op 3.3650 us/op 0.91
Set add up to 64 items then delete last 1.9463 us/op 2.0812 us/op 0.94
OrderedSet add up to 64 items then delete last 3.0195 us/op 3.2339 us/op 0.93
Set add up to 64 items then delete middle 1.9257 us/op 2.0834 us/op 0.92
OrderedSet add up to 64 items then delete middle 4.3612 us/op 4.6924 us/op 0.93
Set add up to 128 items then delete first 3.8647 us/op 4.1235 us/op 0.94
OrderedSet add up to 128 items then delete first 5.6718 us/op 6.2335 us/op 0.91
Set add up to 128 items then delete last 3.5414 us/op 3.7311 us/op 0.95
OrderedSet add up to 128 items then delete last 5.3610 us/op 5.7445 us/op 0.93
Set add up to 128 items then delete middle 3.5302 us/op 3.7094 us/op 0.95
OrderedSet add up to 128 items then delete middle 10.802 us/op 11.264 us/op 0.96
Set add up to 256 items then delete first 7.1897 us/op 7.7145 us/op 0.93
OrderedSet add up to 256 items then delete first 11.391 us/op 12.072 us/op 0.94
Set add up to 256 items then delete last 7.0270 us/op 7.5534 us/op 0.93
OrderedSet add up to 256 items then delete last 10.689 us/op 11.639 us/op 0.92
Set add up to 256 items then delete middle 7.0409 us/op 7.5265 us/op 0.94
OrderedSet add up to 256 items then delete middle 32.494 us/op 35.472 us/op 0.92
pass gossip attestations to forkchoice per slot 2.3636 ms/op 2.5360 ms/op 0.93
forkChoice updateHead vc 100000 bc 64 eq 0 361.87 us/op 381.97 us/op 0.95
forkChoice updateHead vc 600000 bc 64 eq 0 2.1684 ms/op 2.3104 ms/op 0.94
forkChoice updateHead vc 1000000 bc 64 eq 0 3.6400 ms/op 3.8110 ms/op 0.96
forkChoice updateHead vc 600000 bc 320 eq 0 2.1919 ms/op 2.3005 ms/op 0.95
forkChoice updateHead vc 600000 bc 1200 eq 0 2.2275 ms/op 2.3596 ms/op 0.94
forkChoice updateHead vc 600000 bc 7200 eq 0 2.6071 ms/op 3.0623 ms/op 0.85
forkChoice updateHead vc 600000 bc 64 eq 1000 2.7263 ms/op 2.8193 ms/op 0.97
forkChoice updateHead vc 600000 bc 64 eq 10000 2.8740 ms/op 2.9238 ms/op 0.98
forkChoice updateHead vc 600000 bc 64 eq 300000 6.4327 ms/op 7.0660 ms/op 0.91
computeDeltas 1400000 validators 0% inactive 11.798 ms/op 12.144 ms/op 0.97
computeDeltas 1400000 validators 10% inactive 11.107 ms/op 11.500 ms/op 0.97
computeDeltas 1400000 validators 20% inactive 10.113 ms/op 10.374 ms/op 0.97
computeDeltas 1400000 validators 50% inactive 7.8787 ms/op 8.0421 ms/op 0.98
computeDeltas 2100000 validators 0% inactive 17.867 ms/op 18.344 ms/op 0.97
computeDeltas 2100000 validators 10% inactive 16.803 ms/op 17.380 ms/op 0.97
computeDeltas 2100000 validators 20% inactive 15.217 ms/op 15.741 ms/op 0.97
computeDeltas 2100000 validators 50% inactive 9.0357 ms/op 12.084 ms/op 0.75
altair processAttestation - 250000 vs - 7PWei normalcase 1.8035 ms/op 2.4130 ms/op 0.75
altair processAttestation - 250000 vs - 7PWei worstcase 2.6657 ms/op 3.1243 ms/op 0.85
altair processAttestation - setStatus - 1/6 committees join 96.527 us/op 102.60 us/op 0.94
altair processAttestation - setStatus - 1/3 committees join 190.51 us/op 194.05 us/op 0.98
altair processAttestation - setStatus - 1/2 committees join 268.44 us/op 283.11 us/op 0.95
altair processAttestation - setStatus - 2/3 committees join 341.27 us/op 372.26 us/op 0.92
altair processAttestation - setStatus - 4/5 committees join 474.93 us/op 518.13 us/op 0.92
altair processAttestation - setStatus - 100% committees join 565.94 us/op 608.20 us/op 0.93
altair processBlock - 250000 vs - 7PWei normalcase 4.2846 ms/op 4.6656 ms/op 0.92
altair processBlock - 250000 vs - 7PWei normalcase hashState 15.568 ms/op 17.304 ms/op 0.90
altair processBlock - 250000 vs - 7PWei worstcase 19.856 ms/op 23.217 ms/op 0.86
altair processBlock - 250000 vs - 7PWei worstcase hashState 41.288 ms/op 44.666 ms/op 0.92
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3719 ms/op 1.4480 ms/op 0.95
phase0 processBlock - 250000 vs - 7PWei worstcase 16.274 ms/op 18.563 ms/op 0.88
altair processEth1Data - 250000 vs - 7PWei normalcase 277.86 us/op 295.68 us/op 0.94
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.0790 us/op 4.8060 us/op 0.64
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 19.128 us/op 22.411 us/op 0.85
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.2150 us/op 6.5290 us/op 0.80
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.2170 us/op 3.5970 us/op 0.89
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 80.436 us/op 95.605 us/op 0.84
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3062 ms/op 1.3912 ms/op 0.94
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.7106 ms/op 1.8882 ms/op 0.91
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.6875 ms/op 2.0611 ms/op 0.82
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.5332 ms/op 4.6547 ms/op 0.76
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 1.9323 ms/op 2.4420 ms/op 0.79
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.8466 ms/op 5.5234 ms/op 0.70
Tree 40 250000 create 309.65 ms/op 373.08 ms/op 0.83
Tree 40 250000 get(125000) 86.658 ns/op 93.936 ns/op 0.92
Tree 40 250000 set(125000) 960.70 ns/op 1.2366 us/op 0.78
Tree 40 250000 toArray() 11.023 ms/op 22.739 ms/op 0.48
Tree 40 250000 iterate all - toArray() + loop 10.816 ms/op 21.597 ms/op 0.50
Tree 40 250000 iterate all - get(i) 35.714 ms/op 43.778 ms/op 0.82
Array 250000 create 2.0247 ms/op 2.3962 ms/op 0.84
Array 250000 clone - spread 626.21 us/op 748.31 us/op 0.84
Array 250000 get(125000) 0.27700 ns/op 0.30300 ns/op 0.91
Array 250000 set(125000) 0.28100 ns/op 0.30300 ns/op 0.93
Array 250000 iterate all - loop 54.621 us/op 58.726 us/op 0.93
phase0 afterProcessEpoch - 250000 vs - 7PWei 38.086 ms/op 39.730 ms/op 0.96
Array.fill - length 1000000 2.0198 ms/op 2.2044 ms/op 0.92
Array push - length 1000000 8.5604 ms/op 10.594 ms/op 0.81
Array.get 0.19665 ns/op 0.21668 ns/op 0.91
Uint8Array.get 0.21921 ns/op 0.24626 ns/op 0.89
phase0 beforeProcessEpoch - 250000 vs - 7PWei 13.968 ms/op 22.677 ms/op 0.62
altair processEpoch - mainnet_e81889 259.68 ms/op 326.33 ms/op 0.80
mainnet_e81889 - altair beforeProcessEpoch 14.587 ms/op 46.306 ms/op 0.32
mainnet_e81889 - altair processJustificationAndFinalization 5.5500 us/op 7.4960 us/op 0.74
mainnet_e81889 - altair processInactivityUpdates 3.2285 ms/op 8.0634 ms/op 0.40
mainnet_e81889 - altair processRewardsAndPenalties 18.243 ms/op 22.556 ms/op 0.81
mainnet_e81889 - altair processRegistryUpdates 511.00 ns/op 609.00 ns/op 0.84
mainnet_e81889 - altair processSlashings 129.00 ns/op 137.00 ns/op 0.94
mainnet_e81889 - altair processEth1DataReset 126.00 ns/op 138.00 ns/op 0.91
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.6158 ms/op 7.6145 ms/op 0.21
mainnet_e81889 - altair processSlashingsReset 670.00 ns/op 769.00 ns/op 0.87
mainnet_e81889 - altair processRandaoMixesReset 1.1470 us/op 1.6310 us/op 0.70
mainnet_e81889 - altair processHistoricalRootsUpdate 127.00 ns/op 139.00 ns/op 0.91
mainnet_e81889 - altair processParticipationFlagUpdates 408.00 ns/op 522.00 ns/op 0.78
mainnet_e81889 - altair processSyncCommitteeUpdates 102.00 ns/op 114.00 ns/op 0.89
mainnet_e81889 - altair afterProcessEpoch 41.380 ms/op 42.930 ms/op 0.96
capella processEpoch - mainnet_e217614 753.21 ms/op 883.70 ms/op 0.85
mainnet_e217614 - capella beforeProcessEpoch 53.390 ms/op 62.156 ms/op 0.86
mainnet_e217614 - capella processJustificationAndFinalization 6.0280 us/op 6.7050 us/op 0.90
mainnet_e217614 - capella processInactivityUpdates 13.505 ms/op 15.007 ms/op 0.90
mainnet_e217614 - capella processRewardsAndPenalties 84.501 ms/op 92.188 ms/op 0.92
mainnet_e217614 - capella processRegistryUpdates 4.2840 us/op 4.6520 us/op 0.92
mainnet_e217614 - capella processSlashings 126.00 ns/op 134.00 ns/op 0.94
mainnet_e217614 - capella processEth1DataReset 125.00 ns/op 133.00 ns/op 0.94
mainnet_e217614 - capella processEffectiveBalanceUpdates 14.940 ms/op 19.639 ms/op 0.76
mainnet_e217614 - capella processSlashingsReset 653.00 ns/op 707.00 ns/op 0.92
mainnet_e217614 - capella processRandaoMixesReset 1.2440 us/op 1.4100 us/op 0.88
mainnet_e217614 - capella processHistoricalRootsUpdate 126.00 ns/op 137.00 ns/op 0.92
mainnet_e217614 - capella processParticipationFlagUpdates 419.00 ns/op 450.00 ns/op 0.93
mainnet_e217614 - capella afterProcessEpoch 101.86 ms/op 111.52 ms/op 0.91
phase0 processEpoch - mainnet_e58758 313.94 ms/op 333.50 ms/op 0.94
mainnet_e58758 - phase0 beforeProcessEpoch 63.639 ms/op 90.479 ms/op 0.70
mainnet_e58758 - phase0 processJustificationAndFinalization 6.2840 us/op 7.0690 us/op 0.89
mainnet_e58758 - phase0 processRewardsAndPenalties 15.466 ms/op 17.449 ms/op 0.89
mainnet_e58758 - phase0 processRegistryUpdates 2.1470 us/op 2.3870 us/op 0.90
mainnet_e58758 - phase0 processSlashings 127.00 ns/op 135.00 ns/op 0.94
mainnet_e58758 - phase0 processEth1DataReset 211.00 ns/op 132.00 ns/op 1.60
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1626 ms/op 1.1528 ms/op 1.01
mainnet_e58758 - phase0 processSlashingsReset 845.00 ns/op 983.00 ns/op 0.86
mainnet_e58758 - phase0 processRandaoMixesReset 1.1510 us/op 1.4670 us/op 0.78
mainnet_e58758 - phase0 processHistoricalRootsUpdate 131.00 ns/op 136.00 ns/op 0.96
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.0800 us/op 1.3610 us/op 0.79
mainnet_e58758 - phase0 afterProcessEpoch 32.163 ms/op 34.387 ms/op 0.94
phase0 processEffectiveBalanceUpdates - 250000 normalcase 952.44 us/op 1.0286 ms/op 0.93
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 2.5049 ms/op 1.5968 ms/op 1.57
altair processInactivityUpdates - 250000 normalcase 11.162 ms/op 13.512 ms/op 0.83
altair processInactivityUpdates - 250000 worstcase 10.907 ms/op 13.022 ms/op 0.84
phase0 processRegistryUpdates - 250000 normalcase 2.3460 us/op 2.5010 us/op 0.94
phase0 processRegistryUpdates - 250000 badcase_full_deposits 131.77 us/op 146.24 us/op 0.90
phase0 processRegistryUpdates - 250000 worstcase 0.5 57.523 ms/op 76.903 ms/op 0.75
altair processRewardsAndPenalties - 250000 normalcase 15.583 ms/op 16.425 ms/op 0.95
altair processRewardsAndPenalties - 250000 worstcase 15.243 ms/op 15.571 ms/op 0.98
phase0 getAttestationDeltas - 250000 normalcase 5.2093 ms/op 5.5228 ms/op 0.94
phase0 getAttestationDeltas - 250000 worstcase 5.0388 ms/op 5.7200 ms/op 0.88
phase0 processSlashings - 250000 worstcase 56.018 us/op 66.197 us/op 0.85
altair processSyncCommitteeUpdates - 250000 9.5847 ms/op 12.932 ms/op 0.74
BeaconState.hashTreeRoot - No change 162.00 ns/op 173.00 ns/op 0.94
BeaconState.hashTreeRoot - 1 full validator 81.293 us/op 85.562 us/op 0.95
BeaconState.hashTreeRoot - 32 full validator 841.63 us/op 1.0037 ms/op 0.84
BeaconState.hashTreeRoot - 512 full validator 8.0988 ms/op 9.5720 ms/op 0.85
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 97.536 us/op 118.92 us/op 0.82
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.3905 ms/op 1.6583 ms/op 0.84
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 17.803 ms/op 23.333 ms/op 0.76
BeaconState.hashTreeRoot - 1 balances 75.675 us/op 91.758 us/op 0.82
BeaconState.hashTreeRoot - 32 balances 723.19 us/op 819.91 us/op 0.88
BeaconState.hashTreeRoot - 512 balances 5.8333 ms/op 6.7511 ms/op 0.86
BeaconState.hashTreeRoot - 250000 balances 120.77 ms/op 150.60 ms/op 0.80
aggregationBits - 2048 els - zipIndexesInBitList 18.274 us/op 21.243 us/op 0.86
regular array get 100000 times 21.844 us/op 22.619 us/op 0.97
wrappedArray get 100000 times 22.066 us/op 22.987 us/op 0.96
arrayWithProxy get 100000 times 11.332 ms/op 9.7791 ms/op 1.16
ssz.Root.equals 20.136 ns/op 21.420 ns/op 0.94
byteArrayEquals 20.166 ns/op 21.603 ns/op 0.93
Buffer.compare 8.3380 ns/op 9.0280 ns/op 0.92
processSlot - 1 slots 9.7310 us/op 11.127 us/op 0.87
processSlot - 32 slots 1.9615 ms/op 2.3437 ms/op 0.84
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 4.4844 ms/op 4.4363 ms/op 1.01
getCommitteeAssignments - req 1 vs - 250000 vc 1.5812 ms/op 1.7161 ms/op 0.92
getCommitteeAssignments - req 100 vs - 250000 vc 3.2246 ms/op 3.4914 ms/op 0.92
getCommitteeAssignments - req 1000 vs - 250000 vc 3.4858 ms/op 3.7370 ms/op 0.93
findModifiedValidators - 10000 modified validators 704.90 ms/op 748.86 ms/op 0.94
findModifiedValidators - 1000 modified validators 432.59 ms/op 478.97 ms/op 0.90
findModifiedValidators - 100 modified validators 322.93 ms/op 303.68 ms/op 1.06
findModifiedValidators - 10 modified validators 144.64 ms/op 248.91 ms/op 0.58
findModifiedValidators - 1 modified validators 161.10 ms/op 183.92 ms/op 0.88
findModifiedValidators - no difference 159.98 ms/op 195.00 ms/op 0.82
migrate state 1500000 validators, 3400 modified, 2000 new 2.8691 s/op 3.0888 s/op 0.93
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.4600 ns/op 3.7700 ns/op 0.92
state getBlockRootAtSlot - 250000 vs - 7PWei 330.19 ns/op 419.79 ns/op 0.79
computeProposerIndex 100000 validators 1.2507 ms/op 1.3722 ms/op 0.91
getNextSyncCommitteeIndices 1000 validators 2.6885 ms/op 2.9130 ms/op 0.92
getNextSyncCommitteeIndices 10000 validators 23.722 ms/op 25.668 ms/op 0.92
getNextSyncCommitteeIndices 100000 validators 80.831 ms/op 85.798 ms/op 0.94
computeProposers - vc 250000 516.10 us/op 564.89 us/op 0.91
computeEpochShuffling - vc 250000 37.185 ms/op 39.305 ms/op 0.95
getNextSyncCommittee - vc 250000 8.7909 ms/op 9.7199 ms/op 0.90
nodejs block root to RootHex using toHex 83.986 ns/op 93.099 ns/op 0.90
nodejs block root to RootHex using toRootHex 48.328 ns/op 51.670 ns/op 0.94
nodejs fromHex(blob) 684.85 us/op 746.07 us/op 0.92
nodejs fromHexInto(blob) 588.10 us/op 620.50 us/op 0.95
nodejs block root to RootHex using the deprecated toHexString 331.03 ns/op 561.70 ns/op 0.59
nodejs byteArrayEquals 32 bytes (block root) 24.100 ns/op 26.084 ns/op 0.92
nodejs byteArrayEquals 48 bytes (pubkey) 35.308 ns/op 37.520 ns/op 0.94
nodejs byteArrayEquals 96 bytes (signature) 34.009 ns/op 34.023 ns/op 1.00
nodejs byteArrayEquals 1024 bytes 41.468 ns/op 41.083 ns/op 1.01
nodejs byteArrayEquals 131072 bytes (blob) 1.6466 us/op 1.7400 us/op 0.95
browser block root to RootHex using toHex 134.06 ns/op 142.55 ns/op 0.94
browser block root to RootHex using toRootHex 122.56 ns/op 130.72 ns/op 0.94
browser fromHex(blob) 1.4487 ms/op 1.5133 ms/op 0.96
browser fromHexInto(blob) 593.94 us/op 619.50 us/op 0.96
browser block root to RootHex using the deprecated toHexString 434.87 ns/op 363.12 ns/op 1.20
browser byteArrayEquals 32 bytes (block root) 26.589 ns/op 27.304 ns/op 0.97
browser byteArrayEquals 48 bytes (pubkey) 37.492 ns/op 38.586 ns/op 0.97
browser byteArrayEquals 96 bytes (signature) 69.880 ns/op 72.406 ns/op 0.97
browser byteArrayEquals 1024 bytes 700.53 ns/op 738.88 ns/op 0.95
browser byteArrayEquals 131072 bytes (blob) 88.714 us/op 92.051 us/op 0.96

by benchmarkbot/action

ensi321 and others added 2 commits May 25, 2026 19:32
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…al wrapper inputs

- publishExecutionPayloadEnvelope handler now checks supplied blob count
  matches bid.blobKzgCommitments before building data column sidecars
- writeReqJson/writeReqSsz throw when only one of blobs/kzgProofs is supplied

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ensi321
Copy link
Copy Markdown
Contributor Author

ensi321 commented May 26, 2026

@gemini-code-assist can you do another round of review please?

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 implements stateless publishing for the POST /eth/v1/beacon/execution_payload_envelope endpoint per beacon-APIs PR #580, allowing clients to supply blobs and kzgProofs alongside the envelope. It introduces the SignedExecutionPayloadEnvelopeContents wrapper, updates route serialization/deserialization logic for both JSON and SSZ formats, and implements the stateless publishing path in the beacon node API. Feedback focuses on optimizing SSZ deserialization by replacing a slow try-catch block with a deterministic offset check, and adding explicit validation to reject requests that supply only one of blobs or kzgProofs.

Comment thread packages/api/src/beacon/routes/beacon/block.ts Outdated
Comment thread packages/beacon-node/src/api/impl/beacon/blocks/index.ts
ensi321 and others added 3 commits May 25, 2026 20:21
…t partial blob inputs in handler

- parseReqSsz checks first 4 bytes (offset 12 = Contents, 100 = bare envelope)
  instead of try/catch deserialize for faster, deterministic dispatch
- publishExecutionPayloadEnvelope handler throws on partial blobs/kzgProofs
  so a client that supplies only one field fails fast instead of silently
  falling back to the stateful path

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
publishExecutionPayloadEnvelope handler now calls validateCellsAndKzgCommitments
against payloadInput.getBlobKzgCommitments() before building data column sidecars.
Previously only counts were checked, so malformed proofs could be added to local
state and gossiped. Crypto verification now matches the gossip ingest path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ensi321 ensi321 marked this pull request as ready for review May 26, 2026 05:22
@ensi321 ensi321 requested a review from a team as a code owner May 26, 2026 05:22
@codecov
Copy link
Copy Markdown

codecov Bot commented May 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.56%. Comparing base (fc6798b) to head (fd38e7c).
⚠️ Report is 161 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #9401      +/-   ##
============================================
+ Coverage     52.46%   52.56%   +0.09%     
============================================
  Files           848      848              
  Lines         62953    60937    -2016     
  Branches       4658     4485     -173     
============================================
- Hits          33030    32031     -999     
+ Misses        29855    28844    -1011     
+ Partials         68       62       -6     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd38e7c2be

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +696 to +698
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
if (!payloadInput) {
throw new ApiError(404, `PayloadEnvelopeInput not found for block root ${blockRootHex}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Defer payload cache lookup until after slot-alignment wait

Move the seenPayloadEnvelopeInputCache lookup back to after the msToBlockSlot sleep; doing it here makes publishExecutionPayloadEnvelope fail fast with 404 in near-slot submissions where the block/payload pipelines race by a few hundred milliseconds. In the previous ordering, the handler could wait up to MAX_API_CLOCK_DISPARITY_MS and then find the cache entry, but this change rejects those same requests before that grace window, which can break the new stateless external-builder path under normal timing jitter.

Useful? React with 👍 / 👎.

// sits after a 96-byte signature plus the 4-byte offset header, so its first
// offset is 100.
const firstOffset = body.length >= 4 ? body[0] | (body[1] << 8) | (body[2] << 16) | (body[3] << 24) : 0;
if (firstOffset === 12) {
Copy link
Copy Markdown
Member

@nflaig nflaig May 27, 2026

Choose a reason for hiding this comment

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

this doesn't make any sense, we need a metadata header to differentiate this, the spec is not complete, left a comment here ethereum/beacon-APIs#580 (comment), I think we should push for introducing a BlindedExecutionPayloadEnvelope for the stateful flow

Copy link
Copy Markdown
Contributor Author

@ensi321 ensi321 May 27, 2026

Choose a reason for hiding this comment

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

I agree. I believe other clients implemented this way. The original plan was we just need something transient so we can test external bids with buildoor in devnet 4 which is now dead.

@ensi321 ensi321 marked this pull request as draft May 28, 2026 19:49
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