Skip to content

Conversation

@nflaig
Copy link
Member

@nflaig nflaig commented Nov 27, 2025

Motivation

Description

Store indexed attestations for each block during block/signature verification to avoid recomputing them during import

  • compute indexedAttestationsByBlock once during verification
  • enhance FullyVerifiedBlock to include indexed attestations for block import

Closes #8625

@nflaig nflaig requested a review from a team as a code owner November 27, 2025 21:37
@gemini-code-assist
Copy link
Contributor

Summary of Changes

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

This pull request implements a significant performance optimization within the block processing pipeline by addressing the redundant calculation of indexedAttestations. Previously, these attestations were computed independently during both the block verification and block import phases. The changes introduce a mechanism to compute indexedAttestations only once during the initial verification step and then pass them along as part of the FullyVerifiedBlock object. This ensures that subsequent operations, such as block import and signature verification, can directly utilize these pre-computed values, thereby reducing duplicate work and improving overall processing efficiency.

Highlights

  • Performance Optimization: Eliminates redundant computation of indexedAttestations by calculating them once during block verification and reusing them during block import.
  • Data Flow Enhancement: The FullyVerifiedBlock type now includes pre-computed indexedAttestations, streamlining data transfer between verification and import stages.
  • Refactored Attestation Handling: Updates importBlock, verifyBlocksInEpoch, verifyBlocksSignatures, and getBlockSignatureSets to leverage the new pre-computed indexedAttestations parameter.
Using Gemini Code Assist

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

Invoking Gemini

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

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

Customization

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

Limitations & Feedback

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

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

Footnotes

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

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses the performance issue of recomputing indexed attestations by introducing a deduplication mechanism. The changes are well-structured, computing the indexed attestations once during verifyBlocksInEpoch and then passing them down for use in signature verification and block import. The implementation is clean, and the necessary updates to data structures, function signatures, and tests are all correctly handled. This is a solid improvement that should reduce redundant computations as intended.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 27, 2025

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 209cd00 Previous: 548d7aa Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 936.98 us/op 930.42 us/op 1.01
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 38.953 us/op 36.236 us/op 1.07
BLS verify - blst 878.16 us/op 867.99 us/op 1.01
BLS verifyMultipleSignatures 3 - blst 1.3005 ms/op 1.3086 ms/op 0.99
BLS verifyMultipleSignatures 8 - blst 1.9974 ms/op 1.9669 ms/op 1.02
BLS verifyMultipleSignatures 32 - blst 5.1691 ms/op 4.9972 ms/op 1.03
BLS verifyMultipleSignatures 64 - blst 10.407 ms/op 9.8835 ms/op 1.05
BLS verifyMultipleSignatures 128 - blst 18.006 ms/op 17.016 ms/op 1.06
BLS deserializing 10000 signatures 719.70 ms/op 697.03 ms/op 1.03
BLS deserializing 100000 signatures 7.0502 s/op 7.0000 s/op 1.01
BLS verifyMultipleSignatures - same message - 3 - blst 909.00 us/op 973.61 us/op 0.93
BLS verifyMultipleSignatures - same message - 8 - blst 999.58 us/op 1.0901 ms/op 0.92
BLS verifyMultipleSignatures - same message - 32 - blst 1.7065 ms/op 1.7495 ms/op 0.98
BLS verifyMultipleSignatures - same message - 64 - blst 2.6345 ms/op 2.6695 ms/op 0.99
BLS verifyMultipleSignatures - same message - 128 - blst 4.4559 ms/op 4.4582 ms/op 1.00
BLS aggregatePubkeys 32 - blst 19.926 us/op 19.749 us/op 1.01
BLS aggregatePubkeys 128 - blst 72.515 us/op 71.079 us/op 1.02
notSeenSlots=1 numMissedVotes=1 numBadVotes=10 53.230 ms/op 51.693 ms/op 1.03
notSeenSlots=1 numMissedVotes=0 numBadVotes=4 52.278 ms/op 49.032 ms/op 1.07
notSeenSlots=2 numMissedVotes=1 numBadVotes=10 38.454 ms/op 38.568 ms/op 1.00
getSlashingsAndExits - default max 77.752 us/op 73.504 us/op 1.06
getSlashingsAndExits - 2k 280.04 us/op 367.92 us/op 0.76
isKnown best case - 1 super set check 210.00 ns/op 206.00 ns/op 1.02
isKnown normal case - 2 super set checks 206.00 ns/op 202.00 ns/op 1.02
isKnown worse case - 16 super set checks 206.00 ns/op 202.00 ns/op 1.02
InMemoryCheckpointStateCache - add get delete 2.3840 us/op 2.3890 us/op 1.00
validate api signedAggregateAndProof - struct 1.4517 ms/op 1.4865 ms/op 0.98
validate gossip signedAggregateAndProof - struct 1.6566 ms/op 1.4470 ms/op 1.14
batch validate gossip attestation - vc 640000 - chunk 32 113.67 us/op 122.01 us/op 0.93
batch validate gossip attestation - vc 640000 - chunk 64 103.26 us/op 108.23 us/op 0.95
batch validate gossip attestation - vc 640000 - chunk 128 96.734 us/op 102.72 us/op 0.94
batch validate gossip attestation - vc 640000 - chunk 256 105.00 us/op 110.17 us/op 0.95
pickEth1Vote - no votes 1.0986 ms/op 980.74 us/op 1.12
pickEth1Vote - max votes 7.9787 ms/op 7.7709 ms/op 1.03
pickEth1Vote - Eth1Data hashTreeRoot value x2048 14.358 ms/op 13.349 ms/op 1.08
pickEth1Vote - Eth1Data hashTreeRoot tree x2048 19.118 ms/op 19.900 ms/op 0.96
pickEth1Vote - Eth1Data fastSerialize value x2048 462.03 us/op 433.52 us/op 1.07
pickEth1Vote - Eth1Data fastSerialize tree x2048 2.4477 ms/op 2.4563 ms/op 1.00
bytes32 toHexString 377.00 ns/op 373.00 ns/op 1.01
bytes32 Buffer.toString(hex) 249.00 ns/op 247.00 ns/op 1.01
bytes32 Buffer.toString(hex) from Uint8Array 329.00 ns/op 336.00 ns/op 0.98
bytes32 Buffer.toString(hex) + 0x 244.00 ns/op 249.00 ns/op 0.98
Object access 1 prop 0.11700 ns/op 0.12500 ns/op 0.94
Map access 1 prop 0.12200 ns/op 0.13400 ns/op 0.91
Object get x1000 6.0940 ns/op 5.8220 ns/op 1.05
Map get x1000 6.6760 ns/op 6.5170 ns/op 1.02
Object set x1000 32.728 ns/op 31.877 ns/op 1.03
Map set x1000 22.369 ns/op 21.298 ns/op 1.05
Return object 10000 times 0.30290 ns/op 0.29410 ns/op 1.03
Throw Error 10000 times 4.6865 us/op 4.3822 us/op 1.07
toHex 140.76 ns/op 140.82 ns/op 1.00
Buffer.from 142.45 ns/op 127.75 ns/op 1.12
shared Buffer 92.191 ns/op 88.892 ns/op 1.04
fastMsgIdFn sha256 / 200 bytes 2.2590 us/op 2.2150 us/op 1.02
fastMsgIdFn h32 xxhash / 200 bytes 210.00 ns/op 203.00 ns/op 1.03
fastMsgIdFn h64 xxhash / 200 bytes 261.00 ns/op 262.00 ns/op 1.00
fastMsgIdFn sha256 / 1000 bytes 7.5350 us/op 7.4480 us/op 1.01
fastMsgIdFn h32 xxhash / 1000 bytes 343.00 ns/op 342.00 ns/op 1.00
fastMsgIdFn h64 xxhash / 1000 bytes 340.00 ns/op 339.00 ns/op 1.00
fastMsgIdFn sha256 / 10000 bytes 68.539 us/op 66.840 us/op 1.03
fastMsgIdFn h32 xxhash / 10000 bytes 1.9040 us/op 1.8390 us/op 1.04
fastMsgIdFn h64 xxhash / 10000 bytes 1.2600 us/op 1.2170 us/op 1.04
100 bytes - compress - snappyjs 1.5959 us/op 1.5388 us/op 1.04
100 bytes - compress - snappy 1.2622 us/op 1.1514 us/op 1.10
200 bytes - compress - snappyjs 2.5172 us/op 1.9755 us/op 1.27
200 bytes - compress - snappy 1.4950 us/op 1.3096 us/op 1.14
300 bytes - compress - snappyjs 2.4570 us/op 2.9092 us/op 0.84
300 bytes - compress - snappy 1.3041 us/op 1.3695 us/op 0.95
400 bytes - compress - snappyjs 2.6025 us/op 3.1368 us/op 0.83
400 bytes - compress - snappy 1.4330 us/op 1.3746 us/op 1.04
500 bytes - compress - snappyjs 3.0655 us/op 3.2497 us/op 0.94
500 bytes - compress - snappy 1.3648 us/op 1.4557 us/op 0.94
1000 bytes - compress - snappyjs 4.6095 us/op 4.4018 us/op 1.05
1000 bytes - compress - snappy 1.6305 us/op 1.9490 us/op 0.84
10000 bytes - compress - snappyjs 32.430 us/op 33.173 us/op 0.98
10000 bytes - compress - snappy 38.430 us/op 41.044 us/op 0.94
100 bytes - uncompress - snappyjs 738.96 ns/op 1.2104 us/op 0.61
100 bytes - uncompress - snappy 1.0894 us/op 1.1073 us/op 0.98
200 bytes - uncompress - snappyjs 994.90 ns/op 1.6762 us/op 0.59
200 bytes - uncompress - snappy 1.0929 us/op 1.0698 us/op 1.02
300 bytes - uncompress - snappyjs 1.0938 us/op 1.4565 us/op 0.75
300 bytes - uncompress - snappy 1.1659 us/op 1.1131 us/op 1.05
400 bytes - uncompress - snappyjs 1.3735 us/op 1.5967 us/op 0.86
400 bytes - uncompress - snappy 1.5149 us/op 1.2693 us/op 1.19
500 bytes - uncompress - snappyjs 1.7277 us/op 1.8874 us/op 0.92
500 bytes - uncompress - snappy 1.3025 us/op 1.2474 us/op 1.04
1000 bytes - uncompress - snappyjs 2.5649 us/op 2.7853 us/op 0.92
1000 bytes - uncompress - snappy 1.8684 us/op 1.5457 us/op 1.21
10000 bytes - uncompress - snappyjs 15.951 us/op 14.725 us/op 1.08
10000 bytes - uncompress - snappy 28.918 us/op 30.164 us/op 0.96
send data - 1000 256B messages 18.314 ms/op 17.988 ms/op 1.02
send data - 1000 512B messages 25.728 ms/op 22.664 ms/op 1.14
send data - 1000 1024B messages 30.268 ms/op 32.989 ms/op 0.92
send data - 1000 1200B messages 31.397 ms/op 32.566 ms/op 0.96
send data - 1000 2048B messages 30.867 ms/op 28.950 ms/op 1.07
send data - 1000 4096B messages 38.339 ms/op 34.075 ms/op 1.13
send data - 1000 16384B messages 115.45 ms/op 51.049 ms/op 2.26
send data - 1000 65536B messages 275.19 ms/op 119.80 ms/op 2.30
enrSubnets - fastDeserialize 64 bits 987.00 ns/op 901.00 ns/op 1.10
enrSubnets - ssz BitVector 64 bits 359.00 ns/op 331.00 ns/op 1.08
enrSubnets - fastDeserialize 4 bits 142.00 ns/op 126.00 ns/op 1.13
enrSubnets - ssz BitVector 4 bits 479.00 ns/op 333.00 ns/op 1.44
prioritizePeers score -10:0 att 32-0.1 sync 2-0 262.38 us/op 238.96 us/op 1.10
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 280.28 us/op 268.03 us/op 1.05
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 407.94 us/op 392.07 us/op 1.04
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 777.12 us/op 726.23 us/op 1.07
prioritizePeers score 0:0 att 64-1 sync 4-1 944.60 us/op 874.15 us/op 1.08
array of 16000 items push then shift 1.7069 us/op 1.6738 us/op 1.02
LinkedList of 16000 items push then shift 8.7550 ns/op 7.5640 ns/op 1.16
array of 16000 items push then pop 86.150 ns/op 84.294 ns/op 1.02
LinkedList of 16000 items push then pop 8.6050 ns/op 8.0060 ns/op 1.07
array of 24000 items push then shift 2.5849 us/op 2.3882 us/op 1.08
LinkedList of 24000 items push then shift 9.0030 ns/op 7.5790 ns/op 1.19
array of 24000 items push then pop 120.30 ns/op 111.10 ns/op 1.08
LinkedList of 24000 items push then pop 8.6840 ns/op 8.5430 ns/op 1.02
intersect bitArray bitLen 8 9.8780 ns/op 6.4600 ns/op 1.53
intersect array and set length 8 48.668 ns/op 37.644 ns/op 1.29
intersect bitArray bitLen 128 33.333 ns/op 29.570 ns/op 1.13
intersect array and set length 128 680.03 ns/op 627.74 ns/op 1.08
bitArray.getTrueBitIndexes() bitLen 128 1.1330 us/op 1.0170 us/op 1.11
bitArray.getTrueBitIndexes() bitLen 248 1.9970 us/op 1.7850 us/op 1.12
bitArray.getTrueBitIndexes() bitLen 512 4.2200 us/op 3.7850 us/op 1.11
Full columns - reconstruct all 6 blobs 114.27 us/op 112.83 us/op 1.01
Full columns - reconstruct half of the blobs out of 6 57.105 us/op 44.118 us/op 1.29
Full columns - reconstruct single blob out of 6 25.807 us/op 20.005 us/op 1.29
Half columns - reconstruct all 6 blobs 309.08 ms/op 279.40 ms/op 1.11
Half columns - reconstruct half of the blobs out of 6 160.02 ms/op 137.91 ms/op 1.16
Half columns - reconstruct single blob out of 6 61.891 ms/op 57.368 ms/op 1.08
Full columns - reconstruct all 10 blobs 236.41 us/op 153.69 us/op 1.54
Full columns - reconstruct half of the blobs out of 10 114.19 us/op 72.639 us/op 1.57
Full columns - reconstruct single blob out of 10 27.957 us/op 21.622 us/op 1.29
Half columns - reconstruct all 10 blobs 541.29 ms/op 488.60 ms/op 1.11
Half columns - reconstruct half of the blobs out of 10 270.25 ms/op 243.56 ms/op 1.11
Half columns - reconstruct single blob out of 10 64.453 ms/op 51.053 ms/op 1.26
Full columns - reconstruct all 20 blobs 453.80 us/op 301.38 us/op 1.51
Full columns - reconstruct half of the blobs out of 20 227.55 us/op 145.38 us/op 1.57
Full columns - reconstruct single blob out of 20 27.922 us/op 21.550 us/op 1.30
Half columns - reconstruct all 20 blobs 1.0214 s/op 907.67 ms/op 1.13
Half columns - reconstruct half of the blobs out of 20 498.83 ms/op 460.94 ms/op 1.08
Half columns - reconstruct single blob out of 20 59.374 ms/op 51.574 ms/op 1.15
Buffer.concat 32 items 728.00 ns/op 618.00 ns/op 1.18
Uint8Array.set 32 items 1.4960 us/op 1.3590 us/op 1.10
Buffer.copy 2.8540 us/op 2.1440 us/op 1.33
Uint8Array.set - with subarray 2.0120 us/op 1.6900 us/op 1.19
Uint8Array.set - without subarray 1.2790 us/op 1.2770 us/op 1.00
getUint32 - dataview 212.00 ns/op 200.00 ns/op 1.06
getUint32 - manual 134.00 ns/op 125.00 ns/op 1.07
Set add up to 64 items then delete first 2.1750 us/op 2.3611 us/op 0.92
OrderedSet add up to 64 items then delete first 4.6020 us/op 3.3344 us/op 1.38
Set add up to 64 items then delete last 2.5193 us/op 2.5730 us/op 0.98
OrderedSet add up to 64 items then delete last 5.2423 us/op 4.2142 us/op 1.24
Set add up to 64 items then delete middle 3.4665 us/op 2.8116 us/op 1.23
OrderedSet add up to 64 items then delete middle 7.1528 us/op 6.2742 us/op 1.14
Set add up to 128 items then delete first 6.8656 us/op 5.7595 us/op 1.19
OrderedSet add up to 128 items then delete first 11.475 us/op 8.9823 us/op 1.28
Set add up to 128 items then delete last 6.8659 us/op 5.3006 us/op 1.30
OrderedSet add up to 128 items then delete last 10.824 us/op 8.8718 us/op 1.22
Set add up to 128 items then delete middle 6.6897 us/op 5.3215 us/op 1.26
OrderedSet add up to 128 items then delete middle 18.119 us/op 14.919 us/op 1.21
Set add up to 256 items then delete first 13.485 us/op 11.655 us/op 1.16
OrderedSet add up to 256 items then delete first 22.722 us/op 16.590 us/op 1.37
Set add up to 256 items then delete last 12.488 us/op 10.198 us/op 1.22
OrderedSet add up to 256 items then delete last 20.184 us/op 15.086 us/op 1.34
Set add up to 256 items then delete middle 12.891 us/op 9.4703 us/op 1.36
OrderedSet add up to 256 items then delete middle 50.397 us/op 43.032 us/op 1.17
transfer serialized Status (84 B) 2.4460 us/op 2.1850 us/op 1.12
copy serialized Status (84 B) 1.3530 us/op 1.1910 us/op 1.14
transfer serialized SignedVoluntaryExit (112 B) 2.4990 us/op 2.2380 us/op 1.12
copy serialized SignedVoluntaryExit (112 B) 1.3430 us/op 1.2140 us/op 1.11
transfer serialized ProposerSlashing (416 B) 2.5770 us/op 2.2860 us/op 1.13
copy serialized ProposerSlashing (416 B) 2.2030 us/op 1.2600 us/op 1.75
transfer serialized Attestation (485 B) 2.6220 us/op 2.3140 us/op 1.13
copy serialized Attestation (485 B) 2.0770 us/op 1.2850 us/op 1.62
transfer serialized AttesterSlashing (33232 B) 2.6670 us/op 2.5190 us/op 1.06
copy serialized AttesterSlashing (33232 B) 5.2590 us/op 4.3090 us/op 1.22
transfer serialized Small SignedBeaconBlock (128000 B) 4.4790 us/op 4.1020 us/op 1.09
copy serialized Small SignedBeaconBlock (128000 B) 19.792 us/op 14.120 us/op 1.40
transfer serialized Avg SignedBeaconBlock (200000 B) 5.1620 us/op 4.2770 us/op 1.21
copy serialized Avg SignedBeaconBlock (200000 B) 27.167 us/op 18.530 us/op 1.47
transfer serialized BlobsSidecar (524380 B) 5.8860 us/op 5.1500 us/op 1.14
copy serialized BlobsSidecar (524380 B) 89.261 us/op 110.96 us/op 0.80
transfer serialized Big SignedBeaconBlock (1000000 B) 7.4240 us/op 5.2780 us/op 1.41
copy serialized Big SignedBeaconBlock (1000000 B) 180.83 us/op 126.67 us/op 1.43
pass gossip attestations to forkchoice per slot 3.7364 ms/op 2.7269 ms/op 1.37
forkChoice updateHead vc 100000 bc 64 eq 0 527.88 us/op 471.24 us/op 1.12
forkChoice updateHead vc 600000 bc 64 eq 0 3.1623 ms/op 2.8099 ms/op 1.13
forkChoice updateHead vc 1000000 bc 64 eq 0 5.2089 ms/op 4.6809 ms/op 1.11
forkChoice updateHead vc 600000 bc 320 eq 0 4.5384 ms/op 2.8330 ms/op 1.60
forkChoice updateHead vc 600000 bc 1200 eq 0 3.4757 ms/op 2.8651 ms/op 1.21
forkChoice updateHead vc 600000 bc 7200 eq 0 3.7003 ms/op 3.1782 ms/op 1.16
forkChoice updateHead vc 600000 bc 64 eq 1000 3.1728 ms/op 2.9129 ms/op 1.09
forkChoice updateHead vc 600000 bc 64 eq 10000 3.2611 ms/op 3.0470 ms/op 1.07
forkChoice updateHead vc 600000 bc 64 eq 300000 10.925 ms/op 9.5957 ms/op 1.14
computeDeltas 1400000 validators 0% inactive 15.204 ms/op 13.791 ms/op 1.10
computeDeltas 1400000 validators 10% inactive 14.687 ms/op 12.810 ms/op 1.15
computeDeltas 1400000 validators 20% inactive 14.366 ms/op 11.513 ms/op 1.25
computeDeltas 1400000 validators 50% inactive 9.8120 ms/op 8.7964 ms/op 1.12
computeDeltas 2100000 validators 0% inactive 23.261 ms/op 20.615 ms/op 1.13
computeDeltas 2100000 validators 10% inactive 21.653 ms/op 18.817 ms/op 1.15
computeDeltas 2100000 validators 20% inactive 20.417 ms/op 17.006 ms/op 1.20
computeDeltas 2100000 validators 50% inactive 14.263 ms/op 13.015 ms/op 1.10
altair processAttestation - 250000 vs - 7PWei normalcase 2.6810 ms/op 2.0712 ms/op 1.29
altair processAttestation - 250000 vs - 7PWei worstcase 3.3162 ms/op 2.8745 ms/op 1.15
altair processAttestation - setStatus - 1/6 committees join 158.04 us/op 121.54 us/op 1.30
altair processAttestation - setStatus - 1/3 committees join 283.15 us/op 245.14 us/op 1.16
altair processAttestation - setStatus - 1/2 committees join 394.66 us/op 327.93 us/op 1.20
altair processAttestation - setStatus - 2/3 committees join 493.87 us/op 425.43 us/op 1.16
altair processAttestation - setStatus - 4/5 committees join 710.07 us/op 588.04 us/op 1.21
altair processAttestation - setStatus - 100% committees join 821.15 us/op 688.86 us/op 1.19
altair processBlock - 250000 vs - 7PWei normalcase 4.8621 ms/op 4.1833 ms/op 1.16
altair processBlock - 250000 vs - 7PWei normalcase hashState 32.037 ms/op 32.414 ms/op 0.99
altair processBlock - 250000 vs - 7PWei worstcase 38.557 ms/op 32.092 ms/op 1.20
altair processBlock - 250000 vs - 7PWei worstcase hashState 76.479 ms/op 67.404 ms/op 1.13
phase0 processBlock - 250000 vs - 7PWei normalcase 1.7950 ms/op 1.6079 ms/op 1.12
phase0 processBlock - 250000 vs - 7PWei worstcase 25.099 ms/op 21.154 ms/op 1.19
altair processEth1Data - 250000 vs - 7PWei normalcase 366.50 us/op 329.68 us/op 1.11
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:15 7.2030 us/op 6.4230 us/op 1.12
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:219 54.095 us/op 45.606 us/op 1.19
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:42 12.539 us/op 11.790 us/op 1.06
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:18 7.6120 us/op 6.4040 us/op 1.19
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1020 183.61 us/op 146.44 us/op 1.25
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11777 1.9246 ms/op 1.8073 ms/op 1.06
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.6877 ms/op 2.3754 ms/op 1.13
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.6433 ms/op 2.2702 ms/op 1.16
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 5.5759 ms/op 4.6174 ms/op 1.21
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.7136 ms/op 2.3823 ms/op 1.14
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 6.0109 ms/op 4.5779 ms/op 1.31
Tree 40 250000 create 527.89 ms/op 427.18 ms/op 1.24
Tree 40 250000 get(125000) 144.26 ns/op 135.06 ns/op 1.07
Tree 40 250000 set(125000) 2.1511 us/op 1.4687 us/op 1.46
Tree 40 250000 toArray() 22.822 ms/op 15.538 ms/op 1.47
Tree 40 250000 iterate all - toArray() + loop 22.385 ms/op 15.381 ms/op 1.46
Tree 40 250000 iterate all - get(i) 63.117 ms/op 49.062 ms/op 1.29
Array 250000 create 4.4106 ms/op 3.6792 ms/op 1.20
Array 250000 clone - spread 1.6903 ms/op 784.59 us/op 2.15
Array 250000 get(125000) 0.41600 ns/op 0.40600 ns/op 1.02
Array 250000 set(125000) 0.44300 ns/op 0.43000 ns/op 1.03
Array 250000 iterate all - loop 113.86 us/op 100.67 us/op 1.13
phase0 afterProcessEpoch - 250000 vs - 7PWei 43.394 ms/op 39.972 ms/op 1.09
Array.fill - length 1000000 6.3219 ms/op 3.2827 ms/op 1.93
Array push - length 1000000 17.433 ms/op 13.586 ms/op 1.28
Array.get 0.29210 ns/op 0.27835 ns/op 1.05
Uint8Array.get 0.45869 ns/op 0.43404 ns/op 1.06
phase0 beforeProcessEpoch - 250000 vs - 7PWei 19.880 ms/op 16.106 ms/op 1.23
altair processEpoch - mainnet_e81889 303.91 ms/op 278.45 ms/op 1.09
mainnet_e81889 - altair beforeProcessEpoch 19.430 ms/op 18.917 ms/op 1.03
mainnet_e81889 - altair processJustificationAndFinalization 5.7530 us/op 5.4360 us/op 1.06
mainnet_e81889 - altair processInactivityUpdates 5.4633 ms/op 4.2138 ms/op 1.30
mainnet_e81889 - altair processRewardsAndPenalties 45.151 ms/op 37.470 ms/op 1.21
mainnet_e81889 - altair processRegistryUpdates 778.00 ns/op 651.00 ns/op 1.20
mainnet_e81889 - altair processSlashings 180.00 ns/op 180.00 ns/op 1.00
mainnet_e81889 - altair processEth1DataReset 177.00 ns/op 175.00 ns/op 1.01
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.2295 ms/op 1.2018 ms/op 1.02
mainnet_e81889 - altair processSlashingsReset 939.00 ns/op 832.00 ns/op 1.13
mainnet_e81889 - altair processRandaoMixesReset 1.1980 us/op 1.0900 us/op 1.10
mainnet_e81889 - altair processHistoricalRootsUpdate 180.00 ns/op 174.00 ns/op 1.03
mainnet_e81889 - altair processParticipationFlagUpdates 543.00 ns/op 503.00 ns/op 1.08
mainnet_e81889 - altair processSyncCommitteeUpdates 138.00 ns/op 141.00 ns/op 0.98
mainnet_e81889 - altair afterProcessEpoch 45.133 ms/op 43.873 ms/op 1.03
capella processEpoch - mainnet_e217614 998.94 ms/op 831.10 ms/op 1.20
mainnet_e217614 - capella beforeProcessEpoch 63.241 ms/op 60.844 ms/op 1.04
mainnet_e217614 - capella processJustificationAndFinalization 5.4810 us/op 5.1700 us/op 1.06
mainnet_e217614 - capella processInactivityUpdates 15.141 ms/op 14.075 ms/op 1.08
mainnet_e217614 - capella processRewardsAndPenalties 160.23 ms/op 175.72 ms/op 0.91
mainnet_e217614 - capella processRegistryUpdates 6.5610 us/op 6.2760 us/op 1.05
mainnet_e217614 - capella processSlashings 172.00 ns/op 173.00 ns/op 0.99
mainnet_e217614 - capella processEth1DataReset 171.00 ns/op 172.00 ns/op 0.99
mainnet_e217614 - capella processEffectiveBalanceUpdates 4.1964 ms/op 10.310 ms/op 0.41
mainnet_e217614 - capella processSlashingsReset 879.00 ns/op 858.00 ns/op 1.02
mainnet_e217614 - capella processRandaoMixesReset 1.1440 us/op 1.1130 us/op 1.03
mainnet_e217614 - capella processHistoricalRootsUpdate 174.00 ns/op 172.00 ns/op 1.01
mainnet_e217614 - capella processParticipationFlagUpdates 516.00 ns/op 517.00 ns/op 1.00
mainnet_e217614 - capella afterProcessEpoch 116.29 ms/op 112.62 ms/op 1.03
phase0 processEpoch - mainnet_e58758 268.37 ms/op 276.32 ms/op 0.97
mainnet_e58758 - phase0 beforeProcessEpoch 72.816 ms/op 73.257 ms/op 0.99
mainnet_e58758 - phase0 processJustificationAndFinalization 5.5290 us/op 5.5570 us/op 0.99
mainnet_e58758 - phase0 processRewardsAndPenalties 36.721 ms/op 39.694 ms/op 0.93
mainnet_e58758 - phase0 processRegistryUpdates 3.2550 us/op 3.1000 us/op 1.05
mainnet_e58758 - phase0 processSlashings 182.00 ns/op 178.00 ns/op 1.02
mainnet_e58758 - phase0 processEth1DataReset 175.00 ns/op 172.00 ns/op 1.02
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.2877 ms/op 1.1940 ms/op 1.08
mainnet_e58758 - phase0 processSlashingsReset 995.00 ns/op 935.00 ns/op 1.06
mainnet_e58758 - phase0 processRandaoMixesReset 1.2600 us/op 1.2050 us/op 1.05
mainnet_e58758 - phase0 processHistoricalRootsUpdate 178.00 ns/op 177.00 ns/op 1.01
mainnet_e58758 - phase0 processParticipationRecordUpdates 922.00 ns/op 891.00 ns/op 1.03
mainnet_e58758 - phase0 afterProcessEpoch 36.507 ms/op 41.204 ms/op 0.89
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.3580 ms/op 1.3691 ms/op 0.99
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 3.3946 ms/op 2.5541 ms/op 1.33
altair processInactivityUpdates - 250000 normalcase 19.645 ms/op 16.882 ms/op 1.16
altair processInactivityUpdates - 250000 worstcase 18.055 ms/op 18.353 ms/op 0.98
phase0 processRegistryUpdates - 250000 normalcase 10.868 us/op 6.7410 us/op 1.61
phase0 processRegistryUpdates - 250000 badcase_full_deposits 284.59 us/op 255.95 us/op 1.11
phase0 processRegistryUpdates - 250000 worstcase 0.5 97.855 ms/op 96.545 ms/op 1.01
altair processRewardsAndPenalties - 250000 normalcase 26.287 ms/op 26.161 ms/op 1.00
altair processRewardsAndPenalties - 250000 worstcase 26.912 ms/op 25.134 ms/op 1.07
phase0 getAttestationDeltas - 250000 normalcase 7.6077 ms/op 6.8177 ms/op 1.12
phase0 getAttestationDeltas - 250000 worstcase 14.403 ms/op 5.8273 ms/op 2.47
phase0 processSlashings - 250000 worstcase 94.489 us/op 85.852 us/op 1.10
altair processSyncCommitteeUpdates - 250000 11.323 ms/op 10.980 ms/op 1.03
BeaconState.hashTreeRoot - No change 206.00 ns/op 210.00 ns/op 0.98
BeaconState.hashTreeRoot - 1 full validator 76.461 us/op 97.167 us/op 0.79
BeaconState.hashTreeRoot - 32 full validator 927.32 us/op 775.74 us/op 1.20
BeaconState.hashTreeRoot - 512 full validator 11.678 ms/op 9.0847 ms/op 1.29
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 102.84 us/op 92.188 us/op 1.12
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.2867 ms/op 1.3500 ms/op 0.95
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 18.398 ms/op 22.820 ms/op 0.81
BeaconState.hashTreeRoot - 1 balances 71.484 us/op 74.885 us/op 0.95
BeaconState.hashTreeRoot - 32 balances 673.27 us/op 805.19 us/op 0.84
BeaconState.hashTreeRoot - 512 balances 8.0436 ms/op 8.2607 ms/op 0.97
BeaconState.hashTreeRoot - 250000 balances 219.99 ms/op 168.03 ms/op 1.31
aggregationBits - 2048 els - zipIndexesInBitList 22.192 us/op 21.714 us/op 1.02
byteArrayEquals 32 54.724 ns/op 53.562 ns/op 1.02
Buffer.compare 32 17.609 ns/op 16.989 ns/op 1.04
byteArrayEquals 1024 1.6008 us/op 1.5795 us/op 1.01
Buffer.compare 1024 26.437 ns/op 25.180 ns/op 1.05
byteArrayEquals 16384 25.559 us/op 25.041 us/op 1.02
Buffer.compare 16384 203.85 ns/op 225.83 ns/op 0.90
byteArrayEquals 123687377 190.82 ms/op 192.24 ms/op 0.99
Buffer.compare 123687377 6.0693 ms/op 7.0428 ms/op 0.86
byteArrayEquals 32 - diff last byte 51.471 ns/op 51.878 ns/op 0.99
Buffer.compare 32 - diff last byte 16.731 ns/op 16.821 ns/op 0.99
byteArrayEquals 1024 - diff last byte 1.5552 us/op 1.5819 us/op 0.98
Buffer.compare 1024 - diff last byte 25.013 ns/op 24.712 ns/op 1.01
byteArrayEquals 16384 - diff last byte 24.788 us/op 25.204 us/op 0.98
Buffer.compare 16384 - diff last byte 197.58 ns/op 194.10 ns/op 1.02
byteArrayEquals 123687377 - diff last byte 190.81 ms/op 187.06 ms/op 1.02
Buffer.compare 123687377 - diff last byte 8.3251 ms/op 6.3965 ms/op 1.30
byteArrayEquals 32 - random bytes 5.2290 ns/op 4.9590 ns/op 1.05
Buffer.compare 32 - random bytes 17.291 ns/op 16.636 ns/op 1.04
byteArrayEquals 1024 - random bytes 5.1600 ns/op 4.9450 ns/op 1.04
Buffer.compare 1024 - random bytes 17.566 ns/op 16.554 ns/op 1.06
byteArrayEquals 16384 - random bytes 5.1330 ns/op 4.9260 ns/op 1.04
Buffer.compare 16384 - random bytes 19.738 ns/op 16.457 ns/op 1.20
byteArrayEquals 123687377 - random bytes 6.4500 ns/op 6.2500 ns/op 1.03
Buffer.compare 123687377 - random bytes 18.430 ns/op 17.830 ns/op 1.03
regular array get 100000 times 37.743 us/op 42.507 us/op 0.89
wrappedArray get 100000 times 33.829 us/op 31.602 us/op 1.07
arrayWithProxy get 100000 times 13.105 ms/op 17.787 ms/op 0.74
ssz.Root.equals 46.557 ns/op 44.705 ns/op 1.04
byteArrayEquals 45.684 ns/op 43.852 ns/op 1.04
Buffer.compare 10.593 ns/op 10.072 ns/op 1.05
processSlot - 1 slots 10.422 us/op 10.122 us/op 1.03
processSlot - 32 slots 2.6697 ms/op 3.3013 ms/op 0.81
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 3.8958 ms/op 2.8532 ms/op 1.37
getCommitteeAssignments - req 1 vs - 250000 vc 2.1407 ms/op 2.0683 ms/op 1.04
getCommitteeAssignments - req 100 vs - 250000 vc 4.2215 ms/op 4.0247 ms/op 1.05
getCommitteeAssignments - req 1000 vs - 250000 vc 4.5042 ms/op 4.3005 ms/op 1.05
findModifiedValidators - 10000 modified validators 772.87 ms/op 728.18 ms/op 1.06
findModifiedValidators - 1000 modified validators 734.16 ms/op 668.63 ms/op 1.10
findModifiedValidators - 100 modified validators 296.26 ms/op 212.87 ms/op 1.39
findModifiedValidators - 10 modified validators 158.61 ms/op 139.00 ms/op 1.14
findModifiedValidators - 1 modified validators 146.99 ms/op 159.02 ms/op 0.92
findModifiedValidators - no difference 180.26 ms/op 208.95 ms/op 0.86
compare ViewDUs 6.7522 s/op 6.0907 s/op 1.11
compare each validator Uint8Array 1.3391 s/op 1.5051 s/op 0.89
compare ViewDU to Uint8Array 1.3506 s/op 1.0481 s/op 1.29
migrate state 1000000 validators, 24 modified, 0 new 942.75 ms/op 890.15 ms/op 1.06
migrate state 1000000 validators, 1700 modified, 1000 new 1.2057 s/op 1.4564 s/op 0.83
migrate state 1000000 validators, 3400 modified, 2000 new 1.3864 s/op 1.7953 s/op 0.77
migrate state 1500000 validators, 24 modified, 0 new 1.1929 s/op 1.1681 s/op 1.02
migrate state 1500000 validators, 1700 modified, 1000 new 1.4939 s/op 1.5469 s/op 0.97
migrate state 1500000 validators, 3400 modified, 2000 new 1.6269 s/op 1.9849 s/op 0.82
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.5700 ns/op 4.4400 ns/op 1.03
state getBlockRootAtSlot - 250000 vs - 7PWei 968.42 ns/op 572.49 ns/op 1.69
naive computeProposerIndex 100000 validators 54.438 ms/op 79.483 ms/op 0.68
computeProposerIndex 100000 validators 1.4837 ms/op 1.6447 ms/op 0.90
naiveGetNextSyncCommitteeIndices 1000 validators 7.7353 s/op 11.225 s/op 0.69
getNextSyncCommitteeIndices 1000 validators 115.98 ms/op 140.63 ms/op 0.82
naiveGetNextSyncCommitteeIndices 10000 validators 7.7197 s/op 10.201 s/op 0.76
getNextSyncCommitteeIndices 10000 validators 117.67 ms/op 122.97 ms/op 0.96
naiveGetNextSyncCommitteeIndices 100000 validators 8.4337 s/op 9.4836 s/op 0.89
getNextSyncCommitteeIndices 100000 validators 126.95 ms/op 143.47 ms/op 0.88
naive computeShuffledIndex 100000 validators 24.900 s/op 31.771 s/op 0.78
cached computeShuffledIndex 100000 validators 543.87 ms/op 574.19 ms/op 0.95
naive computeShuffledIndex 2000000 validators 506.69 s/op 483.44 s/op 1.05
cached computeShuffledIndex 2000000 validators 31.969 s/op 41.901 s/op 0.76
computeProposers - vc 250000 623.04 us/op 760.07 us/op 0.82
computeEpochShuffling - vc 250000 41.358 ms/op 42.008 ms/op 0.98
getNextSyncCommittee - vc 250000 10.319 ms/op 10.535 ms/op 0.98
computeSigningRoot for AttestationData 20.202 us/op 21.442 us/op 0.94
hash AttestationData serialized data then Buffer.toString(base64) 1.6114 us/op 1.6005 us/op 1.01
toHexString serialized data 1.1051 us/op 1.2181 us/op 0.91
Buffer.toString(base64) 154.82 ns/op 169.04 ns/op 0.92
nodejs block root to RootHex using toHex 150.66 ns/op 152.79 ns/op 0.99
nodejs block root to RootHex using toRootHex 92.866 ns/op 87.425 ns/op 1.06
nodejs fromHex(blob) 117.33 us/op 120.97 us/op 0.97
nodejs fromHexInto(blob) 834.47 us/op 829.29 us/op 1.01
nodejs block root to RootHex using the deprecated toHexString 208.49 ns/op 218.28 ns/op 0.96
browser block root to RootHex using toHex 173.41 ns/op 176.33 ns/op 0.98
browser block root to RootHex using toRootHex 163.73 ns/op 166.35 ns/op 0.98
browser fromHex(blob) 781.21 us/op 799.93 us/op 0.98
browser fromHexInto(blob) 819.27 us/op 827.60 us/op 0.99
browser block root to RootHex using the deprecated toHexString 822.79 ns/op 837.78 ns/op 0.98

by benchmarkbot/action

@nflaig nflaig changed the title fix: deduplicate getIndexedAttestation() during verify/import block fix: avoid recomputing indexed attestation during block import Nov 28, 2025
@nflaig nflaig changed the title fix: avoid recomputing indexed attestation during block import fix: avoid recomputing indexed attestations during block import Nov 28, 2025
twoeths
twoeths previously approved these changes Nov 28, 2025
Copy link
Contributor

@twoeths twoeths left a comment

Choose a reason for hiding this comment

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

looks good to me

  • on feat4 mainnet node
Image
  • same type of node on unstable
Image
  • same type of node on stable
Image

@nflaig
Copy link
Member Author

nflaig commented Dec 2, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an optimization to avoid recomputing indexed attestations during block import by pre-computing and caching them during block verification. The changes are well-implemented and correctly propagate the pre-computed indexedAttestations through the block processing pipeline, from verifyBlocksInEpoch to importBlock and verifyBlocksSignatures. My review includes a couple of suggestions to improve code clarity and maintainability.

@nflaig
Copy link
Member Author

nflaig commented Dec 3, 2025

I redeployed this yesterday to feat4, import block time is reduced which is what this PR should improve

image

@nflaig nflaig merged commit bc1fed4 into unstable Dec 3, 2025
20 of 26 checks passed
@nflaig nflaig deleted the nflaig/deduplicate-indexed-attestations branch December 3, 2025 13:49
@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.02%. Comparing base (548d7aa) to head (26d9efb).
⚠️ Report is 1 commits behind head on unstable.

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #8637   +/-   ##
=========================================
  Coverage     52.02%   52.02%           
=========================================
  Files           848      848           
  Lines         65837    65815   -22     
  Branches       4811     4810    -1     
=========================================
- Hits          34250    34242    -8     
+ Misses        31518    31504   -14     
  Partials         69       69           
🚀 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.

@wemeetagain
Copy link
Member

🎉 This PR is included in v1.38.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.

Duplicate getIndexedAttestation() during verifying/importing blocks

4 participants