Skip to content

feat: enhance NetworkProcessor for gloas#9025

Merged
nflaig merged 21 commits into
unstablefrom
te/gloas_network_processor
Apr 2, 2026
Merged

feat: enhance NetworkProcessor for gloas#9025
nflaig merged 21 commits into
unstablefrom
te/gloas_network_processor

Conversation

@twoeths
Copy link
Copy Markdown
Contributor

@twoeths twoeths commented Mar 12, 2026

Motivation

In the GLOAS fork, several gossip message types depend on the execution payload envelope being known before they can be properly validated:

  • beacon_attestation / beacon_aggregate_and_proof with attIndex === 1 must wait for the envelope
  • payload_attestation_message with payloadPresent = true must wait for the envelope
  • data_column_sidecar in GLOAS triggers envelope search but does not await (pushes to queue immediately)
  • execution_payload_bid triggers parent block/envelope search and await
  • execution_payload triggers block search but does not await (pushes to queue immediately)
  • beacon_block triggers parent block/envelope search but does not await (pushes to queue immediately)

Previously the NetworkProcessor only had a mechanism to await unknown blocks. This PR extends it to also await unknown execution payload envelopes for GLOAS.

Description

  • Adds slot+root extraction from serialized bytes for GLOAS gossip types: execution_payload (envelope), payload_attestation_message, execution_payload_bid, and data_column_sidecar (block root in GLOAS)
  • Restructures onPendingGossipsubMessage() into two extraction rounds:
    • Round 1 (extractBlockSlotRootFn): extracts slot + block root from serialized bytes — common to all topics. If the block root is unknown, always triggers searchUnknownBlock() and defaults to AwaitBlock.
    • Round 2 (per-topic switch): decouples the search action from the await action for specific topics. For example, data_column_sidecar triggers searchUnknownEnvelope() but does not await (pushes to queue immediately), while beacon_attestation with index = 1 overrides the round-1 result and awaits the envelope instead.
  • Introduces awaitingMessagesByPayloadBlockRoot — a new pending-message queue that holds gossip messages until the required execution payload envelope is known, mirroring the existing awaitingMessagesByBlockRoot queue
  • Adds searchUnknownEnvelope() — the envelope-side counterpart to searchUnknownBlock(), emitting ChainEvent.unknownEnvelopeBlockRoot to trigger the unknown-envelope sync path
  • Enhances onPendingGossipsubMessage() with a PreprocessAction enum (PushToQueue | AwaitBlock | AwaitEnvelope) to cleanly route each message to the right waiting queue based on GLOAS fork rules
  • Adds onPayloadEnvelopeProcessed() — called when an envelope is processed, analogous to onBlockProcessed(), to flush and reprocess messages that were awaiting that envelope
  • Adds new Prometheus metrics for the envelope-awaiting queue (awaitingPayloadGossipMessages.*)

AI Assistance Disclosure

Used Claude Code (claude-sonnet-4-6) for co-working.

@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 significantly enhances the NetworkProcessor to support the GLOAS fork by introducing a dedicated mechanism for handling gossip messages that require a known execution payload envelope. This change ensures that critical messages, such as certain attestations and data column sidecars, are properly validated and processed even when their associated execution payload envelopes are not immediately available, thereby improving the network's resilience and correctness under GLOAS.

Highlights

  • GLOAS Fork Support: Introduced a new mechanism in the NetworkProcessor to handle gossip messages that depend on the execution payload envelope being known, specifically for the GLOAS fork. This includes new queues and processing logic.
  • Gossip Message Processing: Enhanced the onPendingGossipsubMessage() function with a PreProcessAction enum to intelligently route messages to either a block-awaiting queue, an envelope-awaiting queue, or the standard processing queue based on GLOAS fork rules and message type.
  • New Awaiting Queue and Metrics: Added awaitingPayloadByRootBySlot, a new pending-message queue for execution payload envelopes, mirroring the existing block-awaiting queue. Corresponding Prometheus metrics (awaitingPayloadGossipMessages.*) were introduced to monitor this new queue.
  • Data Extraction and Chain Events: Implemented new utility functions to extract slot and root information from serialized bytes for GLOAS-specific gossip types (execution_payload, payload_attestation_message, execution_payload_bid, data_column_sidecar). A new ChainEvent.unknownEnvelopeBlockRoot was added to trigger the sync path for unknown envelopes.
  • Refactoring and Clarity: Renamed awaitingGossipsubMessagesByRootBySlot to awaitingBlockByRootBySlot and updated related metrics and variables for improved clarity, distinguishing between block-dependent and envelope-dependent gossip messages.

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

Changelog
  • dashboards/lodestar_networking.json
    • Updated Grafana dashboard to reflect renamed metrics for awaiting block gossip messages.
    • Added new panels and metrics for the newly introduced awaiting payload gossip messages.
  • packages/beacon-node/src/api/impl/validator/index.ts
    • Renamed the network.searchUnknownSlotRoot call to network.searchUnknownBlock.
  • packages/beacon-node/src/chain/chain.ts
    • Added a new seenEnvelope method to check if an execution payload envelope for a given block root has been seen.
  • packages/beacon-node/src/chain/emitter.ts
    • Introduced a new ChainEvent.unknownEnvelopeBlockRoot to signal when an unknown execution payload envelope is encountered.
    • Updated ChainEventData and IChainEvents types to include the new event.
  • packages/beacon-node/src/chain/interface.ts
    • Added seenEnvelope method to the IBeaconChain interface.
  • packages/beacon-node/src/chain/validation/attestation.ts
    • Renamed getCommitteeIndexFromSingleAttestationSerialized to getIndexFromSingleAttestationSerialized for broader applicability.
  • packages/beacon-node/src/metrics/metrics/lodestar.ts
    • Renamed existing metrics related to reprocessed gossip attestations to awaitingBlockGossipMessages for clarity.
    • Introduced a new set of metrics under awaitingPayloadGossipMessages to track gossip messages waiting for execution payload envelopes.
  • packages/beacon-node/src/network/interface.ts
    • Renamed searchUnknownSlotRoot to searchUnknownBlock in the INetwork interface.
    • Added a new searchUnknownEnvelope method to the INetwork interface.
  • packages/beacon-node/src/network/network.ts
    • Implemented the searchUnknownBlock and searchUnknownEnvelope methods by delegating to the networkProcessor.
  • packages/beacon-node/src/network/processor/extractSlotRootFns.ts
    • Added new functions to extract slot and root from serialized bytes for GLOAS-specific gossip types: execution_payload, payload_attestation_message, execution_payload_bid, and data_column_sidecar.
  • packages/beacon-node/src/network/processor/gossipHandlers.ts
    • Updated calls to network.searchUnknownSlotRoot to network.searchUnknownBlock.
  • packages/beacon-node/src/network/processor/index.ts
    • Introduced MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS and MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK constants.
    • Added PreProcessAction enum to categorize how pending gossip messages should be handled.
    • Refactored onPendingGossipsubMessage to use PreProcessAction and route messages to either block or envelope awaiting queues.
    • Introduced awaitingPayloadByRootBySlot map and unknownPayloadGossipsubMessagesCount for envelope-dependent messages.
    • Added searchUnknownEnvelope method to trigger envelope sync.
    • Implemented onPayloadProcessed to reprocess messages awaiting a specific execution payload envelope.
    • Updated onClockSlot to prune both block and payload awaiting queues and reset their counts.
    • Renamed awaitingGossipsubMessagesByRootBySlot to awaitingBlockByRootBySlot and updated its usage.
  • packages/beacon-node/src/sync/unknownBlock.ts
    • Updated comments to reflect the renaming of searchUnknownSlotRoot to searchUnknownBlock.
  • packages/beacon-node/src/util/sszBytes.ts
    • Added isForkPostGloas check.
    • Renamed getCommitteeIndexFromSingleAttestationSerialized to getIndexFromSingleAttestationSerialized.
    • Added getIndexFromSignedAggregateAndProofSerialized.
    • Introduced getSlotFromDataColumnSidecarSerialized and getBeaconBlockRootFromDataColumnSidecarSerialized with GLOAS fork logic.
    • Added functions to extract slot, block root, and payload presence from SignedExecutionPayloadEnvelope, PayloadAttestationMessage, and SignedExecutionPayloadBid serialized bytes.
  • packages/beacon-node/test/unit/util/sszBytes.test.ts
    • Updated test cases for getIndexFromSingleAttestationSerialized.
    • Added new test cases for getIndexFromSignedAggregateAndProofSerialized.
    • Included new fork definitions (fulu, gloas) and test cases for SignedExecutionPayloadEnvelope, PayloadAttestationMessage, SignedExecutionPayloadBid, and DataColumnSidecar (for both Fulu and GLOAS forks).
  • packages/beacon-node/test/utils/blocksAndData.d.ts
    • Added GLOAS_FORK_EPOCH constant.
    • Updated slots type to include gloas.
  • packages/beacon-node/test/utils/blocksAndData.js
    • Added GLOAS_FORK_EPOCH constant.
    • Updated config and clock initialization to include GLOAS_FORK_EPOCH.
    • Updated slots object to include gloas.
  • packages/fork-choice/src/forkChoice/forkChoice.ts
    • Added hasEnvelopeUnsafe and hasEnvelopeHexUnsafe methods to check for the presence of a full payload variant.
  • packages/fork-choice/src/forkChoice/interface.ts
    • Added hasEnvelopeUnsafe and hasEnvelopeHexUnsafe methods to the IForkChoice interface.
  • packages/fork-choice/src/protoArray/protoArray.ts
    • Added a hasEnvelope method to check if a FULL payload variant (execution payload envelope) exists for a given block root.
Activity
  • No human activity has been recorded on this pull request yet.
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
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

The pull request introduces new metrics and logic for handling 'awaiting block' and 'awaiting payload' gossip messages, particularly for the upcoming Gloas fork. This includes renaming existing reprocess metrics to awaitingBlockGossipMessages and adding new awaitingPayloadGossipMessages metrics, along with new searchUnknownEnvelope functionality and seenEnvelope checks in the chain and fork choice. However, the review identified several critical issues: an incorrect import of a test utility file into production code, the onPayloadProcessed method not being registered as an event handler, and incorrect resetting of gossip message counters, which could lead to unbounded memory growth. Improvements were suggested to correctly decrement these counters when messages are processed or expire.

Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
Comment thread packages/beacon-node/src/network/processor/index.ts Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 12, 2026

Performance Report

🚀🚀 Significant benchmark improvement detected

Benchmark suite Current: f80bdae Previous: 8b81ee6 Ratio
enrSubnets - fastDeserialize 64 bits 750.00 ns/op 2.7560 us/op 0.27
Full benchmark results
Benchmark suite Current: f80bdae Previous: 8b81ee6 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.3331 ms/op 1.4185 ms/op 0.94
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 39.150 us/op 38.853 us/op 1.01
BLS verify - blst 749.90 us/op 714.87 us/op 1.05
BLS verifyMultipleSignatures 3 - blst 1.3532 ms/op 1.3264 ms/op 1.02
BLS verifyMultipleSignatures 8 - blst 2.1533 ms/op 2.0903 ms/op 1.03
BLS verifyMultipleSignatures 32 - blst 6.7737 ms/op 6.5550 ms/op 1.03
BLS verifyMultipleSignatures 64 - blst 13.148 ms/op 12.819 ms/op 1.03
BLS verifyMultipleSignatures 128 - blst 25.710 ms/op 24.501 ms/op 1.05
BLS deserializing 10000 signatures 632.79 ms/op 623.95 ms/op 1.01
BLS deserializing 100000 signatures 6.4591 s/op 6.1188 s/op 1.06
BLS verifyMultipleSignatures - same message - 3 - blst 710.91 us/op 679.02 us/op 1.05
BLS verifyMultipleSignatures - same message - 8 - blst 927.47 us/op 824.20 us/op 1.13
BLS verifyMultipleSignatures - same message - 32 - blst 1.4933 ms/op 1.4966 ms/op 1.00
BLS verifyMultipleSignatures - same message - 64 - blst 2.4805 ms/op 2.2752 ms/op 1.09
BLS verifyMultipleSignatures - same message - 128 - blst 4.1713 ms/op 3.9075 ms/op 1.07
BLS aggregatePubkeys 32 - blst 17.740 us/op 16.886 us/op 1.05
BLS aggregatePubkeys 128 - blst 63.350 us/op 59.743 us/op 1.06
getSlashingsAndExits - default max 54.322 us/op 47.123 us/op 1.15
getSlashingsAndExits - 2k 361.00 us/op 378.12 us/op 0.95
proposeBlockBody type=full, size=empty 759.90 us/op 615.66 us/op 1.23
isKnown best case - 1 super set check 207.00 ns/op 379.00 ns/op 0.55
isKnown normal case - 2 super set checks 167.00 ns/op 387.00 ns/op 0.43
isKnown worse case - 16 super set checks 167.00 ns/op 388.00 ns/op 0.43
validate api signedAggregateAndProof - struct 1.5344 ms/op 1.4839 ms/op 1.03
validate gossip signedAggregateAndProof - struct 1.5389 ms/op 1.4851 ms/op 1.04
batch validate gossip attestation - vc 640000 - chunk 32 109.74 us/op 105.66 us/op 1.04
batch validate gossip attestation - vc 640000 - chunk 64 96.354 us/op 90.193 us/op 1.07
batch validate gossip attestation - vc 640000 - chunk 128 90.683 us/op 83.509 us/op 1.09
batch validate gossip attestation - vc 640000 - chunk 256 86.607 us/op 80.324 us/op 1.08
bytes32 toHexString 296.00 ns/op 479.00 ns/op 0.62
bytes32 Buffer.toString(hex) 186.00 ns/op 375.00 ns/op 0.50
bytes32 Buffer.toString(hex) from Uint8Array 268.00 ns/op 465.00 ns/op 0.58
bytes32 Buffer.toString(hex) + 0x 188.00 ns/op 376.00 ns/op 0.50
Return object 10000 times 0.21520 ns/op 0.22080 ns/op 0.97
Throw Error 10000 times 3.6700 us/op 3.1836 us/op 1.15
toHex 108.69 ns/op 90.370 ns/op 1.20
Buffer.from 93.332 ns/op 87.013 ns/op 1.07
shared Buffer 65.489 ns/op 57.974 ns/op 1.13
fastMsgIdFn sha256 / 200 bytes 1.5160 us/op 1.6180 us/op 0.94
fastMsgIdFn h32 xxhash / 200 bytes 155.00 ns/op 341.00 ns/op 0.45
fastMsgIdFn h64 xxhash / 200 bytes 198.00 ns/op 398.00 ns/op 0.50
fastMsgIdFn sha256 / 1000 bytes 4.8090 us/op 4.7030 us/op 1.02
fastMsgIdFn h32 xxhash / 1000 bytes 250.00 ns/op 420.00 ns/op 0.60
fastMsgIdFn h64 xxhash / 1000 bytes 256.00 ns/op 442.00 ns/op 0.58
fastMsgIdFn sha256 / 10000 bytes 42.575 us/op 39.699 us/op 1.07
fastMsgIdFn h32 xxhash / 10000 bytes 1.3100 us/op 1.4140 us/op 0.93
fastMsgIdFn h64 xxhash / 10000 bytes 849.00 ns/op 996.00 ns/op 0.85
send data - 1000 256B messages 3.9872 ms/op 4.2432 ms/op 0.94
send data - 1000 512B messages 4.5702 ms/op 4.4653 ms/op 1.02
send data - 1000 1024B messages 4.8549 ms/op 4.6328 ms/op 1.05
send data - 1000 1200B messages 4.9781 ms/op 4.7236 ms/op 1.05
send data - 1000 2048B messages 5.0986 ms/op 5.0709 ms/op 1.01
send data - 1000 4096B messages 5.6635 ms/op 5.4717 ms/op 1.04
send data - 1000 16384B messages 55.887 ms/op 26.893 ms/op 2.08
send data - 1000 65536B messages 206.12 ms/op 118.87 ms/op 1.73
enrSubnets - fastDeserialize 64 bits 750.00 ns/op 2.7560 us/op 0.27
enrSubnets - ssz BitVector 64 bits 274.00 ns/op 469.00 ns/op 0.58
enrSubnets - fastDeserialize 4 bits 107.00 ns/op 289.00 ns/op 0.37
enrSubnets - ssz BitVector 4 bits 281.00 ns/op 469.00 ns/op 0.60
prioritizePeers score -10:0 att 32-0.1 sync 2-0 211.34 us/op 204.66 us/op 1.03
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 250.16 us/op 237.76 us/op 1.05
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 412.98 us/op 321.74 us/op 1.28
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 662.45 us/op 574.93 us/op 1.15
prioritizePeers score 0:0 att 64-1 sync 4-1 809.87 us/op 668.81 us/op 1.21
array of 16000 items push then shift 1.3216 us/op 1.2018 us/op 1.10
LinkedList of 16000 items push then shift 7.3870 ns/op 7.2800 ns/op 1.01
array of 16000 items push then pop 71.126 ns/op 61.288 ns/op 1.16
LinkedList of 16000 items push then pop 6.0880 ns/op 5.9470 ns/op 1.02
array of 24000 items push then shift 1.9461 us/op 1.7554 us/op 1.11
LinkedList of 24000 items push then shift 6.9780 ns/op 7.0290 ns/op 0.99
array of 24000 items push then pop 102.01 ns/op 88.562 ns/op 1.15
LinkedList of 24000 items push then pop 6.1400 ns/op 5.9200 ns/op 1.04
intersect bitArray bitLen 8 4.8110 ns/op 4.7290 ns/op 1.02
intersect array and set length 8 29.708 ns/op 27.616 ns/op 1.08
intersect bitArray bitLen 128 24.349 ns/op 22.795 ns/op 1.07
intersect array and set length 128 502.34 ns/op 471.58 ns/op 1.07
bitArray.getTrueBitIndexes() bitLen 128 1.1020 us/op 1.2240 us/op 0.90
bitArray.getTrueBitIndexes() bitLen 248 1.7860 us/op 1.9210 us/op 0.93
bitArray.getTrueBitIndexes() bitLen 512 3.6050 us/op 3.6730 us/op 0.98
Full columns - reconstruct all 6 blobs 181.68 us/op 185.54 us/op 0.98
Full columns - reconstruct half of the blobs out of 6 104.67 us/op 83.781 us/op 1.25
Full columns - reconstruct single blob out of 6 35.443 us/op 32.117 us/op 1.10
Half columns - reconstruct all 6 blobs 391.06 ms/op 361.02 ms/op 1.08
Half columns - reconstruct half of the blobs out of 6 195.90 ms/op 180.09 ms/op 1.09
Half columns - reconstruct single blob out of 6 70.318 ms/op 64.270 ms/op 1.09
Full columns - reconstruct all 10 blobs 223.31 us/op 253.59 us/op 0.88
Full columns - reconstruct half of the blobs out of 10 103.03 us/op 110.62 us/op 0.93
Full columns - reconstruct single blob out of 10 32.159 us/op 31.158 us/op 1.03
Half columns - reconstruct all 10 blobs 651.92 ms/op 598.93 ms/op 1.09
Half columns - reconstruct half of the blobs out of 10 328.16 ms/op 302.04 ms/op 1.09
Half columns - reconstruct single blob out of 10 68.478 ms/op 63.587 ms/op 1.08
Full columns - reconstruct all 20 blobs 1.9731 ms/op 1.3245 ms/op 1.49
Full columns - reconstruct half of the blobs out of 20 179.75 us/op 198.20 us/op 0.91
Full columns - reconstruct single blob out of 20 32.224 us/op 32.138 us/op 1.00
Half columns - reconstruct all 20 blobs 1.3023 s/op 1.1977 s/op 1.09
Half columns - reconstruct half of the blobs out of 20 651.41 ms/op 601.14 ms/op 1.08
Half columns - reconstruct single blob out of 20 70.466 ms/op 64.361 ms/op 1.09
Set add up to 64 items then delete first 2.6483 us/op 2.5188 us/op 1.05
OrderedSet add up to 64 items then delete first 3.4846 us/op 3.2986 us/op 1.06
Set add up to 64 items then delete last 2.4629 us/op 2.3312 us/op 1.06
OrderedSet add up to 64 items then delete last 3.3602 us/op 3.2948 us/op 1.02
Set add up to 64 items then delete middle 2.2139 us/op 2.0719 us/op 1.07
OrderedSet add up to 64 items then delete middle 4.8350 us/op 4.7501 us/op 1.02
Set add up to 128 items then delete first 4.4222 us/op 3.9884 us/op 1.11
OrderedSet add up to 128 items then delete first 6.7169 us/op 6.0270 us/op 1.11
Set add up to 128 items then delete last 4.0323 us/op 3.7795 us/op 1.07
OrderedSet add up to 128 items then delete last 5.9097 us/op 5.7771 us/op 1.02
Set add up to 128 items then delete middle 4.0113 us/op 3.7700 us/op 1.06
OrderedSet add up to 128 items then delete middle 11.764 us/op 11.584 us/op 1.02
Set add up to 256 items then delete first 8.1749 us/op 7.4259 us/op 1.10
OrderedSet add up to 256 items then delete first 12.447 us/op 11.274 us/op 1.10
Set add up to 256 items then delete last 7.9624 us/op 7.4625 us/op 1.07
OrderedSet add up to 256 items then delete last 11.726 us/op 11.581 us/op 1.01
Set add up to 256 items then delete middle 7.9175 us/op 7.3790 us/op 1.07
OrderedSet add up to 256 items then delete middle 35.097 us/op 34.277 us/op 1.02
pass gossip attestations to forkchoice per slot 2.5651 ms/op 2.4626 ms/op 1.04
forkChoice updateHead vc 100000 bc 64 eq 0 436.78 us/op 374.57 us/op 1.17
forkChoice updateHead vc 600000 bc 64 eq 0 2.6013 ms/op 2.2551 ms/op 1.15
forkChoice updateHead vc 1000000 bc 64 eq 0 4.3748 ms/op 3.7118 ms/op 1.18
forkChoice updateHead vc 600000 bc 320 eq 0 2.6432 ms/op 2.2399 ms/op 1.18
forkChoice updateHead vc 600000 bc 1200 eq 0 2.6491 ms/op 2.2729 ms/op 1.17
forkChoice updateHead vc 600000 bc 7200 eq 0 3.1391 ms/op 2.5766 ms/op 1.22
forkChoice updateHead vc 600000 bc 64 eq 1000 3.0255 ms/op 2.8018 ms/op 1.08
forkChoice updateHead vc 600000 bc 64 eq 10000 3.1647 ms/op 2.8798 ms/op 1.10
forkChoice updateHead vc 600000 bc 64 eq 300000 7.2683 ms/op 6.5803 ms/op 1.10
computeDeltas 1400000 validators 0% inactive 12.976 ms/op 12.165 ms/op 1.07
computeDeltas 1400000 validators 10% inactive 12.285 ms/op 11.321 ms/op 1.09
computeDeltas 1400000 validators 20% inactive 11.377 ms/op 10.241 ms/op 1.11
computeDeltas 1400000 validators 50% inactive 8.5403 ms/op 7.9431 ms/op 1.08
computeDeltas 2100000 validators 0% inactive 19.396 ms/op 18.229 ms/op 1.06
computeDeltas 2100000 validators 10% inactive 19.095 ms/op 17.113 ms/op 1.12
computeDeltas 2100000 validators 20% inactive 17.175 ms/op 15.539 ms/op 1.11
computeDeltas 2100000 validators 50% inactive 9.8551 ms/op 9.0408 ms/op 1.09
altair processAttestation - 250000 vs - 7PWei normalcase 1.6999 ms/op 1.8305 ms/op 0.93
altair processAttestation - 250000 vs - 7PWei worstcase 2.5846 ms/op 2.5789 ms/op 1.00
altair processAttestation - setStatus - 1/6 committees join 98.329 us/op 95.840 us/op 1.03
altair processAttestation - setStatus - 1/3 committees join 196.76 us/op 183.46 us/op 1.07
altair processAttestation - setStatus - 1/2 committees join 279.11 us/op 266.27 us/op 1.05
altair processAttestation - setStatus - 2/3 committees join 373.82 us/op 348.44 us/op 1.07
altair processAttestation - setStatus - 4/5 committees join 619.29 us/op 494.93 us/op 1.25
altair processAttestation - setStatus - 100% committees join 595.78 us/op 580.25 us/op 1.03
altair processBlock - 250000 vs - 7PWei normalcase 3.5575 ms/op 3.6915 ms/op 0.96
altair processBlock - 250000 vs - 7PWei normalcase hashState 16.680 ms/op 16.472 ms/op 1.01
altair processBlock - 250000 vs - 7PWei worstcase 23.167 ms/op 20.583 ms/op 1.13
altair processBlock - 250000 vs - 7PWei worstcase hashState 48.218 ms/op 40.372 ms/op 1.19
phase0 processBlock - 250000 vs - 7PWei normalcase 1.4325 ms/op 1.2345 ms/op 1.16
phase0 processBlock - 250000 vs - 7PWei worstcase 19.009 ms/op 16.522 ms/op 1.15
altair processEth1Data - 250000 vs - 7PWei normalcase 316.98 us/op 271.00 us/op 1.17
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.3280 us/op 3.2860 us/op 1.01
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 20.597 us/op 20.790 us/op 0.99
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.7730 us/op 5.8780 us/op 0.98
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.5670 us/op 3.7030 us/op 0.96
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 91.812 us/op 90.552 us/op 1.01
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.4302 ms/op 1.3851 ms/op 1.03
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8740 ms/op 1.8684 ms/op 1.00
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8707 ms/op 1.8384 ms/op 1.02
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.9640 ms/op 4.2828 ms/op 0.93
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.1405 ms/op 2.1035 ms/op 1.02
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.2561 ms/op 4.3794 ms/op 0.97
Tree 40 250000 create 370.40 ms/op 354.17 ms/op 1.05
Tree 40 250000 get(125000) 95.529 ns/op 89.120 ns/op 1.07
Tree 40 250000 set(125000) 1.0592 us/op 981.87 ns/op 1.08
Tree 40 250000 toArray() 20.712 ms/op 16.080 ms/op 1.29
Tree 40 250000 iterate all - toArray() + loop 23.501 ms/op 17.023 ms/op 1.38
Tree 40 250000 iterate all - get(i) 43.770 ms/op 41.014 ms/op 1.07
Array 250000 create 2.4394 ms/op 2.2282 ms/op 1.09
Array 250000 clone - spread 745.49 us/op 676.03 us/op 1.10
Array 250000 get(125000) 0.30500 ns/op 0.47300 ns/op 0.64
Array 250000 set(125000) 0.30700 ns/op 0.48300 ns/op 0.64
Array 250000 iterate all - loop 58.954 us/op 55.851 us/op 1.06
phase0 afterProcessEpoch - 250000 vs - 7PWei 54.026 ms/op 49.632 ms/op 1.09
Array.fill - length 1000000 2.3576 ms/op 2.8222 ms/op 0.84
Array push - length 1000000 10.661 ms/op 9.7501 ms/op 1.09
Array.get 0.21173 ns/op 0.19728 ns/op 1.07
Uint8Array.get 0.24275 ns/op 0.23494 ns/op 1.03
phase0 beforeProcessEpoch - 250000 vs - 7PWei 15.745 ms/op 16.282 ms/op 0.97
altair processEpoch - mainnet_e81889 270.42 ms/op 252.81 ms/op 1.07
mainnet_e81889 - altair beforeProcessEpoch 15.406 ms/op 13.812 ms/op 1.12
mainnet_e81889 - altair processJustificationAndFinalization 5.6830 us/op 6.0030 us/op 0.95
mainnet_e81889 - altair processInactivityUpdates 3.4827 ms/op 3.3170 ms/op 1.05
mainnet_e81889 - altair processRewardsAndPenalties 18.463 ms/op 18.264 ms/op 1.01
mainnet_e81889 - altair processRegistryUpdates 545.00 ns/op 770.00 ns/op 0.71
mainnet_e81889 - altair processSlashings 134.00 ns/op 341.00 ns/op 0.39
mainnet_e81889 - altair processEth1DataReset 128.00 ns/op 340.00 ns/op 0.38
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.7371 ms/op 2.2980 ms/op 0.76
mainnet_e81889 - altair processSlashingsReset 719.00 ns/op 910.00 ns/op 0.79
mainnet_e81889 - altair processRandaoMixesReset 1.1900 us/op 1.4560 us/op 0.82
mainnet_e81889 - altair processHistoricalRootsUpdate 137.00 ns/op 344.00 ns/op 0.40
mainnet_e81889 - altair processParticipationFlagUpdates 425.00 ns/op 654.00 ns/op 0.65
mainnet_e81889 - altair processSyncCommitteeUpdates 106.00 ns/op 318.00 ns/op 0.33
mainnet_e81889 - altair afterProcessEpoch 42.514 ms/op 39.208 ms/op 1.08
capella processEpoch - mainnet_e217614 816.74 ms/op 795.46 ms/op 1.03
mainnet_e217614 - capella beforeProcessEpoch 67.472 ms/op 56.393 ms/op 1.20
mainnet_e217614 - capella processJustificationAndFinalization 6.3410 us/op 6.8960 us/op 0.92
mainnet_e217614 - capella processInactivityUpdates 14.568 ms/op 14.736 ms/op 0.99
mainnet_e217614 - capella processRewardsAndPenalties 88.050 ms/op 92.339 ms/op 0.95
mainnet_e217614 - capella processRegistryUpdates 4.5090 us/op 4.7090 us/op 0.96
mainnet_e217614 - capella processSlashings 134.00 ns/op 349.00 ns/op 0.38
mainnet_e217614 - capella processEth1DataReset 126.00 ns/op 340.00 ns/op 0.37
mainnet_e217614 - capella processEffectiveBalanceUpdates 6.3471 ms/op 14.454 ms/op 0.44
mainnet_e217614 - capella processSlashingsReset 701.00 ns/op 901.00 ns/op 0.78
mainnet_e217614 - capella processRandaoMixesReset 1.2290 us/op 1.4910 us/op 0.82
mainnet_e217614 - capella processHistoricalRootsUpdate 131.00 ns/op 344.00 ns/op 0.38
mainnet_e217614 - capella processParticipationFlagUpdates 435.00 ns/op 648.00 ns/op 0.67
mainnet_e217614 - capella afterProcessEpoch 108.23 ms/op 109.09 ms/op 0.99
phase0 processEpoch - mainnet_e58758 319.27 ms/op 304.83 ms/op 1.05
mainnet_e58758 - phase0 beforeProcessEpoch 60.792 ms/op 64.642 ms/op 0.94
mainnet_e58758 - phase0 processJustificationAndFinalization 6.3020 us/op 6.4690 us/op 0.97
mainnet_e58758 - phase0 processRewardsAndPenalties 15.234 ms/op 15.967 ms/op 0.95
mainnet_e58758 - phase0 processRegistryUpdates 2.3060 us/op 2.4080 us/op 0.96
mainnet_e58758 - phase0 processSlashings 134.00 ns/op 347.00 ns/op 0.39
mainnet_e58758 - phase0 processEth1DataReset 139.00 ns/op 338.00 ns/op 0.41
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 832.95 us/op 804.24 us/op 1.04
mainnet_e58758 - phase0 processSlashingsReset 849.00 ns/op 1.1090 us/op 0.77
mainnet_e58758 - phase0 processRandaoMixesReset 1.2000 us/op 1.6320 us/op 0.74
mainnet_e58758 - phase0 processHistoricalRootsUpdate 223.00 ns/op 354.00 ns/op 0.63
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.0520 us/op 3.2580 us/op 0.32
mainnet_e58758 - phase0 afterProcessEpoch 33.626 ms/op 33.714 ms/op 1.00
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.0055 ms/op 978.86 us/op 1.03
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.2451 ms/op 3.6500 ms/op 0.34
altair processInactivityUpdates - 250000 normalcase 10.528 ms/op 11.915 ms/op 0.88
altair processInactivityUpdates - 250000 worstcase 10.558 ms/op 12.724 ms/op 0.83
phase0 processRegistryUpdates - 250000 normalcase 2.1090 us/op 2.4260 us/op 0.87
phase0 processRegistryUpdates - 250000 badcase_full_deposits 149.50 us/op 141.40 us/op 1.06
phase0 processRegistryUpdates - 250000 worstcase 0.5 63.268 ms/op 63.069 ms/op 1.00
altair processRewardsAndPenalties - 250000 normalcase 16.000 ms/op 14.990 ms/op 1.07
altair processRewardsAndPenalties - 250000 worstcase 13.331 ms/op 14.820 ms/op 0.90
phase0 getAttestationDeltas - 250000 normalcase 5.5399 ms/op 5.4331 ms/op 1.02
phase0 getAttestationDeltas - 250000 worstcase 5.4911 ms/op 5.4342 ms/op 1.01
phase0 processSlashings - 250000 worstcase 60.758 us/op 59.035 us/op 1.03
altair processSyncCommitteeUpdates - 250000 10.278 ms/op 10.543 ms/op 0.97
BeaconState.hashTreeRoot - No change 174.00 ns/op 397.00 ns/op 0.44
BeaconState.hashTreeRoot - 1 full validator 69.529 us/op 87.662 us/op 0.79
BeaconState.hashTreeRoot - 32 full validator 822.43 us/op 911.53 us/op 0.90
BeaconState.hashTreeRoot - 512 full validator 6.8068 ms/op 9.2147 ms/op 0.74
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 90.172 us/op 113.35 us/op 0.80
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.5119 ms/op 1.4529 ms/op 1.04
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 14.746 ms/op 20.199 ms/op 0.73
BeaconState.hashTreeRoot - 1 balances 67.325 us/op 83.173 us/op 0.81
BeaconState.hashTreeRoot - 32 balances 674.34 us/op 777.13 us/op 0.87
BeaconState.hashTreeRoot - 512 balances 5.2636 ms/op 6.0711 ms/op 0.87
BeaconState.hashTreeRoot - 250000 balances 112.23 ms/op 131.55 ms/op 0.85
aggregationBits - 2048 els - zipIndexesInBitList 20.642 us/op 19.667 us/op 1.05
regular array get 100000 times 23.373 us/op 22.377 us/op 1.04
wrappedArray get 100000 times 23.377 us/op 22.434 us/op 1.04
arrayWithProxy get 100000 times 10.590 ms/op 9.7207 ms/op 1.09
ssz.Root.equals 21.804 ns/op 20.915 ns/op 1.04
byteArrayEquals 21.593 ns/op 20.696 ns/op 1.04
Buffer.compare 8.9550 ns/op 8.6120 ns/op 1.04
processSlot - 1 slots 9.8860 us/op 10.360 us/op 0.95
processSlot - 32 slots 2.3134 ms/op 2.0664 ms/op 1.12
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 5.3445 ms/op 6.8620 ms/op 0.78
getCommitteeAssignments - req 1 vs - 250000 vc 1.7182 ms/op 1.6983 ms/op 1.01
getCommitteeAssignments - req 100 vs - 250000 vc 3.4884 ms/op 3.4352 ms/op 1.02
getCommitteeAssignments - req 1000 vs - 250000 vc 3.7933 ms/op 3.6872 ms/op 1.03
findModifiedValidators - 10000 modified validators 836.25 ms/op 822.07 ms/op 1.02
findModifiedValidators - 1000 modified validators 495.73 ms/op 527.98 ms/op 0.94
findModifiedValidators - 100 modified validators 348.32 ms/op 364.80 ms/op 0.95
findModifiedValidators - 10 modified validators 247.96 ms/op 298.67 ms/op 0.83
findModifiedValidators - 1 modified validators 194.62 ms/op 271.52 ms/op 0.72
findModifiedValidators - no difference 161.56 ms/op 265.66 ms/op 0.61
migrate state 1500000 validators, 3400 modified, 2000 new 2.8445 s/op 3.6582 s/op 0.78
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.6300 ns/op 5.6900 ns/op 0.64
state getBlockRootAtSlot - 250000 vs - 7PWei 278.04 ns/op 414.20 ns/op 0.67
computeProposerIndex 100000 validators 1.4678 ms/op 1.3636 ms/op 1.08
getNextSyncCommitteeIndices 1000 validators 2.8769 ms/op 2.8674 ms/op 1.00
getNextSyncCommitteeIndices 10000 validators 25.407 ms/op 25.477 ms/op 1.00
getNextSyncCommitteeIndices 100000 validators 88.104 ms/op 93.386 ms/op 0.94
computeProposers - vc 250000 556.97 us/op 879.16 us/op 0.63
computeEpochShuffling - vc 250000 39.817 ms/op 41.252 ms/op 0.97
getNextSyncCommittee - vc 250000 9.4616 ms/op 11.137 ms/op 0.85
nodejs block root to RootHex using toHex 112.85 ns/op 110.23 ns/op 1.02
nodejs block root to RootHex using toRootHex 72.888 ns/op 67.513 ns/op 1.08
nodejs fromHex(blob) 769.15 us/op 891.56 us/op 0.86
nodejs fromHexInto(blob) 644.64 us/op 621.18 us/op 1.04
nodejs block root to RootHex using the deprecated toHexString 524.26 ns/op 513.90 ns/op 1.02
nodejs byteArrayEquals 32 bytes (block root) 25.947 ns/op 25.820 ns/op 1.00
nodejs byteArrayEquals 48 bytes (pubkey) 37.502 ns/op 37.538 ns/op 1.00
nodejs byteArrayEquals 96 bytes (signature) 35.999 ns/op 31.937 ns/op 1.13
nodejs byteArrayEquals 1024 bytes 43.132 ns/op 37.625 ns/op 1.15
nodejs byteArrayEquals 131072 bytes (blob) 1.7727 us/op 1.7393 us/op 1.02
browser block root to RootHex using toHex 145.33 ns/op 143.66 ns/op 1.01
browser block root to RootHex using toRootHex 129.40 ns/op 130.58 ns/op 0.99
browser fromHex(blob) 1.5679 ms/op 1.6062 ms/op 0.98
browser fromHexInto(blob) 642.31 us/op 624.15 us/op 1.03
browser block root to RootHex using the deprecated toHexString 363.56 ns/op 344.68 ns/op 1.05
browser byteArrayEquals 32 bytes (block root) 27.971 ns/op 27.834 ns/op 1.00
browser byteArrayEquals 48 bytes (pubkey) 39.489 ns/op 39.338 ns/op 1.00
browser byteArrayEquals 96 bytes (signature) 73.856 ns/op 73.164 ns/op 1.01
browser byteArrayEquals 1024 bytes 752.15 ns/op 759.03 ns/op 0.99
browser byteArrayEquals 131072 bytes (blob) 94.473 us/op 94.030 us/op 1.00

by benchmarkbot/action

ensi321 pushed a commit that referenced this pull request Mar 23, 2026
…l gossip types (#9059)

**Motivation**

This PR is a step toward #9025 where we enhance `NetworkProcessor` for
GLOAS.

The network processor's "await unknown block" mechanism was originally
built specifically for attestation messages. As we add support for more
gossip types that depend on block availability (and eventually payload
envelopes in GLOAS), the code needs to be generalized before those new
types are introduced.

**Description**

Generalizes the network processor's unknown-block-waiting mechanism from
attestation-specific to all gossip message types:

- Rename `searchUnknownSlotRoot` → `searchUnknownBlock` across
`INetwork`, `Network`, `NetworkProcessor`, and all call sites
- Flatten the two-level `awaitingGossipsubMessagesByRootBySlot:
MapDef<Slot, MapDef<RootHex, Set<...>>>` into a single
`awaitingBlockByRoot: MapDef<RootHex, Set<...>>`, using a separate
`unknownBlocksBySlot` map for slot-based pruning
- Rename metric group `reprocessGossipAttestations` →
`awaitingBlockGossipMessages`; add a `topic` label so per-topic
breakdown is visible in dashboards
- Add `BlockInputSource.network_processor` to distinguish messages
queued by the processor itself
- Update Grafana dashboard panels to use the new metric names and
`{{topic}}` legend format
- Changed:
- we cannot track pending messages by slot by root because message slot
is not the same to block slot starting gloas
- based on that, expiry windows are changed from 1 slot to 3 slots, to
prepare for gloas. I think this does not affect much because we should
have find the block in the same slot, if not it'll never arrive.

**AI Assistance Disclosure**

Used Claude Code to assist with implementation.

---------

Co-authored-by: Tuyen Nguyen <twoeths@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@twoeths twoeths force-pushed the te/gloas_network_processor branch from 552b9f8 to 7f9d42b Compare March 24, 2026 11:29
@twoeths twoeths marked this pull request as ready for review March 25, 2026 10:05
@twoeths twoeths requested a review from a team as a code owner March 25, 2026 10:05
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: 6ced0c377c

ℹ️ 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/chain.ts Outdated
Comment on lines +454 to +455
// do not await the block, we want UnknownBlockSync to handle it.
preprocessResult = {action: PreprocessAction.PushToQueue};
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 Keep unknown-block execution payload gossip pending

For execution_payload, the preprocessor now always sets PushToQueue even when the referenced block is unknown. In that case validation returns BLOCK_ROOT_UNKNOWN and the envelope is dropped (the new follow-up event is not consumed anywhere in beacon-node), so the message is never retried after block sync. This can leave blocks permanently missing their FULL payload variant and starve envelope-dependent gossip reprocessing.

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.

true concern, this is a TODO for UnknownBlockSync in the next PR
will add that comment

// if validation fails, we will NOT forward this gossip block to peers
// blockInput will then be queued inside BlockInputSync
// if the gossip block is really invalid, it will be pruned there
const blockInput = chain.seenBlockInputCache.getByBlock({
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.

we have dead code without this change, see line 193 below (to handle unknownParent event)

Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts Outdated
Comment thread packages/fork-choice/src/protoArray/protoArray.ts
@@ -182,10 +182,7 @@ export function getSlotFromSingleAttestationSerialized(data: Uint8Array): Slot |
* Extract committee index from SingleAttestation serialized bytes.
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.

I think you should update this comment since you are changing the naming of this function.

Index no longer means committee index post-electra

Comment thread packages/beacon-node/src/chain/chain.ts Outdated
Comment thread packages/beacon-node/src/network/processor/extractSlotRootFns.ts
Copy link
Copy Markdown
Contributor

@lodekeeper lodekeeper left a comment

Choose a reason for hiding this comment

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

Thorough review across bug hunting, architecture, security, maintainability, and approach challenge.

Architecture: The two-round extraction + PreprocessAction enum is a good model, but the envelope recovery path is asymmetric with block recovery — events are emitted with no sync subscriber, and the NetworkProcessor is absorbing protocol-layout interpretation that will grow with each new GLOAS topic type.

Approach: Core design is sound — ePBS needs envelope dependency tracking, and search-vs-await is the right abstraction. But the duplicated queue lifecycle (block-awaiting vs envelope-awaiting) and the fragile override chain in onPendingGossipsubMessage will accumulate maintenance cost. Consider extracting a shared GossipDependencyQueue class and/or a per-topic preprocessing descriptor.

Overall: The functionality is needed and the direction is right. Key concerns are the missing envelope sync path (events fire into the void) and the unbounded envelope search state. Details inline.

Comment thread packages/beacon-node/src/network/processor/index.ts
Comment thread packages/beacon-node/src/network/processor/index.ts
Comment thread packages/beacon-node/src/network/processor/gossipHandlers.ts
Comment thread packages/beacon-node/src/chain/emitter.ts Outdated
Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts Outdated
Comment thread packages/beacon-node/src/network/processor/extractSlotRootFns.ts Outdated
Comment thread packages/beacon-node/src/util/sszBytes.ts
wemeetagain
wemeetagain previously approved these changes Mar 30, 2026
preprocessResult = {action: PreprocessAction.PushToQueue};
break;
}
case GossipType.execution_payload_bid: {
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.

I think we talked about this briefly here https://discord.com/channels/593655374469660673/1372263082415493200/1481401039801417840 I think we didn't reach a conclusion

that maybe we don't need this mechanism for bids. Maybe we shouldn't trigger block and payload search when a bid has unknown block and payload. Maybe we should just discard it.

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.

initially I thought we skipped handling this topic to make the code simpler
but then we discussed that we want to forward this if it's valid to help spread to other nodes https://discord.com/channels/593655374469660673/1372263082415493200/1481599538211848242

now I don't see any down side to handle this

Maybe we shouldn't trigger block and payload search when a bid has unknown block and payload. Maybe we should just discard it.

I think we should search any blocks and payloads that are not known to us, it helps our node to backward sync when we're close to head and handle unstable network (ie get all possible chain segments/forks and decide which one is canonical)

);
}
if (protoBlock?.executionPayloadBlockHash && protoBlock.executionPayloadBlockHash !== parentBlockHash) {
this.searchUnknownEnvelope(
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.

Note that if both parent block and parent payload are missing, then this.searchUnknownEnvelope won't be fired because protoBlock is null due to missing parent block.

Is this intended? If so, then it make sense for L374 to be else if. The code here reads like it is possible to fire both searchUnknownBlock(parentRoot) and searchUnknownEnvelope(parentRoot)

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.

Note that if both parent block and parent payload are missing, then this.searchUnknownEnvelope won't be fired because protoBlock is null due to missing parent block.

in that case we're not sure if there is payload for the block or not so we cannot trigger search. The problem with assuming there is a payload is that in UnknownBlockSync, it assumes peers know that, if it cannot search for that payload, it'll penalize peers

if parent block is missing, here we don't search for payload, and note that this is the 1st attempt, an eager search only. Later on, at gossip handler, we also fire https://github.com/ChainSafe/lodestar/pull/9025/changes#diff-c702dde1ffa90a6d2720c7b4930bc5c08cc1f302cfd09ed9e74d80755b92da8fR194
at that time, UnknownBlockSync should be smart enough to search for payload there when there is a gap between this block and parent block

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.

If so, then it make sense for L374 to be else if. The code here reads like it is possible to fire both searchUnknownBlock(parentRoot) and searchUnknownEnvelope(parentRoot)

switched to else if for better typing + add more comments for the above context in 64ec026

events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage.bind(this));
this.chain.emitter.on(routes.events.EventType.block, this.onBlockProcessed.bind(this));
this.chain.emitter.on(
routes.events.EventType.executionPayloadAvailable,
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.

I just learned the other day that execution_payload_available really only means a payload is available. It doesn't mean payload is verified and valid.
ethereum/beacon-APIs#588 (comment)

So if we listen to this topic, then we are pushing a payload that can be potentially invalid to the queue. But if we listen to execution_payload event instead, which can arrive later, can cause delay on our side. I am not sure which event is the right call here.

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.

switched to awaiting for execution_payload since execution_payload_available is too early
we need to wait for a validated execution_payload anyway, if not waiting messages will be removed from the pending queues in the end
see 546a109

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

execution_payload is definitely the right event here and also mirrors block event, right now I don't see a use case for execution_payload_available outside of notifying the PTC

@twoeths twoeths force-pushed the te/gloas_network_processor branch from 64ec026 to 546a109 Compare March 31, 2026 03:34
nflaig
nflaig previously approved these changes Apr 1, 2026
Copy link
Copy Markdown
Member

@nflaig nflaig left a comment

Choose a reason for hiding this comment

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

looks great, thanks for all the helpful comments, made reviewing much simpler

Comment thread packages/beacon-node/src/chain/seenCache/seenPayloadEnvelopeInput.ts Outdated
Comment thread packages/beacon-node/src/network/processor/extractSlotRootFns.ts Outdated
@nflaig nflaig merged commit e8407e9 into unstable Apr 2, 2026
19 checks passed
@nflaig nflaig deleted the te/gloas_network_processor branch April 2, 2026 04:23
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.52%. Comparing base (8b81ee6) to head (946c38d).
⚠️ Report is 1 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #9025      +/-   ##
============================================
+ Coverage     52.50%   52.52%   +0.01%     
============================================
  Files           848      848              
  Lines         61481    61447      -34     
  Branches       4528     4528              
============================================
- Hits          32282    32272      -10     
+ Misses        29134    29110      -24     
  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.

nflaig added a commit that referenced this pull request Apr 2, 2026
Fulu can cache a BlockInput from data columns before the full beacon block is
available.

PR #9025 narrowed seenBlock() to only treat roots with a cached block as
seen, which caused the network processor to start unknownBlockRoot sync for
roots that were already being assembled locally from columns.

Restore the broader seen-root check for BlockInput cache entries while
keeping hasBlock() available for the narrower "full block present" query.
Add a regression test covering a Fulu column-first cache entry.
nflaig added a commit that referenced this pull request Apr 2, 2026
PR #9025 moved beacon_block parent search into the network processor
preprocessing path for every fork.

For pre-Gloas forks, including Fulu, that is too early. A beacon block should
only trigger unknown-parent recovery after normal gossip validation concludes
that the block is otherwise processable but blocked on a missing parent.
Running the search earlier causes ignored pre-Gloas blocks to start unnecessary
unknown-block sync work.

Keep the proactive beacon_block parent and envelope search Gloas-only and
restore the previous pre-Gloas behavior.
twoeths pushed a commit that referenced this pull request Apr 3, 2026
Follow up to #9025 to fix
attestation data index extraction, currently it seems we extract the
committee index but for how we use it (post gloas), we want the
`data.index`

relevant code

https://github.com/ChainSafe/lodestar/blob/e8407e965790276fae8a22dd7ff57358b9d785e1/packages/beacon-node/src/network/processor/index.ts#L401-L405
twoeths pushed a commit that referenced this pull request Apr 3, 2026
Follow up to #9025, there is
no need to delay processing payload attestations until the payload
envelope is fully imported, the gossip validation and fork choice only
require beacon block to be known.

This is mostly problematic because PTC members will cast their vote
based on the fact that the payload and all data is available, but not
that the payload is valid (as per execution layer) and fully imported.

This timing discrepancy means that it's very likely we receive
`payload_attestation_message` on gossip before we have fully imported
the corresponding payload and currently this means we would delay
re-gossiping and importing these messages for no good reason.
@wemeetagain
Copy link
Copy Markdown
Member

🎉 This PR is included in v1.42.0 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants