Skip to content

feat: implement epbs block production [wip]#8838

Draft
nflaig wants to merge 3 commits intounstablefrom
nflaig/epbs-block-production
Draft

feat: implement epbs block production [wip]#8838
nflaig wants to merge 3 commits intounstablefrom
nflaig/epbs-block-production

Conversation

@nflaig
Copy link
Member

@nflaig nflaig commented Feb 2, 2026

No description provided.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @nflaig, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces the foundational changes required to support block production for the upcoming 'Gloas' fork. The core change involves a shift from directly embedding execution payloads in beacon blocks to a system where beacon blocks contain bids, and the full execution payload is published in a separate envelope. This architectural change is crucial for enabling a more flexible and potentially more decentralized block building process, allowing for external builders to participate more effectively.

Highlights

  • Gloas Fork Block Production: Introduced a new block production mechanism specifically for the 'Gloas' fork, which changes how execution payloads are handled. Instead of including the full execution payload directly in the beacon block, proposers now submit an 'execution payload bid' within the beacon block.
  • New API Endpoints: Added three new API endpoints: 'publishExecutionPayloadEnvelope' (POST) to broadcast signed execution payload envelopes, 'produceBlockV4' (GET) for validators to request a block with an execution payload bid, and 'getExecutionPayloadEnvelope' (GET) to retrieve the full execution payload envelope.
  • Separated Execution Payload Envelope: The full execution payload, along with associated data columns (for PeerDAS), is now encapsulated in an 'execution payload envelope' which is published separately from the beacon block. This supports a builder-bid architecture.
  • Validator Signing for Envelopes: Implemented a new 'signExecutionPayloadEnvelope' method in the validator store, allowing validators to sign the execution payload envelope using the 'DOMAIN_BEACON_BUILDER' domain.
  • Data Column Handling for Gloas: Updated the logic for generating data column sidecars to specifically handle the 'Gloas' fork, where blobKzgCommitments are part of the ExecutionPayloadEnvelope rather than the BeaconBlockBody.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 introduces the initial implementation for enshrined proposer-builder separation (EPBS) through the new 'gloas' fork. It adds new API endpoints (produceBlockV4, getExecutionPayloadEnvelope, publishExecutionPayloadEnvelope) and modifies existing ones to support the new block production flow. The changes span across API definitions, beacon node implementation, and validator logic. The overall structure is sound, with a clear separation of concerns for the new gloas flow. However, as a work-in-progress, several critical components are marked with TODOs, such as signature verification for envelopes, state transition logic, and correct inclusion proof generation. I've provided specific feedback on a couple of areas: an incorrect type cast in the API route definitions and a logic improvement for retrieving cached block production data.

signedBlockContents as SignedBlockContents<ForkPostDeneb>
)
: sszTypesFor(fork).SignedBeaconBlock.toJson(
signedBlockContents.signedBlock as SignedBeaconBlock<ForkPreDeneb>
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The type cast as SignedBeaconBlock<ForkPreDeneb> is incorrect for the gloas fork. This else branch is executed for gloas and later forks, but SignedBeaconBlock<ForkPostGloas> has a different structure than SignedBeaconBlock<ForkPreDeneb>. This incorrect cast may lead to improper serialization or runtime errors.

A more appropriate type should be used to encompass all forks handled in this branch (pre-Deneb and post-Gloas forks). A simple as SignedBeaconBlock might be sufficient if the toJson method can correctly handle the generic type.

A similar incorrect cast is present in writeReqSsz on line 460.

Comment on lines 1621 to 1628
if (gloasResult.executionPayload && Number(gloasResult.executionPayload.timestamp) > 0) {
// For self-builds at this slot, this should be our produced block
cachedResult = {
blockRootHex,
...gloasResult,
};
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The condition to find the cached block production result is not specific enough. It checks if the timestamp is greater than 0, which is always true for a valid payload. This could lead to returning an incorrect cached result if multiple blocks are produced in quick succession and the cache contains entries for different slots.

To ensure the correct cached result is retrieved, you should compare the payload's timestamp with the expected timestamp for the requested slot.

          const expectedTimestamp = computeTimeAtSlot(config, slot, chain.genesisTime);
          if (gloasResult.executionPayload?.timestamp === BigInt(expectedTimestamp)) {
            // For self-builds at this slot, this should be our produced block
            cachedResult = {
              blockRootHex,
              ...gloasResult,
            };
            break;
          }

Comment on lines 521 to 522
type: SignableMessageType.BLOCK_V2, // TODO GLOAS: Add dedicated type for envelope signing
data: envelope as unknown as BeaconBlock,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

As noted in the TODO, reusing SignableMessageType.BLOCK_V2 is not ideal. More importantly, casting envelope to BeaconBlock is incorrect and could cause issues with remote signers that expect a BeaconBlock structure for this message type. A dedicated SignableMessageType for ExecutionPayloadEnvelope should be added to ensure type safety and correct handling by remote signers.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: cba3f1e Previous: 45b0426 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.2616 ms/op 888.72 us/op 1.42
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 38.176 us/op 34.559 us/op 1.10
BLS verify - blst 765.33 us/op 862.37 us/op 0.89
BLS verifyMultipleSignatures 3 - blst 1.1842 ms/op 1.2847 ms/op 0.92
BLS verifyMultipleSignatures 8 - blst 1.9202 ms/op 2.1370 ms/op 0.90
BLS verifyMultipleSignatures 32 - blst 5.4691 ms/op 4.5584 ms/op 1.20
BLS verifyMultipleSignatures 64 - blst 10.050 ms/op 8.6243 ms/op 1.17
BLS verifyMultipleSignatures 128 - blst 19.492 ms/op 16.488 ms/op 1.18
BLS deserializing 10000 signatures 746.20 ms/op 640.21 ms/op 1.17
BLS deserializing 100000 signatures 7.0086 s/op 6.1574 s/op 1.14
BLS verifyMultipleSignatures - same message - 3 - blst 835.59 us/op 932.23 us/op 0.90
BLS verifyMultipleSignatures - same message - 8 - blst 1.0088 ms/op 1.0582 ms/op 0.95
BLS verifyMultipleSignatures - same message - 32 - blst 1.7633 ms/op 1.6434 ms/op 1.07
BLS verifyMultipleSignatures - same message - 64 - blst 2.6033 ms/op 2.4603 ms/op 1.06
BLS verifyMultipleSignatures - same message - 128 - blst 4.3956 ms/op 4.0066 ms/op 1.10
BLS aggregatePubkeys 32 - blst 19.895 us/op 17.041 us/op 1.17
BLS aggregatePubkeys 128 - blst 70.620 us/op 60.115 us/op 1.17
getSlashingsAndExits - default max 68.681 us/op 42.188 us/op 1.63
getSlashingsAndExits - 2k 309.94 us/op 331.49 us/op 0.94
isKnown best case - 1 super set check 233.00 ns/op 399.00 ns/op 0.58
isKnown normal case - 2 super set checks 227.00 ns/op 403.00 ns/op 0.56
isKnown worse case - 16 super set checks 220.00 ns/op 406.00 ns/op 0.54
validate api signedAggregateAndProof - struct 1.3890 ms/op 1.9981 ms/op 0.70
validate gossip signedAggregateAndProof - struct 1.4877 ms/op 1.9152 ms/op 0.78
batch validate gossip attestation - vc 640000 - chunk 32 118.35 us/op 110.14 us/op 1.07
batch validate gossip attestation - vc 640000 - chunk 64 101.19 us/op 109.76 us/op 0.92
batch validate gossip attestation - vc 640000 - chunk 128 94.157 us/op 86.393 us/op 1.09
batch validate gossip attestation - vc 640000 - chunk 256 90.708 us/op 82.160 us/op 1.10
bytes32 toHexString 354.00 ns/op 513.00 ns/op 0.69
bytes32 Buffer.toString(hex) 236.00 ns/op 410.00 ns/op 0.58
bytes32 Buffer.toString(hex) from Uint8Array 337.00 ns/op 498.00 ns/op 0.68
bytes32 Buffer.toString(hex) + 0x 244.00 ns/op 413.00 ns/op 0.59
Return object 10000 times 0.23270 ns/op 0.23030 ns/op 1.01
Throw Error 10000 times 4.1097 us/op 3.3913 us/op 1.21
toHex 157.10 ns/op 91.026 ns/op 1.73
Buffer.from 131.46 ns/op 85.905 ns/op 1.53
shared Buffer 86.697 ns/op 57.925 ns/op 1.50
fastMsgIdFn sha256 / 200 bytes 1.9150 us/op 1.6250 us/op 1.18
fastMsgIdFn h32 xxhash / 200 bytes 190.00 ns/op 369.00 ns/op 0.51
fastMsgIdFn h64 xxhash / 200 bytes 262.00 ns/op 453.00 ns/op 0.58
fastMsgIdFn sha256 / 1000 bytes 6.1700 us/op 5.0480 us/op 1.22
fastMsgIdFn h32 xxhash / 1000 bytes 288.00 ns/op 463.00 ns/op 0.62
fastMsgIdFn h64 xxhash / 1000 bytes 316.00 ns/op 524.00 ns/op 0.60
fastMsgIdFn sha256 / 10000 bytes 55.514 us/op 39.975 us/op 1.39
fastMsgIdFn h32 xxhash / 10000 bytes 1.3840 us/op 1.4600 us/op 0.95
fastMsgIdFn h64 xxhash / 10000 bytes 896.00 ns/op 1.1050 us/op 0.81
send data - 1000 256B messages 14.005 ms/op 11.360 ms/op 1.23
send data - 1000 512B messages 15.901 ms/op 14.157 ms/op 1.12
send data - 1000 1024B messages 23.291 ms/op 16.975 ms/op 1.37
send data - 1000 1200B messages 23.044 ms/op 14.723 ms/op 1.57
send data - 1000 2048B messages 22.140 ms/op 16.212 ms/op 1.37
send data - 1000 4096B messages 28.390 ms/op 15.522 ms/op 1.83
send data - 1000 16384B messages 128.10 ms/op 103.41 ms/op 1.24
send data - 1000 65536B messages 264.05 ms/op 208.57 ms/op 1.27
enrSubnets - fastDeserialize 64 bits 1.0490 us/op 968.00 ns/op 1.08
enrSubnets - ssz BitVector 64 bits 383.00 ns/op 525.00 ns/op 0.73
enrSubnets - fastDeserialize 4 bits 126.00 ns/op 331.00 ns/op 0.38
enrSubnets - ssz BitVector 4 bits 341.00 ns/op 512.00 ns/op 0.67
prioritizePeers score -10:0 att 32-0.1 sync 2-0 248.48 us/op 253.27 us/op 0.98
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 302.26 us/op 288.97 us/op 1.05
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 421.60 us/op 341.18 us/op 1.24
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 738.73 us/op 604.66 us/op 1.22
prioritizePeers score 0:0 att 64-1 sync 4-1 910.98 us/op 1.0145 ms/op 0.90
array of 16000 items push then shift 1.6320 us/op 1.1757 us/op 1.39
LinkedList of 16000 items push then shift 7.5120 ns/op 7.3690 ns/op 1.02
array of 16000 items push then pop 77.043 ns/op 62.529 ns/op 1.23
LinkedList of 16000 items push then pop 7.2780 ns/op 6.0270 ns/op 1.21
array of 24000 items push then shift 2.4743 us/op 1.7619 us/op 1.40
LinkedList of 24000 items push then shift 7.8000 ns/op 7.0310 ns/op 1.11
array of 24000 items push then pop 111.65 ns/op 88.270 ns/op 1.26
LinkedList of 24000 items push then pop 7.5950 ns/op 6.0430 ns/op 1.26
intersect bitArray bitLen 8 6.1310 ns/op 4.6380 ns/op 1.32
intersect array and set length 8 35.410 ns/op 28.145 ns/op 1.26
intersect bitArray bitLen 128 27.463 ns/op 25.046 ns/op 1.10
intersect array and set length 128 580.24 ns/op 469.30 ns/op 1.24
bitArray.getTrueBitIndexes() bitLen 128 1.1740 us/op 1.1470 us/op 1.02
bitArray.getTrueBitIndexes() bitLen 248 1.9130 us/op 1.9490 us/op 0.98
bitArray.getTrueBitIndexes() bitLen 512 3.7860 us/op 3.7820 us/op 1.00
Full columns - reconstruct all 6 blobs 394.51 us/op 305.72 us/op 1.29
Full columns - reconstruct half of the blobs out of 6 106.05 us/op 93.762 us/op 1.13
Full columns - reconstruct single blob out of 6 31.045 us/op 30.040 us/op 1.03
Half columns - reconstruct all 6 blobs 284.45 ms/op 235.52 ms/op 1.21
Half columns - reconstruct half of the blobs out of 6 142.62 ms/op 119.02 ms/op 1.20
Half columns - reconstruct single blob out of 6 51.994 ms/op 44.629 ms/op 1.17
Full columns - reconstruct all 10 blobs 404.29 us/op 350.53 us/op 1.15
Full columns - reconstruct half of the blobs out of 10 191.98 us/op 202.27 us/op 0.95
Full columns - reconstruct single blob out of 10 50.118 us/op 35.998 us/op 1.39
Half columns - reconstruct all 10 blobs 475.91 ms/op 390.04 ms/op 1.22
Half columns - reconstruct half of the blobs out of 10 241.50 ms/op 198.50 ms/op 1.22
Half columns - reconstruct single blob out of 10 52.451 ms/op 44.291 ms/op 1.18
Full columns - reconstruct all 20 blobs 836.75 us/op 657.74 us/op 1.27
Full columns - reconstruct half of the blobs out of 20 389.13 us/op 316.49 us/op 1.23
Full columns - reconstruct single blob out of 20 42.709 us/op 39.331 us/op 1.09
Half columns - reconstruct all 20 blobs 951.42 ms/op 779.82 ms/op 1.22
Half columns - reconstruct half of the blobs out of 20 479.74 ms/op 427.59 ms/op 1.12
Half columns - reconstruct single blob out of 20 52.593 ms/op 44.501 ms/op 1.18
Set add up to 64 items then delete first 2.1342 us/op 1.6665 us/op 1.28
OrderedSet add up to 64 items then delete first 3.3325 us/op 2.5391 us/op 1.31
Set add up to 64 items then delete last 2.5342 us/op 1.7757 us/op 1.43
OrderedSet add up to 64 items then delete last 3.8217 us/op 2.5973 us/op 1.47
Set add up to 64 items then delete middle 2.5445 us/op 1.7872 us/op 1.42
OrderedSet add up to 64 items then delete middle 5.2757 us/op 4.3565 us/op 1.21
Set add up to 128 items then delete first 5.1258 us/op 3.4357 us/op 1.49
OrderedSet add up to 128 items then delete first 7.2415 us/op 5.4183 us/op 1.34
Set add up to 128 items then delete last 4.9959 us/op 3.3265 us/op 1.50
OrderedSet add up to 128 items then delete last 7.5294 us/op 4.9802 us/op 1.51
Set add up to 128 items then delete middle 4.8635 us/op 3.3237 us/op 1.46
OrderedSet add up to 128 items then delete middle 14.171 us/op 10.675 us/op 1.33
Set add up to 256 items then delete first 10.526 us/op 6.7248 us/op 1.57
OrderedSet add up to 256 items then delete first 15.279 us/op 10.807 us/op 1.41
Set add up to 256 items then delete last 9.9417 us/op 6.7073 us/op 1.48
OrderedSet add up to 256 items then delete last 15.216 us/op 10.289 us/op 1.48
Set add up to 256 items then delete middle 9.8754 us/op 6.6128 us/op 1.49
OrderedSet add up to 256 items then delete middle 42.966 us/op 33.268 us/op 1.29
pass gossip attestations to forkchoice per slot 2.7274 ms/op 2.0377 ms/op 1.34
forkChoice updateHead vc 100000 bc 64 eq 0 559.75 us/op 366.75 us/op 1.53
forkChoice updateHead vc 600000 bc 64 eq 0 3.3474 ms/op 2.1864 ms/op 1.53
forkChoice updateHead vc 1000000 bc 64 eq 0 5.5480 ms/op 3.6375 ms/op 1.53
forkChoice updateHead vc 600000 bc 320 eq 0 3.3240 ms/op 2.1948 ms/op 1.51
forkChoice updateHead vc 600000 bc 1200 eq 0 3.3555 ms/op 2.2232 ms/op 1.51
forkChoice updateHead vc 600000 bc 7200 eq 0 3.8606 ms/op 2.8417 ms/op 1.36
forkChoice updateHead vc 600000 bc 64 eq 1000 3.7871 ms/op 3.0663 ms/op 1.24
forkChoice updateHead vc 600000 bc 64 eq 10000 3.8851 ms/op 4.3265 ms/op 0.90
forkChoice updateHead vc 600000 bc 64 eq 300000 9.8005 ms/op 7.5716 ms/op 1.29
computeDeltas 1400000 validators 0% inactive 15.987 ms/op 13.056 ms/op 1.22
computeDeltas 1400000 validators 10% inactive 14.908 ms/op 12.305 ms/op 1.21
computeDeltas 1400000 validators 20% inactive 13.868 ms/op 11.850 ms/op 1.17
computeDeltas 1400000 validators 50% inactive 10.923 ms/op 8.4943 ms/op 1.29
computeDeltas 2100000 validators 0% inactive 24.203 ms/op 19.244 ms/op 1.26
computeDeltas 2100000 validators 10% inactive 22.923 ms/op 18.988 ms/op 1.21
computeDeltas 2100000 validators 20% inactive 20.970 ms/op 17.995 ms/op 1.17
computeDeltas 2100000 validators 50% inactive 16.409 ms/op 12.725 ms/op 1.29
altair processAttestation - 250000 vs - 7PWei normalcase 1.9378 ms/op 1.5922 ms/op 1.22
altair processAttestation - 250000 vs - 7PWei worstcase 2.9449 ms/op 2.2843 ms/op 1.29
altair processAttestation - setStatus - 1/6 committees join 128.26 us/op 95.170 us/op 1.35
altair processAttestation - setStatus - 1/3 committees join 236.58 us/op 172.88 us/op 1.37
altair processAttestation - setStatus - 1/2 committees join 335.41 us/op 235.34 us/op 1.43
altair processAttestation - setStatus - 2/3 committees join 446.33 us/op 311.39 us/op 1.43
altair processAttestation - setStatus - 4/5 committees join 585.88 us/op 488.35 us/op 1.20
altair processAttestation - setStatus - 100% committees join 725.43 us/op 568.92 us/op 1.28
altair processBlock - 250000 vs - 7PWei normalcase 3.4800 ms/op 3.0419 ms/op 1.14
altair processBlock - 250000 vs - 7PWei normalcase hashState 17.303 ms/op 14.028 ms/op 1.23
altair processBlock - 250000 vs - 7PWei worstcase 24.608 ms/op 22.491 ms/op 1.09
altair processBlock - 250000 vs - 7PWei worstcase hashState 59.926 ms/op 53.369 ms/op 1.12
phase0 processBlock - 250000 vs - 7PWei normalcase 1.5949 ms/op 1.5764 ms/op 1.01
phase0 processBlock - 250000 vs - 7PWei worstcase 19.793 ms/op 19.693 ms/op 1.01
altair processEth1Data - 250000 vs - 7PWei normalcase 382.10 us/op 295.61 us/op 1.29
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 5.7730 us/op 5.4370 us/op 1.06
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 33.792 us/op 42.736 us/op 0.79
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 9.9950 us/op 13.245 us/op 0.75
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 6.9180 us/op 3.8980 us/op 1.77
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 139.52 us/op 148.38 us/op 0.94
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.8046 ms/op 1.2957 ms/op 1.39
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.4080 ms/op 1.7034 ms/op 1.41
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.4073 ms/op 1.8220 ms/op 1.32
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.8369 ms/op 3.8822 ms/op 1.25
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.9155 ms/op 2.1550 ms/op 1.35
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 5.1229 ms/op 4.2066 ms/op 1.22
Tree 40 250000 create 370.74 ms/op 326.49 ms/op 1.14
Tree 40 250000 get(125000) 130.00 ns/op 87.878 ns/op 1.48
Tree 40 250000 set(125000) 1.2745 us/op 939.27 ns/op 1.36
Tree 40 250000 toArray() 13.400 ms/op 10.760 ms/op 1.25
Tree 40 250000 iterate all - toArray() + loop 12.578 ms/op 10.692 ms/op 1.18
Tree 40 250000 iterate all - get(i) 43.615 ms/op 32.107 ms/op 1.36
Array 250000 create 2.4441 ms/op 1.9959 ms/op 1.22
Array 250000 clone - spread 797.70 us/op 615.17 us/op 1.30
Array 250000 get(125000) 0.34700 ns/op 0.51800 ns/op 0.67
Array 250000 set(125000) 0.39400 ns/op 0.52400 ns/op 0.75
Array 250000 iterate all - loop 67.647 us/op 56.656 us/op 1.19
phase0 afterProcessEpoch - 250000 vs - 7PWei 41.952 ms/op 38.487 ms/op 1.09
Array.fill - length 1000000 2.7947 ms/op 1.8497 ms/op 1.51
Array push - length 1000000 9.3136 ms/op 6.7931 ms/op 1.37
Array.get 0.22076 ns/op 0.19625 ns/op 1.12
Uint8Array.get 0.24952 ns/op 0.19954 ns/op 1.25
phase0 beforeProcessEpoch - 250000 vs - 7PWei 16.172 ms/op 11.055 ms/op 1.46
altair processEpoch - mainnet_e81889 261.24 ms/op 233.82 ms/op 1.12
mainnet_e81889 - altair beforeProcessEpoch 18.378 ms/op 13.749 ms/op 1.34
mainnet_e81889 - altair processJustificationAndFinalization 6.0450 us/op 5.3560 us/op 1.13
mainnet_e81889 - altair processInactivityUpdates 4.1723 ms/op 3.3345 ms/op 1.25
mainnet_e81889 - altair processRewardsAndPenalties 20.701 ms/op 21.215 ms/op 0.98
mainnet_e81889 - altair processRegistryUpdates 681.00 ns/op 785.00 ns/op 0.87
mainnet_e81889 - altair processSlashings 167.00 ns/op 359.00 ns/op 0.47
mainnet_e81889 - altair processEth1DataReset 166.00 ns/op 356.00 ns/op 0.47
mainnet_e81889 - altair processEffectiveBalanceUpdates 2.0689 ms/op 1.6296 ms/op 1.27
mainnet_e81889 - altair processSlashingsReset 858.00 ns/op 946.00 ns/op 0.91
mainnet_e81889 - altair processRandaoMixesReset 1.1140 us/op 1.3000 us/op 0.86
mainnet_e81889 - altair processHistoricalRootsUpdate 168.00 ns/op 365.00 ns/op 0.46
mainnet_e81889 - altair processParticipationFlagUpdates 528.00 ns/op 694.00 ns/op 0.76
mainnet_e81889 - altair processSyncCommitteeUpdates 132.00 ns/op 337.00 ns/op 0.39
mainnet_e81889 - altair afterProcessEpoch 45.511 ms/op 41.612 ms/op 1.09
capella processEpoch - mainnet_e217614 858.22 ms/op 742.60 ms/op 1.16
mainnet_e217614 - capella beforeProcessEpoch 75.984 ms/op 53.538 ms/op 1.42
mainnet_e217614 - capella processJustificationAndFinalization 5.9030 us/op 5.0310 us/op 1.17
mainnet_e217614 - capella processInactivityUpdates 15.661 ms/op 10.792 ms/op 1.45
mainnet_e217614 - capella processRewardsAndPenalties 102.06 ms/op 97.502 ms/op 1.05
mainnet_e217614 - capella processRegistryUpdates 6.1570 us/op 4.8390 us/op 1.27
mainnet_e217614 - capella processSlashings 145.00 ns/op 383.00 ns/op 0.38
mainnet_e217614 - capella processEth1DataReset 186.00 ns/op 392.00 ns/op 0.47
mainnet_e217614 - capella processEffectiveBalanceUpdates 18.641 ms/op 5.4102 ms/op 3.45
mainnet_e217614 - capella processSlashingsReset 908.00 ns/op 1.1990 us/op 0.76
mainnet_e217614 - capella processRandaoMixesReset 1.1220 us/op 1.3360 us/op 0.84
mainnet_e217614 - capella processHistoricalRootsUpdate 168.00 ns/op 365.00 ns/op 0.46
mainnet_e217614 - capella processParticipationFlagUpdates 536.00 ns/op 680.00 ns/op 0.79
mainnet_e217614 - capella afterProcessEpoch 122.91 ms/op 106.39 ms/op 1.16
phase0 processEpoch - mainnet_e58758 233.34 ms/op 231.79 ms/op 1.01
mainnet_e58758 - phase0 beforeProcessEpoch 49.544 ms/op 48.574 ms/op 1.02
mainnet_e58758 - phase0 processJustificationAndFinalization 5.7700 us/op 5.0540 us/op 1.14
mainnet_e58758 - phase0 processRewardsAndPenalties 20.222 ms/op 17.978 ms/op 1.12
mainnet_e58758 - phase0 processRegistryUpdates 3.0370 us/op 2.4710 us/op 1.23
mainnet_e58758 - phase0 processSlashings 167.00 ns/op 379.00 ns/op 0.44
mainnet_e58758 - phase0 processEth1DataReset 170.00 ns/op 381.00 ns/op 0.45
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 952.25 us/op 879.92 us/op 1.08
mainnet_e58758 - phase0 processSlashingsReset 896.00 ns/op 1.0980 us/op 0.82
mainnet_e58758 - phase0 processRandaoMixesReset 1.1510 us/op 1.2010 us/op 0.96
mainnet_e58758 - phase0 processHistoricalRootsUpdate 179.00 ns/op 443.00 ns/op 0.40
mainnet_e58758 - phase0 processParticipationRecordUpdates 880.00 ns/op 1.4440 us/op 0.61
mainnet_e58758 - phase0 afterProcessEpoch 36.656 ms/op 31.875 ms/op 1.15
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.7425 ms/op 1.4895 ms/op 1.17
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 2.0972 ms/op 1.5946 ms/op 1.32
altair processInactivityUpdates - 250000 normalcase 13.878 ms/op 10.400 ms/op 1.33
altair processInactivityUpdates - 250000 worstcase 12.599 ms/op 9.6307 ms/op 1.31
phase0 processRegistryUpdates - 250000 normalcase 4.4740 us/op 4.1790 us/op 1.07
phase0 processRegistryUpdates - 250000 badcase_full_deposits 192.39 us/op 243.01 us/op 0.79
phase0 processRegistryUpdates - 250000 worstcase 0.5 72.520 ms/op 63.454 ms/op 1.14
altair processRewardsAndPenalties - 250000 normalcase 17.676 ms/op 13.135 ms/op 1.35
altair processRewardsAndPenalties - 250000 worstcase 16.984 ms/op 18.518 ms/op 0.92
phase0 getAttestationDeltas - 250000 normalcase 7.3918 ms/op 5.5053 ms/op 1.34
phase0 getAttestationDeltas - 250000 worstcase 7.2865 ms/op 5.0546 ms/op 1.44
phase0 processSlashings - 250000 worstcase 79.914 us/op 92.116 us/op 0.87
altair processSyncCommitteeUpdates - 250000 11.574 ms/op 9.2651 ms/op 1.25
BeaconState.hashTreeRoot - No change 221.00 ns/op 415.00 ns/op 0.53
BeaconState.hashTreeRoot - 1 full validator 91.956 us/op 87.306 us/op 1.05
BeaconState.hashTreeRoot - 32 full validator 934.09 us/op 980.96 us/op 0.95
BeaconState.hashTreeRoot - 512 full validator 7.7069 ms/op 6.5908 ms/op 1.17
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 107.10 us/op 93.423 us/op 1.15
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.6608 ms/op 1.5169 ms/op 1.09
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 18.162 ms/op 13.433 ms/op 1.35
BeaconState.hashTreeRoot - 1 balances 86.420 us/op 73.836 us/op 1.17
BeaconState.hashTreeRoot - 32 balances 818.58 us/op 801.90 us/op 1.02
BeaconState.hashTreeRoot - 512 balances 6.3648 ms/op 4.8058 ms/op 1.32
BeaconState.hashTreeRoot - 250000 balances 127.58 ms/op 131.01 ms/op 0.97
aggregationBits - 2048 els - zipIndexesInBitList 21.499 us/op 19.233 us/op 1.12
regular array get 100000 times 25.366 us/op 22.771 us/op 1.11
wrappedArray get 100000 times 25.358 us/op 22.746 us/op 1.11
arrayWithProxy get 100000 times 15.037 ms/op 10.347 ms/op 1.45
ssz.Root.equals 24.786 ns/op 21.803 ns/op 1.14
byteArrayEquals 24.513 ns/op 25.063 ns/op 0.98
Buffer.compare 10.627 ns/op 9.1360 ns/op 1.16
processSlot - 1 slots 12.610 us/op 9.8770 us/op 1.28
processSlot - 32 slots 2.5471 ms/op 2.4299 ms/op 1.05
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 4.3379 ms/op 1.9564 ms/op 2.22
getCommitteeAssignments - req 1 vs - 250000 vc 1.9507 ms/op 1.6907 ms/op 1.15
getCommitteeAssignments - req 100 vs - 250000 vc 3.7908 ms/op 3.4355 ms/op 1.10
getCommitteeAssignments - req 1000 vs - 250000 vc 4.0245 ms/op 3.6835 ms/op 1.09
findModifiedValidators - 10000 modified validators 540.65 ms/op 407.46 ms/op 1.33
findModifiedValidators - 1000 modified validators 387.13 ms/op 328.56 ms/op 1.18
findModifiedValidators - 100 modified validators 274.69 ms/op 240.79 ms/op 1.14
findModifiedValidators - 10 modified validators 160.72 ms/op 196.03 ms/op 0.82
findModifiedValidators - 1 modified validators 131.80 ms/op 129.23 ms/op 1.02
findModifiedValidators - no difference 146.00 ms/op 103.89 ms/op 1.41
migrate state 1500000 validators, 3400 modified, 2000 new 1.0180 s/op 903.43 ms/op 1.13
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.6600 ns/op 5.8600 ns/op 0.80
state getBlockRootAtSlot - 250000 vs - 7PWei 614.00 ns/op 481.21 ns/op 1.28
computeProposerIndex 100000 validators 1.5440 ms/op 1.3262 ms/op 1.16
getNextSyncCommitteeIndices 1000 validators 112.98 ms/op 98.630 ms/op 1.15
getNextSyncCommitteeIndices 10000 validators 112.87 ms/op 98.913 ms/op 1.14
getNextSyncCommitteeIndices 100000 validators 136.50 ms/op 98.383 ms/op 1.39
computeProposers - vc 250000 642.05 us/op 545.56 us/op 1.18
computeEpochShuffling - vc 250000 42.470 ms/op 38.770 ms/op 1.10
getNextSyncCommittee - vc 250000 10.517 ms/op 9.2830 ms/op 1.13
nodejs block root to RootHex using toHex 141.48 ns/op 115.39 ns/op 1.23
nodejs block root to RootHex using toRootHex 87.574 ns/op 75.982 ns/op 1.15
nodejs fromHex(blob) 348.25 us/op 259.74 us/op 1.34
nodejs fromHexInto(blob) 738.77 us/op 702.04 us/op 1.05
nodejs block root to RootHex using the deprecated toHexString 561.92 ns/op 529.30 ns/op 1.06
nodejs byteArrayEquals 32 bytes (block root) 29.080 ns/op 28.045 ns/op 1.04
nodejs byteArrayEquals 48 bytes (pubkey) 41.670 ns/op 41.404 ns/op 1.01
nodejs byteArrayEquals 96 bytes (signature) 41.470 ns/op 37.858 ns/op 1.10
nodejs byteArrayEquals 1024 bytes 48.316 ns/op 42.172 ns/op 1.15
nodejs byteArrayEquals 131072 bytes (blob) 1.9158 us/op 1.7469 us/op 1.10
browser block root to RootHex using toHex 167.59 ns/op 148.43 ns/op 1.13
browser block root to RootHex using toRootHex 157.76 ns/op 135.66 ns/op 1.16
browser fromHex(blob) 1.3515 ms/op 982.52 us/op 1.38
browser fromHexInto(blob) 794.57 us/op 674.16 us/op 1.18
browser block root to RootHex using the deprecated toHexString 561.33 ns/op 367.13 ns/op 1.53
browser byteArrayEquals 32 bytes (block root) 34.015 ns/op 33.547 ns/op 1.01
browser byteArrayEquals 48 bytes (pubkey) 47.171 ns/op 39.941 ns/op 1.18
browser byteArrayEquals 96 bytes (signature) 91.999 ns/op 74.737 ns/op 1.23
browser byteArrayEquals 1024 bytes 870.62 ns/op 747.04 ns/op 1.17
browser byteArrayEquals 131072 bytes (blob) 109.76 us/op 93.351 us/op 1.18

by benchmarkbot/action

@nflaig
Copy link
Member Author

nflaig commented Feb 8, 2026

@codex review

Copy link

@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: ddd4339d1e

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1608 to +1616
// Search the block production cache for a block produced at this slot
// The cache is keyed by block root, so we need to search through entries
let cachedResult: {blockRootHex: string; produceResult: ProduceFullGloas} | null = null;

for (const [blockRootHex, produceResult] of chain.blockProductionCache.entries()) {
if (produceResult.fork === fork && produceResult.type === BlockType.Full && "executionPayload" in produceResult) {
cachedResult = {blockRootHex, produceResult: produceResult as ProduceFullGloas};
break;
}

Choose a reason for hiding this comment

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

P1 Badge Match cached production result to requested slot

getExecutionPayloadEnvelope claims to look up the cache entry “for a block produced at this slot,” but it only checks fork/type and then returns the first cached entry. If the cache contains multiple produced blocks (e.g., from prior slots or retries), this can return a payload/envelope that does not correspond to the requested slot, causing the validator to sign and publish an envelope that peers will reject. You need to filter by slot (or block root derived from the produced block for this slot) before building the envelope.

Useful? React with 👍 / 👎.

Comment on lines +364 to +368
// For GLOAS, the inclusion proof is for the envelope's blobKzgCommitments field
// The envelope structure has a different gindex than the block body
// TODO GLOAS: Compute proper inclusion proof for envelope's blobKzgCommitments
// For now, use an empty proof since the envelope already contains the commitments explicitly
const kzgCommitmentsInclusionProof = new Array(17).fill(new Uint8Array(32)) as fulu.KzgCommitmentsInclusionProof;

Choose a reason for hiding this comment

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

P1 Badge Do not publish data columns with a dummy inclusion proof

getDataColumnSidecarsForGloas constructs kzgCommitmentsInclusionProof as an all‑zero array with a TODO comment. The validation path uses verifyMerkleBranch against the block header’s bodyRoot; a dummy proof will fail on peers, so self‑built Gloas data column sidecars will be rejected and not propagated. This needs a real inclusion proof computed against the envelope’s blobKzgCommitments field before gossiping.

Useful? React with 👍 / 👎.

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.

1 participant