Skip to content

feat: add PTC data availability and quorum tracking#9284

Open
wemeetagain wants to merge 5 commits intounstablefrom
cayman/ptc-quorum-tracking
Open

feat: add PTC data availability and quorum tracking#9284
wemeetagain wants to merge 5 commits intounstablefrom
cayman/ptc-quorum-tracking

Conversation

@wemeetagain
Copy link
Copy Markdown
Member

Motivation

  • potentially want to understand when PTC untimely and data unavailable votes get cast.

Description

  • add data availability PTC tracking
  • track nay votes separately from yea votes
  • cache quorum reached status
  • emit event when quorum is reached, yea or nay

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 implements tracking and event emission for Payload Timeliness Committee (PTC) quorums concerning both payload timeliness and data availability. The protoArray was updated to manage separate bitsets for positive and negative votes for each category, and new lifecycle events were added to the beacon node emitter. Feedback suggests refining the vote recording logic in protoArray.ts to ensure that a validator's previous vote is cleared when a new one is processed, preventing potential double-counting across 'yea' and 'nay' bitsets.

Comment on lines +696 to +697
votes.payloadTimely[payloadPresentKey].set(ptcIndex, true);
votes.dataAvailable[dataAvailableKey].set(ptcIndex, true);
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.

medium

The current implementation allows a validator to be counted in both the 'yea' and 'nay' bitsets simultaneously if they send multiple messages with different values (e.g., due to equivocation or late updates). To maintain the 'one vote per validator' principle and ensure accurate quorum counting, the bit should be cleared in the opposite bitset when a new vote is recorded.

      votes.payloadTimely[payloadPresentKey].set(ptcIndex, true);
      votes.payloadTimely[1 - payloadPresentKey].set(ptcIndex, false);
      votes.dataAvailable[dataAvailableKey].set(ptcIndex, true);
      votes.dataAvailable[1 - dataAvailableKey].set(ptcIndex, false);

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 26, 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: 37efb30 Previous: a45ba75 Ratio
Full columns - reconstruct all 20 blobs 1.7383 ms/op 522.13 us/op 3.33
Full benchmark results
Benchmark suite Current: 37efb30 Previous: a45ba75 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.1414 ms/op 889.26 us/op 1.28
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 38.808 us/op 40.969 us/op 0.95
BLS verify - blst 716.81 us/op 736.97 us/op 0.97
BLS verifyMultipleSignatures 3 - blst 1.3910 ms/op 1.3834 ms/op 1.01
BLS verifyMultipleSignatures 8 - blst 2.2075 ms/op 2.2183 ms/op 1.00
BLS verifyMultipleSignatures 32 - blst 7.0063 ms/op 7.0435 ms/op 0.99
BLS verifyMultipleSignatures 64 - blst 13.472 ms/op 13.653 ms/op 0.99
BLS verifyMultipleSignatures 128 - blst 25.992 ms/op 26.513 ms/op 0.98
BLS deserializing 10000 signatures 643.54 ms/op 652.80 ms/op 0.99
BLS deserializing 100000 signatures 6.4297 s/op 6.5006 s/op 0.99
BLS verifyMultipleSignatures - same message - 3 - blst 809.73 us/op 729.99 us/op 1.11
BLS verifyMultipleSignatures - same message - 8 - blst 850.52 us/op 932.10 us/op 0.91
BLS verifyMultipleSignatures - same message - 32 - blst 1.4887 ms/op 1.5616 ms/op 0.95
BLS verifyMultipleSignatures - same message - 64 - blst 2.4760 ms/op 2.3315 ms/op 1.06
BLS verifyMultipleSignatures - same message - 128 - blst 4.1373 ms/op 4.1384 ms/op 1.00
BLS aggregatePubkeys 32 - blst 17.761 us/op 18.075 us/op 0.98
BLS aggregatePubkeys 128 - blst 62.712 us/op 64.620 us/op 0.97
getSlashingsAndExits - default max 47.403 us/op 50.429 us/op 0.94
getSlashingsAndExits - 2k 340.37 us/op 377.97 us/op 0.90
proposeBlockBody type=full, size=empty 801.71 us/op 2.0609 ms/op 0.39
isKnown best case - 1 super set check 393.00 ns/op 177.00 ns/op 2.22
isKnown normal case - 2 super set checks 396.00 ns/op 185.00 ns/op 2.14
isKnown worse case - 16 super set checks 388.00 ns/op 185.00 ns/op 2.10
validate api signedAggregateAndProof - struct 1.5579 ms/op 1.5535 ms/op 1.00
validate gossip signedAggregateAndProof - struct 1.5573 ms/op 1.5623 ms/op 1.00
batch validate gossip attestation - vc 640000 - chunk 32 107.15 us/op 108.18 us/op 0.99
batch validate gossip attestation - vc 640000 - chunk 64 92.901 us/op 95.761 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 128 86.959 us/op 89.701 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 256 83.220 us/op 86.089 us/op 0.97
bytes32 toHexString 497.00 ns/op 309.00 ns/op 1.61
bytes32 Buffer.toString(hex) 385.00 ns/op 181.00 ns/op 2.13
bytes32 Buffer.toString(hex) from Uint8Array 459.00 ns/op 263.00 ns/op 1.75
bytes32 Buffer.toString(hex) + 0x 383.00 ns/op 186.00 ns/op 2.06
Return object 10000 times 0.23140 ns/op 0.21970 ns/op 1.05
Throw Error 10000 times 3.3351 us/op 3.3616 us/op 0.99
toHex 94.825 ns/op 106.12 ns/op 0.89
Buffer.from 87.945 ns/op 88.089 ns/op 1.00
shared Buffer 59.228 ns/op 59.775 ns/op 0.99
fastMsgIdFn sha256 / 200 bytes 1.6940 us/op 1.5200 us/op 1.11
fastMsgIdFn h32 xxhash / 200 bytes 352.00 ns/op 163.00 ns/op 2.16
fastMsgIdFn h64 xxhash / 200 bytes 406.00 ns/op 215.00 ns/op 1.89
fastMsgIdFn sha256 / 1000 bytes 4.9990 us/op 4.8830 us/op 1.02
fastMsgIdFn h32 xxhash / 1000 bytes 442.00 ns/op 256.00 ns/op 1.73
fastMsgIdFn h64 xxhash / 1000 bytes 459.00 ns/op 266.00 ns/op 1.73
fastMsgIdFn sha256 / 10000 bytes 42.635 us/op 43.101 us/op 0.99
fastMsgIdFn h32 xxhash / 10000 bytes 1.4860 us/op 1.3040 us/op 1.14
fastMsgIdFn h64 xxhash / 10000 bytes 1.0300 us/op 850.00 ns/op 1.21
send data - 1000 256B messages 4.1178 ms/op 4.2273 ms/op 0.97
send data - 1000 512B messages 4.2163 ms/op 4.3759 ms/op 0.96
send data - 1000 1024B messages 4.3302 ms/op 4.6655 ms/op 0.93
send data - 1000 1200B messages 4.7233 ms/op 4.7803 ms/op 0.99
send data - 1000 2048B messages 4.7068 ms/op 5.0277 ms/op 0.94
send data - 1000 4096B messages 5.5187 ms/op 5.7251 ms/op 0.96
send data - 1000 16384B messages 27.460 ms/op 26.902 ms/op 1.02
send data - 1000 65536B messages 186.24 ms/op 216.42 ms/op 0.86
enrSubnets - fastDeserialize 64 bits 944.00 ns/op 851.00 ns/op 1.11
enrSubnets - ssz BitVector 64 bits 492.00 ns/op 273.00 ns/op 1.80
enrSubnets - fastDeserialize 4 bits 299.00 ns/op 114.00 ns/op 2.62
enrSubnets - ssz BitVector 4 bits 496.00 ns/op 278.00 ns/op 1.78
prioritizePeers score -10:0 att 32-0.1 sync 2-0 218.91 us/op 211.30 us/op 1.04
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 244.49 us/op 256.70 us/op 0.95
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 345.89 us/op 365.87 us/op 0.95
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 606.01 us/op 618.55 us/op 0.98
prioritizePeers score 0:0 att 64-1 sync 4-1 704.52 us/op 727.43 us/op 0.97
array of 16000 items push then shift 1.3768 us/op 1.3301 us/op 1.04
LinkedList of 16000 items push then shift 7.3960 ns/op 7.6990 ns/op 0.96
array of 16000 items push then pop 67.859 ns/op 70.078 ns/op 0.97
LinkedList of 16000 items push then pop 6.2940 ns/op 6.2310 ns/op 1.01
array of 24000 items push then shift 2.1902 us/op 1.9744 us/op 1.11
LinkedList of 24000 items push then shift 6.9580 ns/op 7.1850 ns/op 0.97
array of 24000 items push then pop 98.220 ns/op 101.51 ns/op 0.97
LinkedList of 24000 items push then pop 6.2930 ns/op 6.3620 ns/op 0.99
intersect bitArray bitLen 8 4.9500 ns/op 4.9510 ns/op 1.00
intersect array and set length 8 29.466 ns/op 30.589 ns/op 0.96
intersect bitArray bitLen 128 24.279 ns/op 26.129 ns/op 0.93
intersect array and set length 128 502.13 ns/op 517.07 ns/op 0.97
bitArray.getTrueBitIndexes() bitLen 128 1.2190 us/op 1.0700 us/op 1.14
bitArray.getTrueBitIndexes() bitLen 248 1.9620 us/op 1.8190 us/op 1.08
bitArray.getTrueBitIndexes() bitLen 512 3.7770 us/op 3.7180 us/op 1.02
Full columns - reconstruct all 6 blobs 203.64 us/op 158.63 us/op 1.28
Full columns - reconstruct half of the blobs out of 6 65.487 us/op 72.022 us/op 0.91
Full columns - reconstruct single blob out of 6 34.521 us/op 37.923 us/op 0.91
Half columns - reconstruct all 6 blobs 386.92 ms/op 409.00 ms/op 0.95
Half columns - reconstruct half of the blobs out of 6 190.28 ms/op 203.02 ms/op 0.94
Half columns - reconstruct single blob out of 6 67.162 ms/op 72.488 ms/op 0.93
Full columns - reconstruct all 10 blobs 222.74 us/op 184.03 us/op 1.21
Full columns - reconstruct half of the blobs out of 10 103.15 us/op 143.80 us/op 0.72
Full columns - reconstruct single blob out of 10 37.622 us/op 33.125 us/op 1.14
Half columns - reconstruct all 10 blobs 629.82 ms/op 661.61 ms/op 0.95
Half columns - reconstruct half of the blobs out of 10 316.99 ms/op 333.84 ms/op 0.95
Half columns - reconstruct single blob out of 10 67.145 ms/op 71.459 ms/op 0.94
Full columns - reconstruct all 20 blobs 1.7383 ms/op 522.13 us/op 3.33
Full columns - reconstruct half of the blobs out of 20 200.73 us/op 168.44 us/op 1.19
Full columns - reconstruct single blob out of 20 33.054 us/op 33.357 us/op 0.99
Half columns - reconstruct all 20 blobs 1.2582 s/op 1.3194 s/op 0.95
Half columns - reconstruct half of the blobs out of 20 633.52 ms/op 662.39 ms/op 0.96
Half columns - reconstruct single blob out of 20 67.345 ms/op 72.193 ms/op 0.93
Set add up to 64 items then delete first 2.2059 us/op 2.7727 us/op 0.80
OrderedSet add up to 64 items then delete first 3.4371 us/op 3.3589 us/op 1.02
Set add up to 64 items then delete last 2.1701 us/op 2.4064 us/op 0.90
OrderedSet add up to 64 items then delete last 3.4975 us/op 3.3277 us/op 1.05
Set add up to 64 items then delete middle 2.2164 us/op 2.1434 us/op 1.03
OrderedSet add up to 64 items then delete middle 5.0121 us/op 4.8339 us/op 1.04
Set add up to 128 items then delete first 4.3017 us/op 4.2722 us/op 1.01
OrderedSet add up to 128 items then delete first 6.3897 us/op 6.6371 us/op 0.96
Set add up to 128 items then delete last 4.0306 us/op 3.9754 us/op 1.01
OrderedSet add up to 128 items then delete last 6.1462 us/op 5.8383 us/op 1.05
Set add up to 128 items then delete middle 4.0162 us/op 3.9006 us/op 1.03
OrderedSet add up to 128 items then delete middle 12.161 us/op 11.835 us/op 1.03
Set add up to 256 items then delete first 7.9896 us/op 7.9610 us/op 1.00
OrderedSet add up to 256 items then delete first 11.777 us/op 12.388 us/op 0.95
Set add up to 256 items then delete last 7.9386 us/op 7.7408 us/op 1.03
OrderedSet add up to 256 items then delete last 12.195 us/op 11.633 us/op 1.05
Set add up to 256 items then delete middle 7.8706 us/op 7.6952 us/op 1.02
OrderedSet add up to 256 items then delete middle 35.869 us/op 35.702 us/op 1.00
pass gossip attestations to forkchoice per slot 2.6797 ms/op 2.6026 ms/op 1.03
forkChoice updateHead vc 100000 bc 64 eq 0 426.04 us/op 413.26 us/op 1.03
forkChoice updateHead vc 600000 bc 64 eq 0 2.4412 ms/op 2.4434 ms/op 1.00
forkChoice updateHead vc 1000000 bc 64 eq 0 4.1255 ms/op 4.1221 ms/op 1.00
forkChoice updateHead vc 600000 bc 320 eq 0 2.5111 ms/op 2.5093 ms/op 1.00
forkChoice updateHead vc 600000 bc 1200 eq 0 2.4982 ms/op 2.4974 ms/op 1.00
forkChoice updateHead vc 600000 bc 7200 eq 0 2.8114 ms/op 2.8557 ms/op 0.98
forkChoice updateHead vc 600000 bc 64 eq 1000 2.9671 ms/op 3.0008 ms/op 0.99
forkChoice updateHead vc 600000 bc 64 eq 10000 3.0768 ms/op 3.1294 ms/op 0.98
forkChoice updateHead vc 600000 bc 64 eq 300000 7.1036 ms/op 7.0038 ms/op 1.01
computeDeltas 1400000 validators 0% inactive 12.846 ms/op 12.820 ms/op 1.00
computeDeltas 1400000 validators 10% inactive 12.080 ms/op 12.059 ms/op 1.00
computeDeltas 1400000 validators 20% inactive 11.146 ms/op 11.132 ms/op 1.00
computeDeltas 1400000 validators 50% inactive 8.4566 ms/op 8.4633 ms/op 1.00
computeDeltas 2100000 validators 0% inactive 19.157 ms/op 19.223 ms/op 1.00
computeDeltas 2100000 validators 10% inactive 18.312 ms/op 17.928 ms/op 1.02
computeDeltas 2100000 validators 20% inactive 17.554 ms/op 16.431 ms/op 1.07
computeDeltas 2100000 validators 50% inactive 9.9446 ms/op 9.7608 ms/op 1.02
altair processAttestation - 250000 vs - 7PWei normalcase 1.7033 ms/op 2.1470 ms/op 0.79
altair processAttestation - 250000 vs - 7PWei worstcase 2.4702 ms/op 2.5349 ms/op 0.97
altair processAttestation - setStatus - 1/6 committees join 100.37 us/op 105.51 us/op 0.95
altair processAttestation - setStatus - 1/3 committees join 197.67 us/op 205.75 us/op 0.96
altair processAttestation - setStatus - 1/2 committees join 292.17 us/op 296.25 us/op 0.99
altair processAttestation - setStatus - 2/3 committees join 381.99 us/op 370.66 us/op 1.03
altair processAttestation - setStatus - 4/5 committees join 512.56 us/op 524.47 us/op 0.98
altair processAttestation - setStatus - 100% committees join 602.43 us/op 620.33 us/op 0.97
altair processBlock - 250000 vs - 7PWei normalcase 2.9624 ms/op 3.7722 ms/op 0.79
altair processBlock - 250000 vs - 7PWei normalcase hashState 12.015 ms/op 17.765 ms/op 0.68
altair processBlock - 250000 vs - 7PWei worstcase 19.975 ms/op 21.462 ms/op 0.93
altair processBlock - 250000 vs - 7PWei worstcase hashState 47.032 ms/op 44.647 ms/op 1.05
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3626 ms/op 1.4065 ms/op 0.97
phase0 processBlock - 250000 vs - 7PWei worstcase 17.411 ms/op 16.748 ms/op 1.04
altair processEth1Data - 250000 vs - 7PWei normalcase 293.23 us/op 312.58 us/op 0.94
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 5.4510 us/op 4.4500 us/op 1.22
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 26.864 us/op 21.600 us/op 1.24
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 6.5900 us/op 5.7420 us/op 1.15
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 4.6610 us/op 3.6770 us/op 1.27
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 97.786 us/op 95.447 us/op 1.02
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3976 ms/op 1.4096 ms/op 0.99
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8283 ms/op 1.8601 ms/op 0.98
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8319 ms/op 1.8826 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.9360 ms/op 4.4551 ms/op 0.88
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.0805 ms/op 2.3057 ms/op 0.90
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.2021 ms/op 4.6733 ms/op 0.90
Tree 40 250000 create 310.33 ms/op 332.26 ms/op 0.93
Tree 40 250000 get(125000) 105.46 ns/op 96.104 ns/op 1.10
Tree 40 250000 set(125000) 1.0297 us/op 1.1928 us/op 0.86
Tree 40 250000 toArray() 9.0927 ms/op 10.873 ms/op 0.84
Tree 40 250000 iterate all - toArray() + loop 9.3219 ms/op 10.482 ms/op 0.89
Tree 40 250000 iterate all - get(i) 35.361 ms/op 34.421 ms/op 1.03
Array 250000 create 2.0561 ms/op 2.2392 ms/op 0.92
Array 250000 clone - spread 668.77 us/op 719.15 us/op 0.93
Array 250000 get(125000) 0.48600 ns/op 0.30700 ns/op 1.58
Array 250000 set(125000) 0.49600 ns/op 0.31300 ns/op 1.58
Array 250000 iterate all - loop 56.625 us/op 58.362 us/op 0.97
phase0 afterProcessEpoch - 250000 vs - 7PWei 63.408 ms/op 39.406 ms/op 1.61
Array.fill - length 1000000 2.2731 ms/op 2.2274 ms/op 1.02
Array push - length 1000000 8.8556 ms/op 8.0394 ms/op 1.10
Array.get 0.20833 ns/op 0.20866 ns/op 1.00
Uint8Array.get 0.23987 ns/op 0.25783 ns/op 0.93
phase0 beforeProcessEpoch - 250000 vs - 7PWei 13.038 ms/op 11.961 ms/op 1.09
altair processEpoch - mainnet_e81889 245.57 ms/op 258.04 ms/op 0.95
mainnet_e81889 - altair beforeProcessEpoch 21.054 ms/op 16.473 ms/op 1.28
mainnet_e81889 - altair processJustificationAndFinalization 4.8910 us/op 6.0720 us/op 0.81
mainnet_e81889 - altair processInactivityUpdates 4.3784 ms/op 4.2029 ms/op 1.04
mainnet_e81889 - altair processRewardsAndPenalties 17.503 ms/op 20.082 ms/op 0.87
mainnet_e81889 - altair processRegistryUpdates 785.00 ns/op 560.00 ns/op 1.40
mainnet_e81889 - altair processSlashings 368.00 ns/op 147.00 ns/op 2.50
mainnet_e81889 - altair processEth1DataReset 366.00 ns/op 147.00 ns/op 2.49
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.7452 ms/op 1.6734 ms/op 1.04
mainnet_e81889 - altair processSlashingsReset 950.00 ns/op 731.00 ns/op 1.30
mainnet_e81889 - altair processRandaoMixesReset 1.2430 us/op 1.3850 us/op 0.90
mainnet_e81889 - altair processHistoricalRootsUpdate 359.00 ns/op 148.00 ns/op 2.43
mainnet_e81889 - altair processParticipationFlagUpdates 669.00 ns/op 449.00 ns/op 1.49
mainnet_e81889 - altair processSyncCommitteeUpdates 331.00 ns/op 128.00 ns/op 2.59
mainnet_e81889 - altair afterProcessEpoch 40.841 ms/op 42.963 ms/op 0.95
capella processEpoch - mainnet_e217614 749.50 ms/op 832.94 ms/op 0.90
mainnet_e217614 - capella beforeProcessEpoch 60.605 ms/op 61.801 ms/op 0.98
mainnet_e217614 - capella processJustificationAndFinalization 5.1530 us/op 5.8220 us/op 0.89
mainnet_e217614 - capella processInactivityUpdates 13.733 ms/op 12.796 ms/op 1.07
mainnet_e217614 - capella processRewardsAndPenalties 91.296 ms/op 91.603 ms/op 1.00
mainnet_e217614 - capella processRegistryUpdates 4.7330 us/op 4.5830 us/op 1.03
mainnet_e217614 - capella processSlashings 365.00 ns/op 148.00 ns/op 2.47
mainnet_e217614 - capella processEth1DataReset 366.00 ns/op 144.00 ns/op 2.54
mainnet_e217614 - capella processEffectiveBalanceUpdates 5.3791 ms/op 5.2937 ms/op 1.02
mainnet_e217614 - capella processSlashingsReset 928.00 ns/op 684.00 ns/op 1.36
mainnet_e217614 - capella processRandaoMixesReset 1.3590 us/op 1.1670 us/op 1.16
mainnet_e217614 - capella processHistoricalRootsUpdate 366.00 ns/op 144.00 ns/op 2.54
mainnet_e217614 - capella processParticipationFlagUpdates 675.00 ns/op 429.00 ns/op 1.57
mainnet_e217614 - capella afterProcessEpoch 112.84 ms/op 109.39 ms/op 1.03
phase0 processEpoch - mainnet_e58758 250.05 ms/op 260.29 ms/op 0.96
mainnet_e58758 - phase0 beforeProcessEpoch 48.393 ms/op 47.879 ms/op 1.01
mainnet_e58758 - phase0 processJustificationAndFinalization 5.1130 us/op 5.1230 us/op 1.00
mainnet_e58758 - phase0 processRewardsAndPenalties 15.486 ms/op 15.525 ms/op 1.00
mainnet_e58758 - phase0 processRegistryUpdates 2.5190 us/op 2.2730 us/op 1.11
mainnet_e58758 - phase0 processSlashings 364.00 ns/op 138.00 ns/op 2.64
mainnet_e58758 - phase0 processEth1DataReset 367.00 ns/op 135.00 ns/op 2.72
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 838.52 us/op 923.01 us/op 0.91
mainnet_e58758 - phase0 processSlashingsReset 1.0640 us/op 817.00 ns/op 1.30
mainnet_e58758 - phase0 processRandaoMixesReset 1.9730 us/op 1.0250 us/op 1.92
mainnet_e58758 - phase0 processHistoricalRootsUpdate 378.00 ns/op 141.00 ns/op 2.68
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.2960 us/op 985.00 ns/op 1.32
mainnet_e58758 - phase0 afterProcessEpoch 34.442 ms/op 33.730 ms/op 1.02
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.0144 ms/op 1.0240 ms/op 0.99
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.8311 ms/op 1.6066 ms/op 1.14
altair processInactivityUpdates - 250000 normalcase 11.545 ms/op 11.275 ms/op 1.02
altair processInactivityUpdates - 250000 worstcase 11.576 ms/op 11.256 ms/op 1.03
phase0 processRegistryUpdates - 250000 normalcase 2.8590 us/op 2.1390 us/op 1.34
phase0 processRegistryUpdates - 250000 badcase_full_deposits 151.78 us/op 150.49 us/op 1.01
phase0 processRegistryUpdates - 250000 worstcase 0.5 56.651 ms/op 53.464 ms/op 1.06
altair processRewardsAndPenalties - 250000 normalcase 13.774 ms/op 15.027 ms/op 0.92
altair processRewardsAndPenalties - 250000 worstcase 13.396 ms/op 14.850 ms/op 0.90
phase0 getAttestationDeltas - 250000 normalcase 5.5559 ms/op 5.6649 ms/op 0.98
phase0 getAttestationDeltas - 250000 worstcase 5.4730 ms/op 5.6640 ms/op 0.97
phase0 processSlashings - 250000 worstcase 58.700 us/op 63.332 us/op 0.93
altair processSyncCommitteeUpdates - 250000 10.217 ms/op 10.627 ms/op 0.96
BeaconState.hashTreeRoot - No change 422.00 ns/op 201.00 ns/op 2.10
BeaconState.hashTreeRoot - 1 full validator 75.216 us/op 67.202 us/op 1.12
BeaconState.hashTreeRoot - 32 full validator 754.46 us/op 946.61 us/op 0.80
BeaconState.hashTreeRoot - 512 full validator 8.8606 ms/op 6.6579 ms/op 1.33
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 98.420 us/op 77.559 us/op 1.27
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.3703 ms/op 1.1096 ms/op 1.23
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 17.431 ms/op 14.357 ms/op 1.21
BeaconState.hashTreeRoot - 1 balances 77.436 us/op 68.916 us/op 1.12
BeaconState.hashTreeRoot - 32 balances 662.62 us/op 642.38 us/op 1.03
BeaconState.hashTreeRoot - 512 balances 6.4085 ms/op 5.8713 ms/op 1.09
BeaconState.hashTreeRoot - 250000 balances 123.24 ms/op 110.07 ms/op 1.12
aggregationBits - 2048 els - zipIndexesInBitList 19.314 us/op 20.721 us/op 0.93
regular array get 100000 times 23.090 us/op 23.473 us/op 0.98
wrappedArray get 100000 times 23.039 us/op 23.528 us/op 0.98
arrayWithProxy get 100000 times 10.308 ms/op 14.425 ms/op 0.71
ssz.Root.equals 21.831 ns/op 22.188 ns/op 0.98
byteArrayEquals 21.597 ns/op 21.974 ns/op 0.98
Buffer.compare 9.0930 ns/op 9.1260 ns/op 1.00
processSlot - 1 slots 9.0650 us/op 9.7950 us/op 0.93
processSlot - 32 slots 1.7195 ms/op 1.8174 ms/op 0.95
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 3.2149 ms/op 4.1481 ms/op 0.78
getCommitteeAssignments - req 1 vs - 250000 vc 1.7123 ms/op 1.7529 ms/op 0.98
getCommitteeAssignments - req 100 vs - 250000 vc 3.4712 ms/op 3.5567 ms/op 0.98
getCommitteeAssignments - req 1000 vs - 250000 vc 3.7303 ms/op 3.8292 ms/op 0.97
findModifiedValidators - 10000 modified validators 728.08 ms/op 800.56 ms/op 0.91
findModifiedValidators - 1000 modified validators 469.81 ms/op 530.78 ms/op 0.89
findModifiedValidators - 100 modified validators 263.98 ms/op 306.23 ms/op 0.86
findModifiedValidators - 10 modified validators 230.50 ms/op 300.54 ms/op 0.77
findModifiedValidators - 1 modified validators 178.04 ms/op 218.85 ms/op 0.81
findModifiedValidators - no difference 170.43 ms/op 217.46 ms/op 0.78
migrate state 1500000 validators, 3400 modified, 2000 new 3.1233 s/op 2.9442 s/op 1.06
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 5.6900 ns/op 3.9500 ns/op 1.44
state getBlockRootAtSlot - 250000 vs - 7PWei 296.26 ns/op 334.71 ns/op 0.89
computeProposerIndex 100000 validators 1.3351 ms/op 1.3842 ms/op 0.96
getNextSyncCommitteeIndices 1000 validators 2.9021 ms/op 2.9320 ms/op 0.99
getNextSyncCommitteeIndices 10000 validators 25.453 ms/op 26.684 ms/op 0.95
getNextSyncCommitteeIndices 100000 validators 86.483 ms/op 89.379 ms/op 0.97
computeProposers - vc 250000 531.99 us/op 564.83 us/op 0.94
computeEpochShuffling - vc 250000 40.006 ms/op 40.348 ms/op 0.99
getNextSyncCommittee - vc 250000 9.5716 ms/op 9.5932 ms/op 1.00
nodejs block root to RootHex using toHex 108.32 ns/op 98.062 ns/op 1.10
nodejs block root to RootHex using toRootHex 70.848 ns/op 59.842 ns/op 1.18
nodejs fromHex(blob) 757.04 us/op 878.59 us/op 0.86
nodejs fromHexInto(blob) 650.11 us/op 647.24 us/op 1.00
nodejs block root to RootHex using the deprecated toHexString 540.38 ns/op 484.28 ns/op 1.12
nodejs byteArrayEquals 32 bytes (block root) 26.870 ns/op 26.744 ns/op 1.00
nodejs byteArrayEquals 48 bytes (pubkey) 37.730 ns/op 38.553 ns/op 0.98
nodejs byteArrayEquals 96 bytes (signature) 40.824 ns/op 35.756 ns/op 1.14
nodejs byteArrayEquals 1024 bytes 47.587 ns/op 44.281 ns/op 1.07
nodejs byteArrayEquals 131072 bytes (blob) 1.8065 us/op 1.7808 us/op 1.01
browser block root to RootHex using toHex 147.78 ns/op 147.43 ns/op 1.00
browser block root to RootHex using toRootHex 130.43 ns/op 132.90 ns/op 0.98
browser fromHex(blob) 1.5568 ms/op 1.6236 ms/op 0.96
browser fromHexInto(blob) 658.01 us/op 636.80 us/op 1.03
browser block root to RootHex using the deprecated toHexString 363.50 ns/op 324.65 ns/op 1.12
browser byteArrayEquals 32 bytes (block root) 28.738 ns/op 36.246 ns/op 0.79
browser byteArrayEquals 48 bytes (pubkey) 39.548 ns/op 40.081 ns/op 0.99
browser byteArrayEquals 96 bytes (signature) 73.781 ns/op 75.075 ns/op 0.98
browser byteArrayEquals 1024 bytes 759.13 ns/op 765.57 ns/op 0.99
browser byteArrayEquals 131072 bytes (blob) 95.548 us/op 96.946 us/op 0.99

by benchmarkbot/action

Comment thread packages/fork-choice/src/protoArray/protoArray.ts
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.

generally looks good, but would suggest to review that on top of #9183

@wemeetagain
Copy link
Copy Markdown
Member Author

on top of #9183

funnily enough its entirely orthogonal. Only similarity is the words "data availability" and "fork choice".

@wemeetagain wemeetagain marked this pull request as ready for review April 28, 2026 08:27
@wemeetagain wemeetagain requested a review from a team as a code owner April 28, 2026 08:27
onPTCQuorumPayloadTimely: (blockRoot, payloadTimely) =>
emitter.emit(ChainEvent.forkChoicePTCQuorumPayloadTimely, blockRoot, payloadTimely),
onPTCQuorumDataAvailable: (blockRoot, dataAvailable) =>
emitter.emit(ChainEvent.forkChoicePTCQuorumDataAvailable, blockRoot, dataAvailable),
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.

do we really need two separate events, is there ever a reason (at least right now) to consume these separately?

export type PTCQuorum = boolean | null;
export type PTCVotes = {
votes: BitArray;
payloadTimelyYea: number;
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.

Not a fan of the yea/nay notation.

Maybe payloadTimelyTrue/payloadTimelyFalse or payloadTimelyCount/payloadUntimelyCount or payloadPresentCount/payloadAbsentCount?

export type PTCQuorum = boolean | null;
export type PTCVotes = {
votes: BitArray;
payloadTimelyYea: number;
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.

Also looks like this votes compare to the old BitArray is only to track if a validator has voted yet. We lose the info about whether a validator has voted for timely (and data available) or not because we aggregate them into numbers.
Not sure if we need this info though

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes this is a tradeoff to minimize memory usage here. if we need attribution we can revisit, but i don't think it matters considering there's no carrot or stick incentives here.

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.

4 participants