Repository navigation
feat: reject gossip envelopes if payload does not match its block hash - #10490
nazarhussain wants to merge 15 commits into
Conversation
Add computeExecutionBlockHash to derive an execution payload's block hash from its header fields (RLP + keccak + ordered MPT roots), bellatrix through gloas. Needed for the envelope gossip block hash check in consensus-specs#5770. Move @ethereumjs/rlp to beacon-node dependencies at ^10.1.1, the version discv5 already ships. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Payloads built by the mock EL now carry their actual execution block hash instead of random bytes, so envelopes from it pass the upcoming gossip block hash check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hash Implements consensus-specs#5770: an execution payload envelope is rejected when its payload does not hash to payload.block_hash. Without this, a builder could gossip other contents under the committed hash and get the real envelope ignored. The check runs after the signature check so an unsigned envelope cannot make us hash its payload. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Wire the gossip_execution_payload_envelope handler into the gossip spec runner. Imported gloas blocks seed the envelope input cache, as the gossip block handler does, and valid envelopes are attached to it so duplicates are detected. Skip the valid and ignore_duplicate vectors for now: their envelopes carry a placeholder block hash, which the consensus-specs#5770 check rejects. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Hash with node:crypto keccak-256 (OpenSSL >= 3.2, bundled with Node 24.22.1) instead of the JS implementation from ethereum-cryptography, which beacon-node no longer depends on. The block hash check runs on the main thread in the envelope gossip path. Benchmarked block hash cost (noble -> native): - 300 txs x 400 B, 64 KiB BAL: 9.8 ms -> 1.2 ms - 1500 txs x 300 B, 512 KiB BAL: 57 ms -> 5.9 ms - 2000 txs x 2 KB, 4 MiB BAL: 373 ms -> 18.5 ms Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| /** Matched against `<handler>/<testCase>` */ | ||
| const skippedGossipTests: RegExp[] = [ | ||
| // Envelopes in these vectors carry a placeholder block hash, so the block hash check from | ||
| // consensus-specs#5770 rejects them. Unskip once the vectors are regenerated with real hashes. | ||
| /^gossip_execution_payload_envelope\/gossip_execution_payload_envelope__(valid.*|ignore_duplicate)$/, | ||
| ]; |
There was a problem hiding this comment.
should verify that spec vectors from ethereum/consensus-specs#5770
There was a problem hiding this comment.
All 21 gloas cases now pass on both mainnet and minimal, while the 7 remaining failures per preset are heze cases still using old pinned vectors with placeholder hashes.
| import {MockedBeaconChain, getMockedBeaconChain} from "../../../mocks/mockedBeaconChain.js"; | ||
| import {generateProtoBlock} from "../../../utils/typeGenerator.js"; | ||
|
|
||
| describe("validateGossipExecutionPayloadEnvelope", () => { |
There was a problem hiding this comment.
not sure about these, we wanna avoid custom gossip handler tests if it's covered by spec tests
There was a problem hiding this comment.
Removed the duplicate tests which are part of the specs vectors now.
Match the gossip block handler, which seeds the envelope input with the node's sampled and custody columns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
| Benchmark suite | Current: cc1046e | Previous: 122e5d5 | Ratio |
|---|---|---|---|
| enrSubnets - fastDeserialize 64 bits | 2.5890 us/op | 195.00 ns/op | 13.28 |
Full benchmark results
| Benchmark suite | Current: cc1046e | Previous: 122e5d5 | Ratio |
|---|---|---|---|
| getPubkeys - native cache - req 1000 vs - 250000 vc | 515.38 us/op | 807.66 us/op | 0.64 |
| getPubkeys - validatorsArr - req 1000 vs - 250000 vc | 32.804 us/op | 38.122 us/op | 0.86 |
| BLS verify - blst | 713.88 us/op | 896.97 us/op | 0.80 |
| BLS verifyMultipleSignatures 3 - blst | 1.0195 ms/op | 1.3712 ms/op | 0.74 |
| BLS verifyMultipleSignatures 8 - blst | 1.7492 ms/op | 2.2624 ms/op | 0.77 |
| BLS verifyMultipleSignatures 32 - blst | 5.6543 ms/op | 7.6231 ms/op | 0.74 |
| BLS verifyMultipleSignatures 64 - blst | 10.263 ms/op | 14.429 ms/op | 0.71 |
| BLS verifyMultipleSignatures 128 - blst | 19.913 ms/op | 27.490 ms/op | 0.72 |
| BLS deserializing 10000 signatures | 486.32 ms/op | 645.99 ms/op | 0.75 |
| BLS deserializing 100000 signatures | 4.9290 s/op | 6.5938 s/op | 0.75 |
| BLS verifyMultipleSignatures - same message - 3 - blst | 751.50 us/op | 988.54 us/op | 0.76 |
| BLS verifyMultipleSignatures - same message - 8 - blst | 850.48 us/op | 1.1180 ms/op | 0.76 |
| BLS verifyMultipleSignatures - same message - 32 - blst | 1.3084 ms/op | 1.7328 ms/op | 0.76 |
| BLS verifyMultipleSignatures - same message - 64 - blst | 1.9231 ms/op | 2.5962 ms/op | 0.74 |
| BLS verifyMultipleSignatures - same message - 128 - blst | 3.1321 ms/op | 4.3111 ms/op | 0.73 |
| BLS aggregatePubkeys 32 - blst | 14.639 us/op | 19.893 us/op | 0.74 |
| BLS aggregatePubkeys 128 - blst | 52.294 us/op | 71.412 us/op | 0.73 |
| getSlashingsAndExits - default max | 32.584 us/op | 55.346 us/op | 0.59 |
| getSlashingsAndExits - 2k | 330.38 us/op | 373.87 us/op | 0.88 |
| proposeBlockBody type=full, size=empty | 1.2556 ms/op | 676.11 us/op | 1.86 |
| isKnown best case - 1 super set check | 120.00 ns/op | 212.00 ns/op | 0.57 |
| isKnown normal case - 2 super set checks | 120.00 ns/op | 168.00 ns/op | 0.71 |
| isKnown worse case - 16 super set checks | 123.00 ns/op | 165.00 ns/op | 0.75 |
| validate api signedAggregateAndProof - struct | 1.1135 ms/op | 1.5714 ms/op | 0.71 |
| validate gossip signedAggregateAndProof - struct | 1.1086 ms/op | 1.5697 ms/op | 0.71 |
| batch validate gossip attestation - vc 640000 - chunk 32 | 99.415 us/op | 127.42 us/op | 0.78 |
| batch validate gossip attestation - vc 640000 - chunk 64 | 85.607 us/op | 107.04 us/op | 0.80 |
| batch validate gossip attestation - vc 640000 - chunk 128 | 70.495 us/op | 96.537 us/op | 0.73 |
| batch validate gossip attestation - vc 640000 - chunk 256 | 78.628 us/op | 98.612 us/op | 0.80 |
| block hash typical 300 txs x 400 B, BAL 64 KiB | 1.8328 ms/op | ||
| transactions trie typical 300 txs x 400 B, BAL 64 KiB | 1.5525 ms/op | ||
| block hash heavy 1500 txs x 300 B, BAL 512 KiB | 10.827 ms/op | ||
| transactions trie heavy 1500 txs x 300 B, BAL 512 KiB | 10.346 ms/op | ||
| block hash near gossip max 2000 txs x 2000 B, BAL 4096 KiB | 17.068 ms/op | ||
| transactions trie near gossip max 2000 txs x 2000 B, BAL 4096 KiB | 16.686 ms/op | ||
| bytes32 toHexString | 243.00 ns/op | 308.00 ns/op | 0.79 |
| bytes32 Buffer.toString(hex) | 127.00 ns/op | 168.00 ns/op | 0.76 |
| bytes32 Buffer.toString(hex) from Uint8Array | 198.00 ns/op | 242.00 ns/op | 0.82 |
| bytes32 Buffer.toString(hex) + 0x | 135.00 ns/op | 172.00 ns/op | 0.78 |
| Return object 10000 times | 0.19850 ns/op | 0.21440 ns/op | 0.93 |
| Throw Error 10000 times | 1.5096 us/op | 1.8064 us/op | 0.84 |
| toHex | 71.679 ns/op | 92.658 ns/op | 0.77 |
| Buffer.from | 65.633 ns/op | 84.934 ns/op | 0.77 |
| shared Buffer | 43.777 ns/op | 62.231 ns/op | 0.70 |
| fastMsgIdFn sha256 / 200 bytes | 1.2130 us/op | 1.4900 us/op | 0.81 |
| fastMsgIdFn h32 xxhash / 200 bytes | 135.00 ns/op | 163.00 ns/op | 0.83 |
| fastMsgIdFn h64 xxhash / 200 bytes | 164.00 ns/op | 228.00 ns/op | 0.72 |
| fastMsgIdFn sha256 / 1000 bytes | 3.7870 us/op | 4.8340 us/op | 0.78 |
| fastMsgIdFn h32 xxhash / 1000 bytes | 200.00 ns/op | 252.00 ns/op | 0.79 |
| fastMsgIdFn h64 xxhash / 1000 bytes | 202.00 ns/op | 258.00 ns/op | 0.78 |
| fastMsgIdFn sha256 / 10000 bytes | 33.222 us/op | 42.144 us/op | 0.79 |
| fastMsgIdFn h32 xxhash / 10000 bytes | 965.00 ns/op | 1.2970 us/op | 0.74 |
| fastMsgIdFn h64 xxhash / 10000 bytes | 608.00 ns/op | 863.00 ns/op | 0.70 |
| send data - 1000 256B messages | 3.8550 ms/op | 4.3122 ms/op | 0.89 |
| send data - 1000 512B messages | 5.2164 ms/op | 5.6396 ms/op | 0.92 |
| send data - 1000 1024B messages | 5.7230 ms/op | 6.3739 ms/op | 0.90 |
| send data - 1000 1200B messages | 6.4311 ms/op | 6.7465 ms/op | 0.95 |
| send data - 1000 2048B messages | 10.384 ms/op | 8.8112 ms/op | 1.18 |
| send data - 1000 4096B messages | 39.413 ms/op | 63.055 ms/op | 0.63 |
| send data - 1000 16384B messages | 294.39 ms/op | 443.43 ms/op | 0.66 |
| send data - 1000 65536B messages | 1.7839 s/op | 1.8176 s/op | 0.98 |
| enrSubnets - fastDeserialize 64 bits | 2.5890 us/op | 195.00 ns/op | 13.28 |
| enrSubnets - ssz BitVector 64 bits | 193.00 ns/op | 270.00 ns/op | 0.71 |
| enrSubnets - fastDeserialize 4 bits | 70.000 ns/op | 96.000 ns/op | 0.73 |
| enrSubnets - ssz BitVector 4 bits | 197.00 ns/op | 270.00 ns/op | 0.73 |
| prioritizePeers score -10:0 att 32-0.1 sync 2-0 | 168.97 us/op | 203.55 us/op | 0.83 |
| prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 | 184.33 us/op | 230.22 us/op | 0.80 |
| prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 | 259.84 us/op | 321.85 us/op | 0.81 |
| prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 | 462.58 us/op | 570.72 us/op | 0.81 |
| prioritizePeers score 0:0 att 64-1 sync 4-1 | 553.18 us/op | 689.68 us/op | 0.80 |
| archive index / 1 slot / archive root index lookup | 12.736 us/op | 22.026 us/op | 0.58 |
| archive index / 1 slot / archive block lookup, decode and hash | 2.0822 ms/op | 2.4668 ms/op | 0.84 |
| archive index / 1 slots / 1 columns / legacy | 15.598 us/op | 24.532 us/op | 0.64 |
| archive index / 1 slots / 1 columns / flat files | 89.411 us/op | 178.99 us/op | 0.50 |
| archive index / 1 slots / 8 columns / legacy | 73.682 us/op | 85.038 us/op | 0.87 |
| archive index / 1 slots / 8 columns / flat files | 209.49 us/op | 373.18 us/op | 0.56 |
| archive index / 1 slots / 128 columns / legacy | 7.5265 ms/op | 6.5972 ms/op | 1.14 |
| archive index / 1 slots / 128 columns / flat files | 8.1023 ms/op | 5.3466 ms/op | 1.52 |
| archive index / 8 slots / 1 columns / legacy | 131.88 us/op | 176.00 us/op | 0.75 |
| archive index / 8 slots / 1 columns / flat files | 703.85 us/op | 1.2968 ms/op | 0.54 |
| archive index / 8 slots / 8 columns / legacy | 997.39 us/op | 728.30 us/op | 1.37 |
| archive index / 8 slots / 8 columns / flat files | 2.1851 ms/op | 3.1389 ms/op | 0.70 |
| archive index / 8 slots / 128 columns / legacy | 71.603 ms/op | 101.89 ms/op | 0.70 |
| archive index / 8 slots / 128 columns / flat files | 276.61 ms/op | 253.17 ms/op | 1.09 |
| head state / 1 slot / archive root index lookup | 13.602 us/op | 21.614 us/op | 0.63 |
| head state / 1 slot / archive block lookup, decode and hash | 2.0811 ms/op | 2.5903 ms/op | 0.80 |
| head state / 1 slots / 1 columns / legacy | 15.027 us/op | 23.986 us/op | 0.63 |
| head state / 1 slots / 1 columns / flat files | 82.652 us/op | 142.31 us/op | 0.58 |
| head state / 1 slots / 8 columns / legacy | 62.541 us/op | 74.671 us/op | 0.84 |
| head state / 1 slots / 8 columns / flat files | 222.94 us/op | 348.67 us/op | 0.64 |
| head state / 1 slots / 128 columns / legacy | 12.779 ms/op | 4.4396 ms/op | 2.88 |
| head state / 1 slots / 128 columns / flat files | 2.8921 ms/op | 4.8143 ms/op | 0.60 |
| head state / 8 slots / 1 columns / legacy | 111.23 us/op | 188.30 us/op | 0.59 |
| head state / 8 slots / 1 columns / flat files | 610.90 us/op | 1.1064 ms/op | 0.55 |
| head state / 8 slots / 8 columns / legacy | 1.9796 ms/op | 601.94 us/op | 3.29 |
| head state / 8 slots / 8 columns / flat files | 1.6425 ms/op | 3.0103 ms/op | 0.55 |
| head state / 8 slots / 128 columns / legacy | 99.439 ms/op | 97.176 ms/op | 1.02 |
| head state / 8 slots / 128 columns / flat files | 250.65 ms/op | 206.57 ms/op | 1.21 |
| array of 16000 items push then shift | 768.15 ns/op | 1.2921 us/op | 0.59 |
| LinkedList of 16000 items push then shift | 5.0100 ns/op | 6.6760 ns/op | 0.75 |
| array of 16000 items push then pop | 43.842 ns/op | 77.721 ns/op | 0.56 |
| LinkedList of 16000 items push then pop | 4.7370 ns/op | 5.9110 ns/op | 0.80 |
| array of 24000 items push then shift | 1.0970 us/op | 1.9115 us/op | 0.57 |
| LinkedList of 24000 items push then shift | 5.7700 ns/op | 6.3430 ns/op | 0.91 |
| array of 24000 items push then pop | 65.693 ns/op | 106.69 ns/op | 0.62 |
| LinkedList of 24000 items push then pop | 4.8270 ns/op | 5.9030 ns/op | 0.82 |
| intersect bitArray bitLen 8 | 2.3980 ns/op | 3.8720 ns/op | 0.62 |
| intersect array and set length 8 | 22.658 ns/op | 31.489 ns/op | 0.72 |
| intersect bitArray bitLen 128 | 15.016 ns/op | 23.097 ns/op | 0.65 |
| intersect array and set length 128 | 378.77 ns/op | 519.19 ns/op | 0.73 |
| bitArray.getTrueBitIndexes() bitLen 128 | 854.00 ns/op | 902.00 ns/op | 0.95 |
| bitArray.getTrueBitIndexes() bitLen 248 | 1.4900 us/op | 1.6540 us/op | 0.90 |
| bitArray.getTrueBitIndexes() bitLen 512 | 3.2120 us/op | 3.4260 us/op | 0.94 |
| Full columns - reconstruct all 6 blobs | 136.59 us/op | 134.47 us/op | 1.02 |
| Full columns - reconstruct half of the blobs out of 6 | 83.059 us/op | 72.545 us/op | 1.14 |
| Full columns - reconstruct single blob out of 6 | 30.414 us/op | 5.9686 ms/op | 0.01 |
| Half columns - reconstruct all 6 blobs | 306.23 ms/op | 390.98 ms/op | 0.78 |
| Half columns - reconstruct half of the blobs out of 6 | 155.30 ms/op | 194.52 ms/op | 0.80 |
| Half columns - reconstruct single blob out of 6 | 54.827 ms/op | 70.093 ms/op | 0.78 |
| Set add up to 64 items then delete first | 1.3501 us/op | 2.6432 us/op | 0.51 |
| OrderedSet add up to 64 items then delete first | 2.0008 us/op | 3.5339 us/op | 0.57 |
| Set add up to 64 items then delete last | 1.6419 us/op | 3.0994 us/op | 0.53 |
| OrderedSet add up to 64 items then delete last | 2.6010 us/op | 3.9720 us/op | 0.65 |
| Set add up to 64 items then delete middle | 1.6131 us/op | 3.0212 us/op | 0.53 |
| OrderedSet add up to 64 items then delete middle | 3.3128 us/op | 5.3388 us/op | 0.62 |
| Set add up to 128 items then delete first | 4.0999 us/op | 5.7509 us/op | 0.71 |
| OrderedSet add up to 128 items then delete first | 6.2173 us/op | 7.6103 us/op | 0.82 |
| Set add up to 128 items then delete last | 4.0539 us/op | 5.9917 us/op | 0.68 |
| OrderedSet add up to 128 items then delete last | 5.8460 us/op | 7.8187 us/op | 0.75 |
| Set add up to 128 items then delete middle | 4.2849 us/op | 5.7897 us/op | 0.74 |
| OrderedSet add up to 128 items then delete middle | 9.8908 us/op | 13.269 us/op | 0.75 |
| Set add up to 256 items then delete first | 7.8519 us/op | 11.570 us/op | 0.68 |
| OrderedSet add up to 256 items then delete first | 12.624 us/op | 15.578 us/op | 0.81 |
| Set add up to 256 items then delete last | 7.6112 us/op | 12.174 us/op | 0.63 |
| OrderedSet add up to 256 items then delete last | 11.818 us/op | 15.851 us/op | 0.75 |
| Set add up to 256 items then delete middle | 8.2451 us/op | 11.681 us/op | 0.71 |
| OrderedSet add up to 256 items then delete middle | 29.572 us/op | 38.261 us/op | 0.77 |
| runFastConfirmationRules vc:100000 bc:96 eq:0 | 2.8870 ms/op | 3.9941 ms/op | 0.72 |
| runFastConfirmationRules vc:600000 bc:96 eq:0 | 26.654 ms/op | 32.788 ms/op | 0.81 |
| runFastConfirmationRules vc:1000000 bc:96 eq:0 | 44.297 ms/op | 60.566 ms/op | 0.73 |
| runFastConfirmationRules vc:600000 bc:320 eq:0 | 25.332 ms/op | 32.018 ms/op | 0.79 |
| runFastConfirmationRules vc:100000 bc:96 eq:1000 | 1.0817 s/op | 1.2482 s/op | 0.87 |
| pass gossip attestations to forkchoice per slot | 3.5284 ms/op | 4.6147 ms/op | 0.76 |
| forkChoice updateHead vc 100000 bc 64 eq 0 | 350.78 us/op | 479.22 us/op | 0.73 |
| forkChoice updateHead vc 600000 bc 64 eq 0 | 1.8279 ms/op | 2.9212 ms/op | 0.63 |
| forkChoice updateHead vc 1000000 bc 64 eq 0 | 3.0750 ms/op | 4.7361 ms/op | 0.65 |
| forkChoice updateHead vc 600000 bc 320 eq 0 | 1.8496 ms/op | 2.4321 ms/op | 0.76 |
| forkChoice updateHead vc 600000 bc 1200 eq 0 | 1.8756 ms/op | 2.5159 ms/op | 0.75 |
| forkChoice updateHead vc 600000 bc 7200 eq 0 | 2.3682 ms/op | 3.4465 ms/op | 0.69 |
| forkChoice updateHead vc 600000 bc 64 eq 1000 | 1.9246 ms/op | 2.9391 ms/op | 0.65 |
| forkChoice updateHead vc 600000 bc 64 eq 10000 | 2.0464 ms/op | 3.0761 ms/op | 0.67 |
| forkChoice updateHead vc 600000 bc 64 eq 300000 | 5.4823 ms/op | 7.4317 ms/op | 0.74 |
| forkChoice updateHead vc 600000 bc 64 eq 0 gloas boosted | 2.2398 ms/op | 3.4101 ms/op | 0.66 |
| computeDeltas 1400000 validators 0% inactive | 8.3714 ms/op | 12.337 ms/op | 0.68 |
| computeDeltas 1400000 validators 10% inactive | 7.9312 ms/op | 11.865 ms/op | 0.67 |
| computeDeltas 1400000 validators 20% inactive | 7.5264 ms/op | 11.905 ms/op | 0.63 |
| computeDeltas 1400000 validators 50% inactive | 6.1995 ms/op | 9.7083 ms/op | 0.64 |
| computeDeltas 2100000 validators 0% inactive | 11.345 ms/op | 19.916 ms/op | 0.57 |
| computeDeltas 2100000 validators 10% inactive | 10.368 ms/op | 18.960 ms/op | 0.55 |
| computeDeltas 2100000 validators 20% inactive | 9.6595 ms/op | 17.900 ms/op | 0.54 |
| computeDeltas 2100000 validators 50% inactive | 7.8227 ms/op | 14.520 ms/op | 0.54 |
| altair processAttestation - 250000 vs - 7PWei normalcase | 12.466 ms/op | 6.2833 ms/op | 1.98 |
| altair processAttestation - 250000 vs - 7PWei worstcase | 14.664 ms/op | 9.1300 ms/op | 1.61 |
| altair processAttestation - setStatus - 1/6 committees join | 71.406 us/op | 122.92 us/op | 0.58 |
| altair processAttestation - setStatus - 1/3 committees join | 142.06 us/op | 243.12 us/op | 0.58 |
| altair processAttestation - setStatus - 1/2 committees join | 196.45 us/op | 321.72 us/op | 0.61 |
| altair processAttestation - setStatus - 2/3 committees join | 257.87 us/op | 392.17 us/op | 0.66 |
| altair processAttestation - setStatus - 4/5 committees join | 361.01 us/op | 593.55 us/op | 0.61 |
| altair processAttestation - setStatus - 100% committees join | 436.47 us/op | 674.04 us/op | 0.65 |
| altair processBlock - 250000 vs - 7PWei normalcase | 13.826 ms/op | 8.7666 ms/op | 1.58 |
| altair processBlock - 250000 vs - 7PWei normalcase hashState | 35.270 ms/op | 31.173 ms/op | 1.13 |
| altair processBlock - 250000 vs - 7PWei worstcase | 33.238 ms/op | 35.597 ms/op | 0.93 |
| altair processBlock - 250000 vs - 7PWei worstcase hashState | 68.351 ms/op | 60.647 ms/op | 1.13 |
| phase0 processBlock - 250000 vs - 7PWei normalcase | 5.4957 ms/op | 4.0078 ms/op | 1.37 |
| phase0 processBlock - 250000 vs - 7PWei worstcase | 50.221 ms/op | 39.028 ms/op | 1.29 |
| altair processEth1Data - 250000 vs - 7PWei normalcase | 222.40 us/op | 296.95 us/op | 0.75 |
| getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 | 2.3520 us/op | 3.3450 us/op | 0.70 |
| getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 | 16.637 us/op | 18.762 us/op | 0.89 |
| getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 | 4.3920 us/op | 5.6810 us/op | 0.77 |
| getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 | 2.5320 us/op | 3.6560 us/op | 0.69 |
| getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 | 77.100 us/op | 101.96 us/op | 0.76 |
| getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 | 1.0425 ms/op | 1.3593 ms/op | 0.77 |
| getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 | 1.3967 ms/op | 1.7587 ms/op | 0.79 |
| getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 | 1.5104 ms/op | 1.8238 ms/op | 0.83 |
| getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 | 2.8099 ms/op | 3.7509 ms/op | 0.75 |
| getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 | 1.5741 ms/op | 2.0073 ms/op | 0.78 |
| getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 | 4.6769 ms/op | 4.0238 ms/op | 1.16 |
| Tree 40 250000 create | 287.68 ms/op | 370.45 ms/op | 0.78 |
| Tree 40 250000 get(125000) | 71.513 ns/op | 106.14 ns/op | 0.67 |
| Tree 40 250000 set(125000) | 786.22 ns/op | 1.0760 us/op | 0.73 |
| Tree 40 250000 toArray() | 19.497 ms/op | 9.9694 ms/op | 1.96 |
| Tree 40 250000 iterate all - toArray() + loop | 19.918 ms/op | 16.210 ms/op | 1.23 |
| Tree 40 250000 iterate all - get(i) | 36.470 ms/op | 38.465 ms/op | 0.95 |
| Array 250000 create | 1.5484 ms/op | 2.4805 ms/op | 0.62 |
| Array 250000 clone - spread | 361.11 us/op | 792.78 us/op | 0.46 |
| Array 250000 get(125000) | 0.26200 ns/op | 0.30400 ns/op | 0.86 |
| Array 250000 set(125000) | 0.26000 ns/op | 0.30700 ns/op | 0.85 |
| Array 250000 iterate all - loop | 46.567 us/op | 56.410 us/op | 0.83 |
| phase0 afterProcessEpoch - 250000 vs - 7PWei | 5.9023 ms/op | 16.535 ms/op | 0.36 |
| Array.fill - length 1000000 | 2.3236 ms/op | 4.0238 ms/op | 0.58 |
| Array push - length 1000000 | 9.2751 ms/op | 15.555 ms/op | 0.60 |
| Array.get | 0.18577 ns/op | 0.21025 ns/op | 0.88 |
| Uint8Array.get | 0.20347 ns/op | 0.23905 ns/op | 0.85 |
| phase0 beforeProcessEpoch - 250000 vs - 7PWei | 16.813 ms/op | 24.778 ms/op | 0.68 |
| altair processEpoch - mainnet_e81889 | 235.87 ms/op | 281.69 ms/op | 0.84 |
| mainnet_e81889 - altair beforeProcessEpoch | 31.963 ms/op | 38.651 ms/op | 0.83 |
| mainnet_e81889 - altair processJustificationAndFinalization | 5.2370 us/op | 6.5090 us/op | 0.80 |
| mainnet_e81889 - altair processInactivityUpdates | 6.3066 ms/op | 3.6030 ms/op | 1.75 |
| mainnet_e81889 - altair processRewardsAndPenalties | 18.529 ms/op | 23.980 ms/op | 0.77 |
| mainnet_e81889 - altair processRegistryUpdates | 450.00 ns/op | 621.00 ns/op | 0.72 |
| mainnet_e81889 - altair processSlashings | 114.00 ns/op | 143.00 ns/op | 0.80 |
| mainnet_e81889 - altair processEth1DataReset | 113.00 ns/op | 134.00 ns/op | 0.84 |
| mainnet_e81889 - altair processEffectiveBalanceUpdates | 1.2640 ms/op | 1.6432 ms/op | 0.77 |
| mainnet_e81889 - altair processSlashingsReset | 586.00 ns/op | 702.00 ns/op | 0.83 |
| mainnet_e81889 - altair processRandaoMixesReset | 883.00 ns/op | 1.0100 us/op | 0.87 |
| mainnet_e81889 - altair processHistoricalRootsUpdate | 114.00 ns/op | 138.00 ns/op | 0.83 |
| mainnet_e81889 - altair processParticipationFlagUpdates | 390.00 ns/op | 456.00 ns/op | 0.86 |
| mainnet_e81889 - altair processSyncCommitteeUpdates | 90.000 ns/op | 106.00 ns/op | 0.85 |
| mainnet_e81889 - altair afterProcessEpoch | 42.732 ms/op | 44.802 ms/op | 0.95 |
| capella processEpoch - mainnet_e217614 | 745.71 ms/op | 886.95 ms/op | 0.84 |
| mainnet_e217614 - capella beforeProcessEpoch | 73.487 ms/op | 82.294 ms/op | 0.89 |
| mainnet_e217614 - capella processJustificationAndFinalization | 5.8720 us/op | 34.066 ms/op | 0.00 |
| mainnet_e217614 - capella processInactivityUpdates | 14.039 ms/op | 17.960 ms/op | 0.78 |
| mainnet_e217614 - capella processRewardsAndPenalties | 91.555 ms/op | 109.02 ms/op | 0.84 |
| mainnet_e217614 - capella processRegistryUpdates | 3.6310 us/op | 4.8940 us/op | 0.74 |
| mainnet_e217614 - capella processSlashings | 112.00 ns/op | 143.00 ns/op | 0.78 |
| mainnet_e217614 - capella processEth1DataReset | 110.00 ns/op | 135.00 ns/op | 0.81 |
| mainnet_e217614 - capella processEffectiveBalanceUpdates | 10.948 ms/op | 22.405 ms/op | 0.49 |
| mainnet_e217614 - capella processSlashingsReset | 553.00 ns/op | 910.00 ns/op | 0.61 |
| mainnet_e217614 - capella processRandaoMixesReset | 959.00 ns/op | 1.1610 us/op | 0.83 |
| mainnet_e217614 - capella processHistoricalSummariesUpdate | 112.00 ns/op | 185.00 ns/op | 0.61 |
| mainnet_e217614 - capella processParticipationFlagUpdates | 366.00 ns/op | 534.00 ns/op | 0.69 |
| mainnet_e217614 - capella afterProcessEpoch | 29.293 ms/op | 58.566 ms/op | 0.50 |
| phase0 processEpoch - mainnet_e58758 | 239.80 ms/op | 252.93 ms/op | 0.95 |
| mainnet_e58758 - phase0 beforeProcessEpoch | 69.701 ms/op | 104.79 ms/op | 0.67 |
| mainnet_e58758 - phase0 processJustificationAndFinalization | 5.2610 us/op | 8.6700 us/op | 0.61 |
| mainnet_e58758 - phase0 processRewardsAndPenalties | 15.670 ms/op | 18.023 ms/op | 0.87 |
| mainnet_e58758 - phase0 processRegistryUpdates | 1.8050 us/op | 2.4910 us/op | 0.72 |
| mainnet_e58758 - phase0 processSlashings | 110.00 ns/op | 143.00 ns/op | 0.77 |
| mainnet_e58758 - phase0 processEth1DataReset | 105.00 ns/op | 137.00 ns/op | 0.77 |
| mainnet_e58758 - phase0 processEffectiveBalanceUpdates | 517.55 us/op | 855.50 us/op | 0.60 |
| mainnet_e58758 - phase0 processSlashingsReset | 689.00 ns/op | 1.1570 us/op | 0.60 |
| mainnet_e58758 - phase0 processRandaoMixesReset | 984.00 ns/op | 1.5090 us/op | 0.65 |
| mainnet_e58758 - phase0 processHistoricalRootsUpdate | 112.00 ns/op | 141.00 ns/op | 0.79 |
| mainnet_e58758 - phase0 processParticipationRecordUpdates | 914.00 ns/op | 1.3170 us/op | 0.69 |
| mainnet_e58758 - phase0 afterProcessEpoch | 5.9956 ms/op | 7.9249 ms/op | 0.76 |
| phase0 processEffectiveBalanceUpdates - 250000 normalcase | 674.10 us/op | 1.0496 ms/op | 0.64 |
| phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 | 851.51 us/op | 3.8619 ms/op | 0.22 |
| gloas processInactivityUpdates - 250000 inactivity leak all eligible missed target | 10.016 ms/op | 43.873 ms/op | 0.23 |
| phase0 processRegistryUpdates - 250000 normalcase | 1.5890 us/op | 2.9810 us/op | 0.53 |
| phase0 processRegistryUpdates - 250000 badcase_full_deposits | 114.69 us/op | 174.97 us/op | 0.66 |
| phase0 processRegistryUpdates - 250000 worstcase 0.5 | 56.239 ms/op | 91.012 ms/op | 0.62 |
| altair processRewardsAndPenalties - 250000 normalcase | 14.120 ms/op | 84.986 ms/op | 0.17 |
| altair processRewardsAndPenalties - 250000 worstcase | 13.113 ms/op | 17.396 ms/op | 0.75 |
| phase0 getAttestationDeltas - 250000 normalcase | 3.9156 ms/op | 5.9101 ms/op | 0.66 |
| phase0 getAttestationDeltas - 250000 worstcase | 3.3532 ms/op | 6.0207 ms/op | 0.56 |
| phase0 processSlashings - 250000 worstcase | 44.565 us/op | 68.047 us/op | 0.65 |
| altair processSyncCommitteeUpdates - 250000 | 7.4809 ms/op | 10.640 ms/op | 0.70 |
| BeaconState.hashTreeRoot - No change | 130.00 ns/op | 173.00 ns/op | 0.75 |
| BeaconState.hashTreeRoot - 1 full validator | 86.061 us/op | 117.22 us/op | 0.73 |
| BeaconState.hashTreeRoot - 32 full validator | 1.2112 ms/op | 1.4734 ms/op | 0.82 |
| BeaconState.hashTreeRoot - 512 full validator | 10.750 ms/op | 10.563 ms/op | 1.02 |
| BeaconState.hashTreeRoot - 1 validator.effectiveBalance | 89.015 us/op | 124.02 us/op | 0.72 |
| BeaconState.hashTreeRoot - 32 validator.effectiveBalance | 1.4047 ms/op | 2.3967 ms/op | 0.59 |
| BeaconState.hashTreeRoot - 512 validator.effectiveBalance | 23.923 ms/op | 35.370 ms/op | 0.68 |
| BeaconState.hashTreeRoot - 1 balances | 83.271 us/op | 109.09 us/op | 0.76 |
| BeaconState.hashTreeRoot - 32 balances | 773.28 us/op | 1.5003 ms/op | 0.52 |
| BeaconState.hashTreeRoot - 512 balances | 7.3249 ms/op | 10.064 ms/op | 0.73 |
| BeaconState.hashTreeRoot - 250000 balances | 138.85 ms/op | 217.01 ms/op | 0.64 |
| aggregationBits - 2048 els - zipIndexesInBitList | 15.489 us/op | 23.527 us/op | 0.66 |
| regular array get 100000 times | 19.327 us/op | 23.440 us/op | 0.82 |
| wrappedArray get 100000 times | 19.760 us/op | 23.564 us/op | 0.84 |
| arrayWithProxy get 100000 times | 7.4165 ms/op | 10.200 ms/op | 0.73 |
| ssz.Root.equals | 15.453 ns/op | 21.843 ns/op | 0.71 |
| byteArrayEquals | 15.122 ns/op | 21.649 ns/op | 0.70 |
| Buffer.compare | 6.4640 ns/op | 9.1460 ns/op | 0.71 |
| processSlot - 1 slots | 8.4900 us/op | 18.703 us/op | 0.45 |
| processSlot - 32 slots | 1.9648 ms/op | 2.8054 ms/op | 0.70 |
| getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei | 3.2629 ms/op | 9.5095 ms/op | 0.34 |
| getCommitteeAssignments - req 1 vs - 250000 vc | 1.4545 ms/op | 1.7448 ms/op | 0.83 |
| getCommitteeAssignments - req 100 vs - 250000 vc | 3.3838 ms/op | 3.5833 ms/op | 0.94 |
| getCommitteeAssignments - req 1000 vs - 250000 vc | 3.5395 ms/op | 3.8709 ms/op | 0.91 |
| findModifiedValidators - 10000 modified validators | 762.02 ms/op | 1.2604 s/op | 0.60 |
| findModifiedValidators - 1000 modified validators | 447.49 ms/op | 782.76 ms/op | 0.57 |
| findModifiedValidators - 100 modified validators | 311.58 ms/op | 568.20 ms/op | 0.55 |
| findModifiedValidators - 10 modified validators | 276.19 ms/op | 517.19 ms/op | 0.53 |
| findModifiedValidators - 1 modified validators | 242.32 ms/op | 404.72 ms/op | 0.60 |
| findModifiedValidators - no difference | 193.28 ms/op | 396.56 ms/op | 0.49 |
| migrate state 1500000 validators, 3400 modified, 2000 new | 3.1918 s/op | 3.7216 s/op | 0.86 |
| RootCache.getBlockRootAtSlot - 250000 vs - 7PWei | 2.5300 ns/op | 3.3600 ns/op | 0.75 |
| state getBlockRootAtSlot - 250000 vs - 7PWei | 750.37 ns/op | 547.85 ns/op | 1.37 |
| computeProposerIndex 100000 validators | 1.1084 ms/op | 1.2162 ms/op | 0.91 |
| getNextSyncCommitteeIndices 1000 validators | 135.10 us/op | 145.35 us/op | 0.93 |
| getNextSyncCommitteeIndices 10000 validators | 296.52 us/op | 339.88 us/op | 0.87 |
| getNextSyncCommitteeIndices 100000 validators | 1.9016 ms/op | 2.2339 ms/op | 0.85 |
| computeProposers - vc 250000 | 438.19 us/op | 452.49 us/op | 0.97 |
| computeEpochShuffling - vc 250000 | 13.298 ms/op | 42.156 ms/op | 0.32 |
| getNextSyncCommittee - vc 250000 | 6.9349 ms/op | 8.9338 ms/op | 0.78 |
| nodejs block root to RootHex using toHex | 68.135 ns/op | 89.094 ns/op | 0.76 |
| nodejs block root to RootHex using toRootHex | 42.744 ns/op | 58.070 ns/op | 0.74 |
| nodejs fromHex(blob) | 922.95 us/op | 1.0247 ms/op | 0.90 |
| nodejs fromHexInto(blob) | 53.332 us/op | 69.465 us/op | 0.77 |
| nodejs block root to RootHex using the deprecated toHexString | 503.05 ns/op | 587.72 ns/op | 0.86 |
| nodejs byteArrayEquals 32 bytes (block root) | 17.340 ns/op | 24.727 ns/op | 0.70 |
| nodejs byteArrayEquals 48 bytes (pubkey) | 24.918 ns/op | 35.965 ns/op | 0.69 |
| nodejs byteArrayEquals 96 bytes (signature) | 25.442 ns/op | 33.363 ns/op | 0.76 |
| nodejs byteArrayEquals 1024 bytes | 30.658 ns/op | 39.767 ns/op | 0.77 |
| nodejs byteArrayEquals 131072 bytes (blob) | 942.13 ns/op | 1.6772 us/op | 0.56 |
| browser block root to RootHex using toHex | 108.13 ns/op | 137.01 ns/op | 0.79 |
| browser block root to RootHex using toRootHex | 101.52 ns/op | 123.70 ns/op | 0.82 |
| browser fromHex(blob) | 1.5239 ms/op | 1.9428 ms/op | 0.78 |
| browser fromHexInto(blob) | 434.96 us/op | 626.00 us/op | 0.69 |
| browser block root to RootHex using the deprecated toHexString | 501.54 ns/op | 419.99 ns/op | 1.19 |
| browser byteArrayEquals 32 bytes (block root) | 18.066 ns/op | 27.519 ns/op | 0.66 |
| browser byteArrayEquals 48 bytes (pubkey) | 25.866 ns/op | 38.845 ns/op | 0.67 |
| browser byteArrayEquals 96 bytes (signature) | 48.425 ns/op | 73.018 ns/op | 0.66 |
| browser byteArrayEquals 1024 bytes | 497.00 ns/op | 739.23 ns/op | 0.67 |
| browser byteArrayEquals 131072 bytes (blob) | 62.327 us/op | 93.210 us/op | 0.67 |
by benchmarkbot/action
|
🤖 Validated and benchmarked Validation
Also ran the Sepolia block shape
Benchmark on the same payloads (ms per block) Measured on Apple M3 Pro (11 cores, 18 GiB), macOS 27.2, Node v24.22.1 (OpenSSL 3.5.9), single thread, mean of 5 runs per block.
Native keccak is about 10x faster on real blocks. The check adds about 0.5 ms per envelope to gossip validation, under 1.4 ms in the worst block. The transactions trie is about 73% of the remaining cost. |
those numbers look low. we should try to see what it looks like using 200m gas. Maybe plataberget has different numbers |
|
🤖 Same check on mainnet, which has larger blocks than Sepolia. 1000 consecutive finalized mainnet blocks (fulu, slots 15527190 to 15528192): computed hash matches Blocks: 238 txs and 166 KiB tx data on average, up to 2574 txs and 805 KiB (Sepolia: 97 txs, 58 KiB).
The transactions trie is ~92% of the cost; the max is the 2574-tx block. Mainnet has no BAL yet; native keccak adds ~1.4 ms per MiB of BAL. Apple M3 Pro, Node v24.22.1 (OpenSSL 3.5.9), single thread, mean of 5 runs per block. |
|
Maybe not worth optimizing, but also curious what napi bound |
|
I don't have any strong data, but my gut is I don't like anything 5ms or more on main thread if we can help it. |
|
🤖 Same check on Platåberget (glamsterdam-devnet-8, 200M gas limit): 1000 envelopes with ≥150M gas used (slots 333469 to 346206), all hashes match. At 200M gas the block access list (BAL) dominates, while txs stay small:
Largest block on the chain (slot 77962, 2394 txs, 542 KiB): 9.0 ms native, 42.7 ms with Apple M3 Pro, Node v24.22.1 (OpenSSL 3.5.9). |
|
🤖 Check order: block hash vs signature, following feedback on the spec PR. The signature check is not cheap: before the ~1 ms BLS verify it computes the envelope signing root, a
Apple M3 Pro, Node v24.22.1, single thread. |
Follow the spec order. The signature check computes the envelope signing root over every transaction, which costs more than the block hash, so rejecting a wrong hash first is the cheaper path. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An empty transaction is a deletion in the transactions trie, so trailing empty transactions do not change the block hash. A builder could sign such a variant, pass the block hash check and take the seen slot, while the EL rejects the payload. Reject it before hashing, as verify_and_notify_new_payload does. Add a gossip handler test that a signed variant is rejected and not marked seen, and the committed envelope is still accepted. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Plot p50, p90, p99 and mean of lodestar_gossip_execution_payload_envelope_block_hash_seconds in the Gossip Execution Payload Envelope Validation row of the networking dashboard. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Follow consensus-specs#5770 (5d3eb185): REJECT an envelope whose parent beacon block root differs from the block's parent root, whose prev_randao or gas_limit differ from the bid, or whose parent_hash, timestamp or withdrawals differ from the block's post-state. The post-state is now loaded before the block hash so these cheap checks reject a mismatched payload before it is hashed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| // An empty transaction is a deletion in the transactions trie, so trailing empty transactions would not change | ||
| // the block hash. The EL rejects such a payload, as `verify_and_notify_new_payload` does before hashing. | ||
| for (let i = 0; i < payload.transactions.length; i++) { | ||
| if (payload.transactions.getSize(i) === 0) { |
There was a problem hiding this comment.
is that part of the spec? I know that we added something similar for inclusion lists but I am skeptical why we need all of these checks on gossip here, in case of empty transactions, could we reject this already during serialization?
There was a problem hiding this comment.
Yes it's part of the specs now, added via ethereum/consensus-specs@a640e7e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
consensus-specs#5770 (5d3eb18) adds an optional payload field to meta.yaml blocks entries. Record that envelope as on_execution_payload_envelope would, so blocks building on the parent's full payload can be imported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Motivation
Implements consensus-specs#5770. Gossip only checked
payload.block_hash == bid.block_hash, so a builder could gossip other contents under the committed hash, take the seen entry and get the real envelope ignored.Draft until #5770 is released.
Description
Envelope gossip checks, new ones marked
+:computeExecutionBlockHash: keccak256 of the RLP header with MPT roots for transactions and withdrawals and the EIP-7685 requests hash. Bellatrix to heze.node:cryptokeccak.@ethereumjs/rlpis already installed via discv5.lodestar_gossip_execution_payload_envelope_block_hash_secondswith a dashboard panel.valid*andignore_duplicateare skipped until the pinned vectors get real hashes.Verification
sanity/blockspayloads (bellatrix to fulu) and 1000 real blocks each from Sepolia, mainnet and Platåberget (see comments). One Sepolia envelope is a test fixture.AI Assistance Disclosure
🤖 Generated with Claude Code