Skip to content

feat: enhance processChainSegment for ePBS#9103

Closed
twoeths wants to merge 27 commits intounstablefrom
te/gloas_process_chain_segment
Closed

feat: enhance processChainSegment for ePBS#9103
twoeths wants to merge 27 commits intounstablefrom
te/gloas_process_chain_segment

Conversation

@twoeths
Copy link
Copy Markdown
Contributor

@twoeths twoeths commented Mar 24, 2026

Motivation

Part of implementing range sync for ePBS (EIP-7732 / Gloas). This is the 1st part — it extends the block verification and import pipeline to process execution payloads and envelopes in parallel with block verification. A 2nd part PR will follow to consume the APIs introduced here and complete the range sync implementation.

Description

Extends the block processing pipeline to handle Gloas blocks with envelopes available at verification time (sync/batch path):

  • processChainSegments: threads envelopes map through the block processing queue
  • assertLinearChainSegment: validates execution hash continuity across Gloas block segments
  • verifyBlocksExecutionPayload: extracts payload from envelope for EL newPayload calls
  • verifyBlocksStateTransitionOnly: runs processExecutionPayloadEnvelope when envelope is available, returning postEnvelopeStates; type-narrowed for safety
  • verifyBlocksSignatures: verifies envelope signatures alongside block signatures
  • FullyVerifiedBlock: renames postState -> postBlockState; uses a discriminated union -- when postEnvelopeState is non-null, executionStatus is narrowed to Valid | Syncing (enforced in processBlocks)
  • importBlock:
    • when postEnvelopeState is non-null, calls forkChoice.onExecutionPayload() (transitions block PENDING -> FULL) with the correct executionStatus, calls regen.processPayloadState() to cache the post-envelope state, and at epoch boundary slots also populates the checkpoint state cache via regen.addCheckpointState()
    • SeenPayloadEnvelopeInput.add() no longer throws when a PayloadEnvelopeInput already exists (range sync may pre-create it); returns {input, created} and renames the metric to createdByBlock
  • getSegmentErrorResponse: uses isGloasBeaconBlock to read parentBlockHash from the signed execution payload bid for Gloas blocks (instead of executionPayload.blockHash, which Gloas blocks do not have)

AI Assistance Disclosure

Used Claude Code to implement and iterate on these changes.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 lays the groundwork for ePBS range synchronization by significantly upgrading the block processing pipeline. It introduces the capability to handle execution payload envelopes alongside beacon blocks, allowing for parallel verification and state transitions. The changes ensure proper execution hash continuity and integrate envelope-specific logic into core verification and import functions, setting the stage for future range sync implementations.

Highlights

  • ePBS Range Sync Foundation: Introduced the initial components for implementing range synchronization for ePBS (EIP-7732 / Gloas), focusing on extending the block verification and import pipeline to process execution payloads and envelopes in parallel with block verification.
  • Parallel Execution Payload Processing: Modified the processChainSegments function to thread an envelopes map through the block processing queue, enabling parallel handling of execution payloads and envelopes during block verification.
  • Execution Hash Continuity Validation: Enhanced assertLinearChainSegment to validate execution hash continuity across Gloas block segments, ensuring that a block's bid.parentBlockHash matches the expected execution hash derived from its parent, and that envelopes reference the correct block root.
  • Execution Payload Extraction and State Transition: Updated verifyBlocksExecutionPayload to extract execution payloads from envelopes for Execution Layer newPayload calls and verifyBlocksStateTransitionOnly to run processExecutionPayloadEnvelope when an envelope is available, returning postEnvelopeStates.
  • Envelope Signature Verification: Integrated envelope signature verification into verifyBlocksSignatures, allowing envelope signatures to be checked alongside block signatures.
  • Refined Block State Representation: Renamed postState to postBlockState within FullyVerifiedBlock and added postEnvelopeState: CachedBeaconStateGloas | null to explicitly differentiate between the beacon block state and the state after processing an execution payload envelope.
  • Fork Choice and State Caching for Envelopes: Modified importBlock to utilize postEnvelopeState for Gloas blocks, calling forkChoice.onExecutionPayload() to transition the block status from PENDING to FULL and regen.processPayloadState() to cache the post-envelope state.

🧠 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.

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
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 is a significant step towards implementing range sync for ePBS (Gloas). It extends the block verification and import pipeline to handle execution payloads and envelopes in parallel with block verification. The changes are extensive, touching many parts of the block processing pipeline, but they are consistent and appear to be implemented correctly.

The key changes include threading an envelopes map through the processing queue, enhancing assertLinearChainSegment to validate execution hash continuity for Gloas blocks, and updating various verification functions (verifyBlocksExecutionPayload, verifyBlocksStateTransitionOnly, verifyBlocksSignatures) to handle envelopes. The FullyVerifiedBlock type has been updated to include postEnvelopeState, which is then used in importBlock to transition blocks to FULL status when applicable.

The new logic for handling Gloas blocks, including the FULL/EMPTY execution path variants, is well-implemented and the addition of a comprehensive test suite for assertLinearChainSegment is a great addition that improves confidence in the changes. The code is clear, and the TODOs effectively delineate the scope for future work. Overall, this is a high-quality contribution.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 24, 2026

⚠️ Performance Alert ⚠️

Possible performance regression was detected for some benchmarks.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold.

Benchmark suite Current: dc9774c Previous: d4d0b21 Ratio
send data - 1000 16384B messages 95.765 ms/op 20.855 ms/op 4.59
Full benchmark results
Benchmark suite Current: dc9774c Previous: d4d0b21 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.0080 ms/op 857.13 us/op 1.18
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 40.100 us/op 37.242 us/op 1.08
BLS verify - blst 735.19 us/op 723.93 us/op 1.02
BLS verifyMultipleSignatures 3 - blst 1.3945 ms/op 1.3091 ms/op 1.07
BLS verifyMultipleSignatures 8 - blst 2.2186 ms/op 2.0816 ms/op 1.07
BLS verifyMultipleSignatures 32 - blst 6.9612 ms/op 6.4187 ms/op 1.08
BLS verifyMultipleSignatures 64 - blst 13.454 ms/op 12.857 ms/op 1.05
BLS verifyMultipleSignatures 128 - blst 26.038 ms/op 25.062 ms/op 1.04
BLS deserializing 10000 signatures 643.79 ms/op 622.35 ms/op 1.03
BLS deserializing 100000 signatures 6.6744 s/op 6.1305 s/op 1.09
BLS verifyMultipleSignatures - same message - 3 - blst 804.77 us/op 758.28 us/op 1.06
BLS verifyMultipleSignatures - same message - 8 - blst 952.81 us/op 796.72 us/op 1.20
BLS verifyMultipleSignatures - same message - 32 - blst 1.5742 ms/op 1.4800 ms/op 1.06
BLS verifyMultipleSignatures - same message - 64 - blst 2.4880 ms/op 2.2761 ms/op 1.09
BLS verifyMultipleSignatures - same message - 128 - blst 4.1949 ms/op 3.8305 ms/op 1.10
BLS aggregatePubkeys 32 - blst 18.501 us/op 16.756 us/op 1.10
BLS aggregatePubkeys 128 - blst 65.700 us/op 59.513 us/op 1.10
getSlashingsAndExits - default max 48.117 us/op 47.452 us/op 1.01
getSlashingsAndExits - 2k 370.86 us/op 333.48 us/op 1.11
proposeBlockBody type=full, size=empty 859.41 us/op 591.70 us/op 1.45
isKnown best case - 1 super set check 165.00 ns/op 168.00 ns/op 0.98
isKnown normal case - 2 super set checks 167.00 ns/op 170.00 ns/op 0.98
isKnown worse case - 16 super set checks 158.00 ns/op 164.00 ns/op 0.96
validate api signedAggregateAndProof - struct 1.5556 ms/op 1.4531 ms/op 1.07
validate gossip signedAggregateAndProof - struct 1.5534 ms/op 1.4397 ms/op 1.08
batch validate gossip attestation - vc 640000 - chunk 32 108.35 us/op 104.73 us/op 1.03
batch validate gossip attestation - vc 640000 - chunk 64 93.930 us/op 90.926 us/op 1.03
batch validate gossip attestation - vc 640000 - chunk 128 88.676 us/op 86.952 us/op 1.02
batch validate gossip attestation - vc 640000 - chunk 256 86.024 us/op 85.296 us/op 1.01
bytes32 toHexString 301.00 ns/op 275.00 ns/op 1.09
bytes32 Buffer.toString(hex) 180.00 ns/op 177.00 ns/op 1.02
bytes32 Buffer.toString(hex) from Uint8Array 257.00 ns/op 250.00 ns/op 1.03
bytes32 Buffer.toString(hex) + 0x 183.00 ns/op 181.00 ns/op 1.01
Return object 10000 times 0.22160 ns/op 0.20790 ns/op 1.07
Throw Error 10000 times 3.4660 us/op 3.2294 us/op 1.07
toHex 105.93 ns/op 107.98 ns/op 0.98
Buffer.from 95.591 ns/op 84.641 ns/op 1.13
shared Buffer 67.318 ns/op 56.966 ns/op 1.18
fastMsgIdFn sha256 / 200 bytes 1.5510 us/op 1.4280 us/op 1.09
fastMsgIdFn h32 xxhash / 200 bytes 155.00 ns/op 164.00 ns/op 0.95
fastMsgIdFn h64 xxhash / 200 bytes 203.00 ns/op 206.00 ns/op 0.99
fastMsgIdFn sha256 / 1000 bytes 4.9520 us/op 4.5900 us/op 1.08
fastMsgIdFn h32 xxhash / 1000 bytes 245.00 ns/op 249.00 ns/op 0.98
fastMsgIdFn h64 xxhash / 1000 bytes 250.00 ns/op 254.00 ns/op 0.98
fastMsgIdFn sha256 / 10000 bytes 43.913 us/op 40.187 us/op 1.09
fastMsgIdFn h32 xxhash / 10000 bytes 1.3090 us/op 1.1980 us/op 1.09
fastMsgIdFn h64 xxhash / 10000 bytes 849.00 ns/op 765.00 ns/op 1.11
send data - 1000 256B messages 4.2072 ms/op 4.1490 ms/op 1.01
send data - 1000 512B messages 4.3844 ms/op 4.2666 ms/op 1.03
send data - 1000 1024B messages 4.4338 ms/op 4.3889 ms/op 1.01
send data - 1000 1200B messages 4.7682 ms/op 4.7197 ms/op 1.01
send data - 1000 2048B messages 4.8423 ms/op 4.9445 ms/op 0.98
send data - 1000 4096B messages 6.1653 ms/op 5.7465 ms/op 1.07
send data - 1000 16384B messages 95.765 ms/op 20.855 ms/op 4.59
send data - 1000 65536B messages 236.41 ms/op 118.96 ms/op 1.99
enrSubnets - fastDeserialize 64 bits 768.00 ns/op 696.00 ns/op 1.10
enrSubnets - ssz BitVector 64 bits 265.00 ns/op 241.00 ns/op 1.10
enrSubnets - fastDeserialize 4 bits 108.00 ns/op 101.00 ns/op 1.07
enrSubnets - ssz BitVector 4 bits 263.00 ns/op 241.00 ns/op 1.09
prioritizePeers score -10:0 att 32-0.1 sync 2-0 213.75 us/op 193.22 us/op 1.11
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 257.13 us/op 220.38 us/op 1.17
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 364.79 us/op 328.19 us/op 1.11
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 637.28 us/op 599.42 us/op 1.06
prioritizePeers score 0:0 att 64-1 sync 4-1 741.95 us/op 681.39 us/op 1.09
array of 16000 items push then shift 1.3511 us/op 1.1986 us/op 1.13
LinkedList of 16000 items push then shift 7.3750 ns/op 7.1300 ns/op 1.03
array of 16000 items push then pop 70.221 ns/op 62.570 ns/op 1.12
LinkedList of 16000 items push then pop 6.2440 ns/op 5.7980 ns/op 1.08
array of 24000 items push then shift 1.9801 us/op 1.8071 us/op 1.10
LinkedList of 24000 items push then shift 6.9470 ns/op 6.9200 ns/op 1.00
array of 24000 items push then pop 100.33 ns/op 90.291 ns/op 1.11
LinkedList of 24000 items push then pop 6.2830 ns/op 5.8330 ns/op 1.08
intersect bitArray bitLen 8 4.9490 ns/op 4.5980 ns/op 1.08
intersect array and set length 8 30.669 ns/op 28.380 ns/op 1.08
intersect bitArray bitLen 128 24.931 ns/op 23.259 ns/op 1.07
intersect array and set length 128 516.33 ns/op 477.15 ns/op 1.08
bitArray.getTrueBitIndexes() bitLen 128 1.0690 us/op 1.0340 us/op 1.03
bitArray.getTrueBitIndexes() bitLen 248 1.8320 us/op 1.7000 us/op 1.08
bitArray.getTrueBitIndexes() bitLen 512 3.7390 us/op 3.4730 us/op 1.08
Full columns - reconstruct all 6 blobs 167.69 us/op 150.46 us/op 1.11
Full columns - reconstruct half of the blobs out of 6 70.240 us/op 82.394 us/op 0.85
Full columns - reconstruct single blob out of 6 35.034 us/op 29.684 us/op 1.18
Half columns - reconstruct all 6 blobs 407.51 ms/op 373.81 ms/op 1.09
Half columns - reconstruct half of the blobs out of 6 203.49 ms/op 188.17 ms/op 1.08
Half columns - reconstruct single blob out of 6 72.974 ms/op 66.904 ms/op 1.09
Full columns - reconstruct all 10 blobs 309.58 us/op 344.25 us/op 0.90
Full columns - reconstruct half of the blobs out of 10 137.37 us/op 146.13 us/op 0.94
Full columns - reconstruct single blob out of 10 32.873 us/op 29.748 us/op 1.11
Half columns - reconstruct all 10 blobs 675.77 ms/op 611.81 ms/op 1.10
Half columns - reconstruct half of the blobs out of 10 341.60 ms/op 302.43 ms/op 1.13
Half columns - reconstruct single blob out of 10 74.064 ms/op 65.244 ms/op 1.14
Full columns - reconstruct all 20 blobs 1.5701 ms/op 526.51 us/op 2.98
Full columns - reconstruct half of the blobs out of 20 191.80 us/op 271.06 us/op 0.71
Full columns - reconstruct single blob out of 20 31.919 us/op 30.771 us/op 1.04
Half columns - reconstruct all 20 blobs 1.3506 s/op 1.2236 s/op 1.10
Half columns - reconstruct half of the blobs out of 20 675.95 ms/op 625.58 ms/op 1.08
Half columns - reconstruct single blob out of 20 72.375 ms/op 67.801 ms/op 1.07
Set add up to 64 items then delete first 2.1864 us/op 2.0105 us/op 1.09
OrderedSet add up to 64 items then delete first 3.4262 us/op 3.2021 us/op 1.07
Set add up to 64 items then delete last 2.4615 us/op 2.0372 us/op 1.21
OrderedSet add up to 64 items then delete last 3.3178 us/op 3.1432 us/op 1.06
Set add up to 64 items then delete middle 2.2123 us/op 2.0547 us/op 1.08
OrderedSet add up to 64 items then delete middle 4.8809 us/op 4.5056 us/op 1.08
Set add up to 128 items then delete first 4.4163 us/op 3.9776 us/op 1.11
OrderedSet add up to 128 items then delete first 6.7554 us/op 5.8994 us/op 1.15
Set add up to 128 items then delete last 4.0324 us/op 3.6305 us/op 1.11
OrderedSet add up to 128 items then delete last 5.9771 us/op 5.4255 us/op 1.10
Set add up to 128 items then delete middle 4.0726 us/op 3.6191 us/op 1.13
OrderedSet add up to 128 items then delete middle 12.008 us/op 11.369 us/op 1.06
Set add up to 256 items then delete first 8.2356 us/op 7.3534 us/op 1.12
OrderedSet add up to 256 items then delete first 12.569 us/op 11.589 us/op 1.08
Set add up to 256 items then delete last 8.0181 us/op 7.1850 us/op 1.12
OrderedSet add up to 256 items then delete last 11.882 us/op 10.852 us/op 1.09
Set add up to 256 items then delete middle 8.1084 us/op 7.1098 us/op 1.14
OrderedSet add up to 256 items then delete middle 35.912 us/op 33.403 us/op 1.08
pass gossip attestations to forkchoice per slot 2.6069 ms/op 2.4635 ms/op 1.06
forkChoice updateHead vc 100000 bc 64 eq 0 444.51 us/op 374.65 us/op 1.19
forkChoice updateHead vc 600000 bc 64 eq 0 2.6546 ms/op 2.2811 ms/op 1.16
forkChoice updateHead vc 1000000 bc 64 eq 0 4.4093 ms/op 3.6895 ms/op 1.20
forkChoice updateHead vc 600000 bc 320 eq 0 2.7109 ms/op 2.2567 ms/op 1.20
forkChoice updateHead vc 600000 bc 1200 eq 0 2.7059 ms/op 2.2934 ms/op 1.18
forkChoice updateHead vc 600000 bc 7200 eq 0 3.0278 ms/op 2.5788 ms/op 1.17
forkChoice updateHead vc 600000 bc 64 eq 1000 3.2187 ms/op 2.8151 ms/op 1.14
forkChoice updateHead vc 600000 bc 64 eq 10000 3.3524 ms/op 2.9209 ms/op 1.15
forkChoice updateHead vc 600000 bc 64 eq 300000 7.2189 ms/op 6.6072 ms/op 1.09
computeDeltas 1400000 validators 0% inactive 13.473 ms/op 12.190 ms/op 1.11
computeDeltas 1400000 validators 10% inactive 12.565 ms/op 11.488 ms/op 1.09
computeDeltas 1400000 validators 20% inactive 11.897 ms/op 10.602 ms/op 1.12
computeDeltas 1400000 validators 50% inactive 8.9769 ms/op 8.1061 ms/op 1.11
computeDeltas 2100000 validators 0% inactive 20.548 ms/op 18.511 ms/op 1.11
computeDeltas 2100000 validators 10% inactive 19.302 ms/op 17.268 ms/op 1.12
computeDeltas 2100000 validators 20% inactive 17.890 ms/op 15.686 ms/op 1.14
computeDeltas 2100000 validators 50% inactive 13.657 ms/op 9.1458 ms/op 1.49
altair processAttestation - 250000 vs - 7PWei normalcase 2.0606 ms/op 2.1639 ms/op 0.95
altair processAttestation - 250000 vs - 7PWei worstcase 2.7918 ms/op 3.0219 ms/op 0.92
altair processAttestation - setStatus - 1/6 committees join 102.09 us/op 97.426 us/op 1.05
altair processAttestation - setStatus - 1/3 committees join 206.47 us/op 191.91 us/op 1.08
altair processAttestation - setStatus - 1/2 committees join 298.47 us/op 267.66 us/op 1.12
altair processAttestation - setStatus - 2/3 committees join 377.88 us/op 355.16 us/op 1.06
altair processAttestation - setStatus - 4/5 committees join 535.34 us/op 494.84 us/op 1.08
altair processAttestation - setStatus - 100% committees join 636.28 us/op 567.17 us/op 1.12
altair processBlock - 250000 vs - 7PWei normalcase 4.1070 ms/op 3.8615 ms/op 1.06
altair processBlock - 250000 vs - 7PWei normalcase hashState 16.667 ms/op 15.893 ms/op 1.05
altair processBlock - 250000 vs - 7PWei worstcase 21.543 ms/op 20.452 ms/op 1.05
altair processBlock - 250000 vs - 7PWei worstcase hashState 43.224 ms/op 40.722 ms/op 1.06
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3449 ms/op 1.3463 ms/op 1.00
phase0 processBlock - 250000 vs - 7PWei worstcase 17.546 ms/op 17.508 ms/op 1.00
altair processEth1Data - 250000 vs - 7PWei normalcase 301.81 us/op 288.72 us/op 1.05
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.4650 us/op 9.1430 us/op 0.38
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 20.651 us/op 20.137 us/op 1.03
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 6.0260 us/op 5.8770 us/op 1.03
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.7000 us/op 3.4660 us/op 1.07
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 97.200 us/op 93.086 us/op 1.04
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.4361 ms/op 1.3318 ms/op 1.08
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8740 ms/op 1.7262 ms/op 1.09
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8916 ms/op 1.7448 ms/op 1.08
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.7531 ms/op 3.6172 ms/op 1.04
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.1108 ms/op 1.9531 ms/op 1.08
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.1125 ms/op 3.8283 ms/op 1.07
Tree 40 250000 create 341.03 ms/op 299.49 ms/op 1.14
Tree 40 250000 get(125000) 97.824 ns/op 86.513 ns/op 1.13
Tree 40 250000 set(125000) 1.0578 us/op 946.57 ns/op 1.12
Tree 40 250000 toArray() 11.708 ms/op 11.634 ms/op 1.01
Tree 40 250000 iterate all - toArray() + loop 13.001 ms/op 11.735 ms/op 1.11
Tree 40 250000 iterate all - get(i) 41.450 ms/op 35.066 ms/op 1.18
Array 250000 create 2.3108 ms/op 2.0260 ms/op 1.14
Array 250000 clone - spread 705.67 us/op 637.81 us/op 1.11
Array 250000 get(125000) 0.30600 ns/op 0.28700 ns/op 1.07
Array 250000 set(125000) 0.30900 ns/op 0.28700 ns/op 1.08
Array 250000 iterate all - loop 59.509 us/op 55.470 us/op 1.07
phase0 afterProcessEpoch - 250000 vs - 7PWei 53.627 ms/op 37.740 ms/op 1.42
Array.fill - length 1000000 2.2847 ms/op 1.8425 ms/op 1.24
Array push - length 1000000 9.3645 ms/op 8.4752 ms/op 1.10
Array.get 0.21433 ns/op 0.20446 ns/op 1.05
Uint8Array.get 0.25258 ns/op 0.23288 ns/op 1.08
phase0 beforeProcessEpoch - 250000 vs - 7PWei 15.157 ms/op 14.154 ms/op 1.07
altair processEpoch - mainnet_e81889 265.16 ms/op 255.22 ms/op 1.04
mainnet_e81889 - altair beforeProcessEpoch 21.674 ms/op 16.015 ms/op 1.35
mainnet_e81889 - altair processJustificationAndFinalization 5.6350 us/op 5.7960 us/op 0.97
mainnet_e81889 - altair processInactivityUpdates 3.6245 ms/op 3.5274 ms/op 1.03
mainnet_e81889 - altair processRewardsAndPenalties 19.559 ms/op 22.706 ms/op 0.86
mainnet_e81889 - altair processRegistryUpdates 562.00 ns/op 540.00 ns/op 1.04
mainnet_e81889 - altair processSlashings 135.00 ns/op 133.00 ns/op 1.02
mainnet_e81889 - altair processEth1DataReset 133.00 ns/op 128.00 ns/op 1.04
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.7579 ms/op 6.9257 ms/op 0.25
mainnet_e81889 - altair processSlashingsReset 921.00 ns/op 726.00 ns/op 1.27
mainnet_e81889 - altair processRandaoMixesReset 1.1420 us/op 1.3550 us/op 0.84
mainnet_e81889 - altair processHistoricalRootsUpdate 137.00 ns/op 137.00 ns/op 1.00
mainnet_e81889 - altair processParticipationFlagUpdates 460.00 ns/op 447.00 ns/op 1.03
mainnet_e81889 - altair processSyncCommitteeUpdates 107.00 ns/op 112.00 ns/op 0.96
mainnet_e81889 - altair afterProcessEpoch 44.764 ms/op 41.559 ms/op 1.08
capella processEpoch - mainnet_e217614 812.47 ms/op 961.56 ms/op 0.84
mainnet_e217614 - capella beforeProcessEpoch 68.281 ms/op 80.905 ms/op 0.84
mainnet_e217614 - capella processJustificationAndFinalization 5.9190 us/op 9.3400 us/op 0.63
mainnet_e217614 - capella processInactivityUpdates 13.747 ms/op 24.159 ms/op 0.57
mainnet_e217614 - capella processRewardsAndPenalties 96.473 ms/op 123.28 ms/op 0.78
mainnet_e217614 - capella processRegistryUpdates 4.5680 us/op 6.1380 us/op 0.74
mainnet_e217614 - capella processSlashings 138.00 ns/op 195.00 ns/op 0.71
mainnet_e217614 - capella processEth1DataReset 128.00 ns/op 180.00 ns/op 0.71
mainnet_e217614 - capella processEffectiveBalanceUpdates 6.2274 ms/op 24.857 ms/op 0.25
mainnet_e217614 - capella processSlashingsReset 706.00 ns/op 850.00 ns/op 0.83
mainnet_e217614 - capella processRandaoMixesReset 1.1860 us/op 1.6280 us/op 0.73
mainnet_e217614 - capella processHistoricalRootsUpdate 135.00 ns/op 136.00 ns/op 0.99
mainnet_e217614 - capella processParticipationFlagUpdates 448.00 ns/op 528.00 ns/op 0.85
mainnet_e217614 - capella afterProcessEpoch 112.45 ms/op 108.82 ms/op 1.03
phase0 processEpoch - mainnet_e58758 295.48 ms/op 367.40 ms/op 0.80
mainnet_e58758 - phase0 beforeProcessEpoch 57.113 ms/op 90.413 ms/op 0.63
mainnet_e58758 - phase0 processJustificationAndFinalization 5.6100 us/op 6.8630 us/op 0.82
mainnet_e58758 - phase0 processRewardsAndPenalties 15.604 ms/op 17.743 ms/op 0.88
mainnet_e58758 - phase0 processRegistryUpdates 2.3030 us/op 2.3300 us/op 0.99
mainnet_e58758 - phase0 processSlashings 137.00 ns/op 140.00 ns/op 0.98
mainnet_e58758 - phase0 processEth1DataReset 134.00 ns/op 133.00 ns/op 1.01
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 858.44 us/op 1.0902 ms/op 0.79
mainnet_e58758 - phase0 processSlashingsReset 934.00 ns/op 1.0280 us/op 0.91
mainnet_e58758 - phase0 processRandaoMixesReset 1.1560 us/op 1.4750 us/op 0.78
mainnet_e58758 - phase0 processHistoricalRootsUpdate 136.00 ns/op 137.00 ns/op 0.99
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.0450 us/op 1.2780 us/op 0.82
mainnet_e58758 - phase0 afterProcessEpoch 34.104 ms/op 34.298 ms/op 0.99
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.0385 ms/op 1.1086 ms/op 0.94
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.6333 ms/op 3.8729 ms/op 0.42
altair processInactivityUpdates - 250000 normalcase 10.865 ms/op 14.452 ms/op 0.75
altair processInactivityUpdates - 250000 worstcase 11.082 ms/op 12.220 ms/op 0.91
phase0 processRegistryUpdates - 250000 normalcase 3.4960 us/op 4.5060 us/op 0.78
phase0 processRegistryUpdates - 250000 badcase_full_deposits 152.09 us/op 154.17 us/op 0.99
phase0 processRegistryUpdates - 250000 worstcase 0.5 72.002 ms/op 70.818 ms/op 1.02
altair processRewardsAndPenalties - 250000 normalcase 14.619 ms/op 15.141 ms/op 0.97
altair processRewardsAndPenalties - 250000 worstcase 14.010 ms/op 15.172 ms/op 0.92
phase0 getAttestationDeltas - 250000 normalcase 9.4014 ms/op 5.1921 ms/op 1.81
phase0 getAttestationDeltas - 250000 worstcase 5.5702 ms/op 5.2658 ms/op 1.06
phase0 processSlashings - 250000 worstcase 60.751 us/op 59.123 us/op 1.03
altair processSyncCommitteeUpdates - 250000 10.173 ms/op 9.9519 ms/op 1.02
BeaconState.hashTreeRoot - No change 165.00 ns/op 166.00 ns/op 0.99
BeaconState.hashTreeRoot - 1 full validator 67.740 us/op 72.189 us/op 0.94
BeaconState.hashTreeRoot - 32 full validator 731.99 us/op 862.39 us/op 0.85
BeaconState.hashTreeRoot - 512 full validator 7.2133 ms/op 6.6096 ms/op 1.09
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 78.479 us/op 83.673 us/op 0.94
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.1280 ms/op 1.2394 ms/op 0.91
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 13.843 ms/op 13.926 ms/op 0.99
BeaconState.hashTreeRoot - 1 balances 60.968 us/op 60.494 us/op 1.01
BeaconState.hashTreeRoot - 32 balances 599.64 us/op 771.28 us/op 0.78
BeaconState.hashTreeRoot - 512 balances 5.6390 ms/op 4.9681 ms/op 1.14
BeaconState.hashTreeRoot - 250000 balances 111.03 ms/op 137.41 ms/op 0.81
aggregationBits - 2048 els - zipIndexesInBitList 19.885 us/op 19.911 us/op 1.00
regular array get 100000 times 22.944 us/op 22.668 us/op 1.01
wrappedArray get 100000 times 23.034 us/op 22.574 us/op 1.02
arrayWithProxy get 100000 times 10.675 ms/op 15.230 ms/op 0.70
ssz.Root.equals 21.702 ns/op 21.089 ns/op 1.03
byteArrayEquals 21.428 ns/op 20.898 ns/op 1.03
Buffer.compare 9.0300 ns/op 8.6430 ns/op 1.04
processSlot - 1 slots 8.6430 us/op 9.2450 us/op 0.93
processSlot - 32 slots 1.7089 ms/op 1.8856 ms/op 0.91
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 2.3143 ms/op 4.6851 ms/op 0.49
getCommitteeAssignments - req 1 vs - 250000 vc 1.6805 ms/op 1.6331 ms/op 1.03
getCommitteeAssignments - req 100 vs - 250000 vc 3.4671 ms/op 3.3764 ms/op 1.03
getCommitteeAssignments - req 1000 vs - 250000 vc 3.7345 ms/op 3.6404 ms/op 1.03
findModifiedValidators - 10000 modified validators 771.41 ms/op 837.66 ms/op 0.92
findModifiedValidators - 1000 modified validators 448.84 ms/op 654.85 ms/op 0.69
findModifiedValidators - 100 modified validators 290.42 ms/op 300.53 ms/op 0.97
findModifiedValidators - 10 modified validators 223.72 ms/op 235.21 ms/op 0.95
findModifiedValidators - 1 modified validators 181.66 ms/op 170.37 ms/op 1.07
findModifiedValidators - no difference 177.66 ms/op 168.85 ms/op 1.05
migrate state 1500000 validators, 3400 modified, 2000 new 2.6131 s/op 2.6641 s/op 0.98
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.8400 ns/op 3.5500 ns/op 1.08
state getBlockRootAtSlot - 250000 vs - 7PWei 284.01 ns/op 345.94 ns/op 0.82
computeProposerIndex 100000 validators 1.3255 ms/op 1.2837 ms/op 1.03
getNextSyncCommitteeIndices 1000 validators 2.8555 ms/op 2.7334 ms/op 1.04
getNextSyncCommitteeIndices 10000 validators 25.156 ms/op 24.261 ms/op 1.04
getNextSyncCommitteeIndices 100000 validators 86.749 ms/op 83.706 ms/op 1.04
computeProposers - vc 250000 548.42 us/op 630.40 us/op 0.87
computeEpochShuffling - vc 250000 39.013 ms/op 37.226 ms/op 1.05
getNextSyncCommittee - vc 250000 9.4193 ms/op 8.9598 ms/op 1.05
nodejs block root to RootHex using toHex 117.05 ns/op 98.968 ns/op 1.18
nodejs block root to RootHex using toRootHex 76.946 ns/op 63.880 ns/op 1.20
nodejs fromHex(blob) 730.89 us/op 699.27 us/op 1.05
nodejs fromHexInto(blob) 662.88 us/op 611.56 us/op 1.08
nodejs block root to RootHex using the deprecated toHexString 502.24 ns/op 450.82 ns/op 1.11
nodejs byteArrayEquals 32 bytes (block root) 26.036 ns/op 24.965 ns/op 1.04
nodejs byteArrayEquals 48 bytes (pubkey) 37.628 ns/op 36.020 ns/op 1.04
nodejs byteArrayEquals 96 bytes (signature) 38.286 ns/op 30.672 ns/op 1.25
nodejs byteArrayEquals 1024 bytes 43.069 ns/op 36.859 ns/op 1.17
nodejs byteArrayEquals 131072 bytes (blob) 1.7642 us/op 1.6873 us/op 1.05
browser block root to RootHex using toHex 147.59 ns/op 139.25 ns/op 1.06
browser block root to RootHex using toRootHex 133.00 ns/op 126.77 ns/op 1.05
browser fromHex(blob) 1.5368 ms/op 1.4435 ms/op 1.06
browser fromHexInto(blob) 666.34 us/op 604.32 us/op 1.10
browser block root to RootHex using the deprecated toHexString 350.64 ns/op 442.93 ns/op 0.79
browser byteArrayEquals 32 bytes (block root) 27.930 ns/op 26.681 ns/op 1.05
browser byteArrayEquals 48 bytes (pubkey) 39.359 ns/op 38.018 ns/op 1.04
browser byteArrayEquals 96 bytes (signature) 73.692 ns/op 71.000 ns/op 1.04
browser byteArrayEquals 1024 bytes 750.74 ns/op 723.80 ns/op 1.04
browser byteArrayEquals 131072 bytes (blob) 95.119 us/op 91.444 us/op 1.04

by benchmarkbot/action

@twoeths
Copy link
Copy Markdown
Contributor Author

twoeths commented Mar 27, 2026

blocked by #9119 to make it easier to review for this PR

nflaig pushed a commit that referenced this pull request Mar 30, 2026
…#9119)

**Motivation**

- In Gloas (ePBS), execution payloads arrive separately from beacon
blocks via `SignedExecutionPayloadEnvelope`. When `onExecutionPayload()`
is called, the EL may respond with `SYNCING`/`ACCEPTED` (optimistic) in
addition to `VALID`, so fork choice needs to know which status to assign
to the resulting FULL node. Previously `onExecutionPayload()` hardcoded
`ExecutionStatus.Valid`.
- This is a prerequisite for #9103 (enhance processChainSegment for
ePBS).

**Description**

- Split `MaybeValidExecutionStatus` (now removed) into two more precise
types:
- `BlockExecutionStatus` — `Exclude<ExecutionStatus,
ExecutionStatus.Invalid>` — used for `onBlock()`, covers `Valid |
Syncing | PreMerge | PayloadSeparated`
- `PayloadExecutionStatus` — `ExecutionStatus.Valid |
ExecutionStatus.Syncing` — used for `onExecutionPayload()`, narrower
since a payload has already been submitted to the EL
- `onExecutionPayload()` in `IForkChoice`, `ForkChoice`, and
`ProtoArray` now accepts a `executionStatus: PayloadExecutionStatus`
parameter
- `FullyVerifiedBlock` becomes a discriminated union on
`postEnvelopeState: null | IBeaconStateView`, which narrows
`executionStatus` to `PayloadExecutionStatus` when the envelope was
pre-verified during the sync/batch path
- `importExecutionPayload` adds `toForkChoiceExecutionStatus()` to map
`ExecutionPayloadStatus → PayloadExecutionStatus`, allows
`SYNCING`/`ACCEPTED` through for optimistic import, and enables payload
state caching via `regen.processPayloadState()` /
`regen.addCheckpointState()`

**AI Assistance Disclosure**

Used Claude Code to assist with the implementation.

---------

Co-authored-by: Tuyen Nguyen <twoeths@users.noreply.github.com>
@twoeths twoeths force-pushed the te/gloas_process_chain_segment branch from 1362957 to 63bf3c0 Compare March 31, 2026 06:27
@twoeths twoeths marked this pull request as ready for review March 31, 2026 09:15
@twoeths twoeths requested a review from a team as a code owner March 31, 2026 09:15
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: 86d1d4b748

ℹ️ 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 thread packages/beacon-node/src/chain/blocks/importBlock.ts Outdated
@twoeths twoeths marked this pull request as draft April 2, 2026 03:47
@twoeths twoeths marked this pull request as ready for review April 2, 2026 07:17
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: af5ab38c5b

ℹ️ 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 +81 to +82
// FULL variant: advance execution hash to the delivered payload's block hash
currentExecHash = toRootHex(payloadEnvelope.message.payload.blockHash);
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 Allow child blocks to follow EMPTY path when envelope is orphaned

Updating currentExecHash to the envelope payload hash unconditionally makes the next Gloas block require bid.parentBlockHash == envelope.payload.blockHash, which rejects valid segments where a child intentionally builds on the EMPTY variant (e.g., late envelope arrival). This conflicts with verifyBlocksStateTransitionOnly, which explicitly handles that orphan-envelope case by selecting the EMPTY pre-state, so the segment is rejected early with BID_PARENT_HASH_MISMATCH even though state-transition logic supports it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

verifyBlocksStateTransitionOnly is a bit relaxed because in epbs-devnet-0 peers return orphaned payload envelope
this conflict of this logic of assertLinearChainSegment()
maybe we need to relax the logic here too, until it's stable on devnets?
cc @nflaig

Copy link
Copy Markdown
Member

@nflaig nflaig Apr 2, 2026

Choose a reason for hiding this comment

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

based on the comment here from Potuz, it seems like Prysm is already downscoring peers that serve orphaned payloads

image

I also left this comment

image

which Potuz agreed is the way clients should do it

image

so this will be defined in the spec sooner or later (there is a PR already ethereum/consensus-specs#5060), I do think it's nice to have if we can handle this case and just disregard the orphaned payload + downscore the peer, but use other data that is correct, I would say depends on implementation complexity required to support this.

Comment thread packages/beacon-node/src/chain/blocks/index.ts Outdated
Comment thread packages/beacon-node/src/chain/blocks/verifyBlock.ts Outdated
Comment thread packages/beacon-node/src/sync/range/range.ts
Comment thread packages/beacon-node/src/chain/errors/blockError.ts Outdated
Comment thread packages/beacon-node/src/chain/blocks/verifyBlock.ts Outdated
Comment thread packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts Outdated
Comment thread packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts Outdated
const blockHashHex = isGloasBeaconBlock(block.message)
? toRootHex(
payloadEnvelopes?.get(blockInput.slot)?.getPayloadEnvelope()?.message.payload.blockHash ??
block.message.body.signedExecutionPayloadBid.message.parentBlockHash
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be block.message.body.signedExecutionPayloadBid.message.blockHash?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

should be parentBlockHash because there is no payload in this case
this is consistent to

executionPayloadBlockHash: toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash), // post-gloas, we don't know payload hash until we import execution payload. Set to parent payload hash for now

will add that to the comment

toForkChoiceExecutionStatus(execResult.status)
execStatus
);
const newHead = this.recomputeForkChoiceHead(ForkchoiceCaller.importBlock);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In range sync case calling recomputeForkChoiceHead here makes sense. But how about gossip case? Can we not rely on prepareNextSlot to do the recomputation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good call, I'm in favor of doing the same to prepareNextSlot in #9164
it's not a concern for this PR anyway, I'd leave a TODO GLOAS for it instead

@wemeetagain wemeetagain added the spec-gloas Issues targeting the Glamsterdam spec version label Apr 20, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.58%. Comparing base (d4d0b21) to head (717d3cc).
⚠️ Report is 15 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #9103      +/-   ##
============================================
+ Coverage     52.53%   52.58%   +0.05%     
============================================
  Files           848      848              
  Lines         61426    61353      -73     
  Branches       4528     4528              
============================================
- Hits          32269    32264       -5     
+ Misses        29092    29024      -68     
  Partials         65       65              
🚀 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.

@twoeths twoeths mentioned this pull request Apr 21, 2026
@twoeths
Copy link
Copy Markdown
Contributor Author

twoeths commented Apr 21, 2026

superseded by #9242 for the latest alpha.5 spec

@twoeths twoeths closed this Apr 21, 2026
@twoeths twoeths deleted the te/gloas_process_chain_segment branch April 21, 2026 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

spec-gloas Issues targeting the Glamsterdam spec version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants