Skip to content

fix: ptc gossip to dedup by slot not by epoch - #9396

Closed
ensi321 wants to merge 2 commits into
unstablefrom
nc/ptc-gossip-dedup-per-slot
Closed

fix: ptc gossip to dedup by slot not by epoch#9396
ensi321 wants to merge 2 commits into
unstablefrom
nc/ptc-gossip-dedup-per-slot

Conversation

@ensi321

@ensi321 ensi321 commented May 22, 2026

Copy link
Copy Markdown
Member

Follow up on #9369 . Unlike attester, a validator can participate in PTC more than one time per slot and per epoch. Currently our SeenPayloadAttesters assumes only one payload attestation message from a validator per epoch.

This PR updates it so it is per slot

PayloadAttestationMessage gossip dedup was keyed per (epoch, validatorIndex),
which is correct for phase0 attesters (one committee assignment per epoch) but
wrong for PTC. PTC seats are sampled per slot via `compute_ptc`, so a single
validator can be selected into PTC at multiple slots within the same epoch and
must emit one valid message per such slot. The epoch-keyed cache silently
dropped legitimate later-slot messages from the same validator.

Replace `SeenPayloadAttesters extends SeenAttesters` with a stand-alone class
holding two parallel maps:

- per-slot dedup (`isKnown` / `add(slot, ...)`, `prune(slot)`) — mirrors
  `SeenSyncCommitteeMessages` with a 3-slot lookback to cover
  `MAXIMUM_GOSSIP_CLOCK_DISPARITY` and prev-slot stragglers
- per-epoch liveness summary (`isKnownAtEpoch` / `pruneEpoch(epoch)`) — keeps
  the contribution to `validatorSeenAtEpoch` (validator liveness API) that
  the old epoch-keyed cache provided

`add(slot, validatorIndex)` writes both maps atomically. Slot tick prunes the
slot map; epoch tick prunes the epoch map.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

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

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.

Code Review

This pull request refactors the SeenPayloadAttesters cache to implement per-slot deduplication for payload attestations, replacing the previous per-epoch approach. This change allows validators to participate in the PTC multiple times per epoch across different slots while maintaining deduplication within a single slot. The implementation introduces dual tracking via validatorIndexesBySlot and validatorIndexesByEpoch to support both gossip validation and liveness APIs, along with updated pruning logic and unit tests. A review comment suggests adding a defensive check in the add method to prevent inserting data for epochs that have already been pruned, which would improve consistency with other cache implementations in the codebase.

Comment thread packages/beacon-node/src/chain/seenCache/seenAttesters.ts
Mirror SeenAttesters.add by throwing EpochTooLow when the message's epoch is
below lowestPermissibleEpoch. Defensive — the gossip validator's
isCurrentSlotGivenGossipDisparity check already filters stale slots — but
catches programming bugs early and keeps the lowestPermissibleEpoch field
load-bearing instead of write-only.

Addresses PR #9396 review comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: cd84eef Previous: 6c85077 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.1154 ms/op 1.3166 ms/op 0.85
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 39.788 us/op 42.333 us/op 0.94
BLS verify - blst 647.79 us/op 711.16 us/op 0.91
BLS verifyMultipleSignatures 3 - blst 1.3215 ms/op 1.3581 ms/op 0.97
BLS verifyMultipleSignatures 8 - blst 2.0900 ms/op 2.1719 ms/op 0.96
BLS verifyMultipleSignatures 32 - blst 6.6051 ms/op 6.9628 ms/op 0.95
BLS verifyMultipleSignatures 64 - blst 12.935 ms/op 13.266 ms/op 0.98
BLS verifyMultipleSignatures 128 - blst 24.964 ms/op 25.831 ms/op 0.97
BLS deserializing 10000 signatures 632.65 ms/op 636.33 ms/op 0.99
BLS deserializing 100000 signatures 6.1844 s/op 6.3870 s/op 0.97
BLS verifyMultipleSignatures - same message - 3 - blst 736.11 us/op 807.79 us/op 0.91
BLS verifyMultipleSignatures - same message - 8 - blst 804.37 us/op 894.05 us/op 0.90
BLS verifyMultipleSignatures - same message - 32 - blst 1.4848 ms/op 1.5540 ms/op 0.96
BLS verifyMultipleSignatures - same message - 64 - blst 2.2802 ms/op 2.4420 ms/op 0.93
BLS verifyMultipleSignatures - same message - 128 - blst 3.8945 ms/op 4.0683 ms/op 0.96
BLS aggregatePubkeys 32 - blst 17.056 us/op 17.498 us/op 0.97
BLS aggregatePubkeys 128 - blst 61.428 us/op 62.765 us/op 0.98
getSlashingsAndExits - default max 45.393 us/op 47.536 us/op 0.95
getSlashingsAndExits - 2k 334.66 us/op 359.26 us/op 0.93
proposeBlockBody type=full, size=empty 633.83 us/op 692.53 us/op 0.92
isKnown best case - 1 super set check 204.00 ns/op 171.00 ns/op 1.19
isKnown normal case - 2 super set checks 158.00 ns/op 163.00 ns/op 0.97
isKnown worse case - 16 super set checks 158.00 ns/op 162.00 ns/op 0.98
validate api signedAggregateAndProof - struct 1.4427 ms/op 1.5158 ms/op 0.95
validate gossip signedAggregateAndProof - struct 1.4482 ms/op 1.5113 ms/op 0.96
batch validate gossip attestation - vc 640000 - chunk 32 104.65 us/op 106.25 us/op 0.98
batch validate gossip attestation - vc 640000 - chunk 64 91.538 us/op 92.170 us/op 0.99
batch validate gossip attestation - vc 640000 - chunk 128 84.296 us/op 86.859 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 256 80.903 us/op 83.187 us/op 0.97
bytes32 toHexString 276.00 ns/op 285.00 ns/op 0.97
bytes32 Buffer.toString(hex) 163.00 ns/op 172.00 ns/op 0.95
bytes32 Buffer.toString(hex) from Uint8Array 224.00 ns/op 243.00 ns/op 0.92
bytes32 Buffer.toString(hex) + 0x 162.00 ns/op 172.00 ns/op 0.94
Return object 10000 times 0.20870 ns/op 0.21170 ns/op 0.99
Throw Error 10000 times 3.1871 us/op 3.2678 us/op 0.98
toHex 94.562 ns/op 101.58 ns/op 0.93
Buffer.from 79.059 ns/op 88.361 ns/op 0.89
shared Buffer 50.530 ns/op 61.700 ns/op 0.82
fastMsgIdFn sha256 / 200 bytes 1.4240 us/op 1.4730 us/op 0.97
fastMsgIdFn h32 xxhash / 200 bytes 148.00 ns/op 153.00 ns/op 0.97
fastMsgIdFn h64 xxhash / 200 bytes 200.00 ns/op 211.00 ns/op 0.95
fastMsgIdFn sha256 / 1000 bytes 4.6180 us/op 4.7170 us/op 0.98
fastMsgIdFn h32 xxhash / 1000 bytes 236.00 ns/op 244.00 ns/op 0.97
fastMsgIdFn h64 xxhash / 1000 bytes 244.00 ns/op 257.00 ns/op 0.95
fastMsgIdFn sha256 / 10000 bytes 41.470 us/op 41.582 us/op 1.00
fastMsgIdFn h32 xxhash / 10000 bytes 1.2690 us/op 1.2760 us/op 0.99
fastMsgIdFn h64 xxhash / 10000 bytes 818.00 ns/op 832.00 ns/op 0.98
send data - 1000 256B messages 4.0728 ms/op 4.1467 ms/op 0.98
send data - 1000 512B messages 4.2867 ms/op 4.2348 ms/op 1.01
send data - 1000 1024B messages 4.2452 ms/op 4.5559 ms/op 0.93
send data - 1000 1200B messages 4.6858 ms/op 4.6847 ms/op 1.00
send data - 1000 2048B messages 4.8539 ms/op 4.6401 ms/op 1.05
send data - 1000 4096B messages 6.7606 ms/op 5.5728 ms/op 1.21
send data - 1000 16384B messages 36.040 ms/op 19.935 ms/op 1.81
send data - 1000 65536B messages 159.69 ms/op 232.01 ms/op 0.69
enrSubnets - fastDeserialize 64 bits 699.00 ns/op 751.00 ns/op 0.93
enrSubnets - ssz BitVector 64 bits 270.00 ns/op 262.00 ns/op 1.03
enrSubnets - fastDeserialize 4 bits 105.00 ns/op 103.00 ns/op 1.02
enrSubnets - ssz BitVector 4 bits 266.00 ns/op 261.00 ns/op 1.02
prioritizePeers score -10:0 att 32-0.1 sync 2-0 200.84 us/op 208.44 us/op 0.96
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 222.63 us/op 234.34 us/op 0.95
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 327.16 us/op 361.49 us/op 0.91
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 579.85 us/op 603.29 us/op 0.96
prioritizePeers score 0:0 att 64-1 sync 4-1 688.16 us/op 700.91 us/op 0.98
array of 16000 items push then shift 1.2145 us/op 1.2996 us/op 0.93
LinkedList of 16000 items push then shift 7.1470 ns/op 6.8030 ns/op 1.05
array of 16000 items push then pop 73.848 ns/op 66.277 ns/op 1.11
LinkedList of 16000 items push then pop 5.8310 ns/op 5.9170 ns/op 0.99
array of 24000 items push then shift 1.8030 us/op 1.9304 us/op 0.93
LinkedList of 24000 items push then shift 6.7720 ns/op 6.5630 ns/op 1.03
array of 24000 items push then pop 104.69 ns/op 92.432 ns/op 1.13
LinkedList of 24000 items push then pop 5.8300 ns/op 5.9790 ns/op 0.98
intersect bitArray bitLen 8 4.6230 ns/op 4.7380 ns/op 0.98
intersect array and set length 8 28.489 ns/op 29.330 ns/op 0.97
intersect bitArray bitLen 128 23.370 ns/op 24.440 ns/op 0.96
intersect array and set length 128 483.48 ns/op 494.96 ns/op 0.98
bitArray.getTrueBitIndexes() bitLen 128 978.00 ns/op 1.0510 us/op 0.93
bitArray.getTrueBitIndexes() bitLen 248 1.7300 us/op 1.7710 us/op 0.98
bitArray.getTrueBitIndexes() bitLen 512 3.5150 us/op 3.5990 us/op 0.98
Full columns - reconstruct all 6 blobs 207.94 us/op 167.31 us/op 1.24
Full columns - reconstruct half of the blobs out of 6 99.713 us/op 97.485 us/op 1.02
Full columns - reconstruct single blob out of 6 32.326 us/op 35.435 us/op 0.91
Half columns - reconstruct all 6 blobs 377.54 ms/op 377.07 ms/op 1.00
Half columns - reconstruct half of the blobs out of 6 190.53 ms/op 192.52 ms/op 0.99
Half columns - reconstruct single blob out of 6 69.633 ms/op 66.868 ms/op 1.04
Full columns - reconstruct all 10 blobs 249.18 us/op 193.46 us/op 1.29
Full columns - reconstruct half of the blobs out of 10 147.86 us/op 111.90 us/op 1.32
Full columns - reconstruct single blob out of 10 32.956 us/op 32.103 us/op 1.03
Half columns - reconstruct all 10 blobs 641.49 ms/op 678.12 ms/op 0.95
Half columns - reconstruct half of the blobs out of 10 319.96 ms/op 338.76 ms/op 0.94
Half columns - reconstruct single blob out of 10 67.931 ms/op 72.393 ms/op 0.94
Full columns - reconstruct all 20 blobs 574.74 us/op 1.5226 ms/op 0.38
Full columns - reconstruct half of the blobs out of 20 236.59 us/op 214.09 us/op 1.11
Full columns - reconstruct single blob out of 20 30.800 us/op 33.486 us/op 0.92
Half columns - reconstruct all 20 blobs 1.2387 s/op 1.3028 s/op 0.95
Half columns - reconstruct half of the blobs out of 20 624.64 ms/op 648.52 ms/op 0.96
Half columns - reconstruct single blob out of 20 66.547 ms/op 69.243 ms/op 0.96
Set add up to 64 items then delete first 2.0361 us/op 2.7689 us/op 0.74
OrderedSet add up to 64 items then delete first 3.2117 us/op 3.5728 us/op 0.90
Set add up to 64 items then delete last 2.0502 us/op 2.4902 us/op 0.82
OrderedSet add up to 64 items then delete last 3.1768 us/op 3.3930 us/op 0.94
Set add up to 64 items then delete middle 2.0197 us/op 2.1874 us/op 0.92
OrderedSet add up to 64 items then delete middle 4.5910 us/op 4.8976 us/op 0.94
Set add up to 128 items then delete first 4.0162 us/op 4.4085 us/op 0.91
OrderedSet add up to 128 items then delete first 6.3338 us/op 6.7932 us/op 0.93
Set add up to 128 items then delete last 3.9921 us/op 4.1837 us/op 0.95
OrderedSet add up to 128 items then delete last 5.7675 us/op 5.8703 us/op 0.98
Set add up to 128 items then delete middle 3.7464 us/op 3.9395 us/op 0.95
OrderedSet add up to 128 items then delete middle 12.859 us/op 12.008 us/op 1.07
Set add up to 256 items then delete first 7.5693 us/op 8.0746 us/op 0.94
OrderedSet add up to 256 items then delete first 11.609 us/op 12.612 us/op 0.92
Set add up to 256 items then delete last 7.3321 us/op 7.8338 us/op 0.94
OrderedSet add up to 256 items then delete last 11.114 us/op 11.780 us/op 0.94
Set add up to 256 items then delete middle 7.2752 us/op 7.8055 us/op 0.93
OrderedSet add up to 256 items then delete middle 33.748 us/op 36.215 us/op 0.93
pass gossip attestations to forkchoice per slot 2.4647 ms/op 2.6267 ms/op 0.94
forkChoice updateHead vc 100000 bc 64 eq 0 370.06 us/op 417.24 us/op 0.89
forkChoice updateHead vc 600000 bc 64 eq 0 2.2305 ms/op 2.4301 ms/op 0.92
forkChoice updateHead vc 1000000 bc 64 eq 0 3.7304 ms/op 4.1039 ms/op 0.91
forkChoice updateHead vc 600000 bc 320 eq 0 2.3044 ms/op 2.4726 ms/op 0.93
forkChoice updateHead vc 600000 bc 1200 eq 0 2.4274 ms/op 2.5552 ms/op 0.95
forkChoice updateHead vc 600000 bc 7200 eq 0 3.8860 ms/op 2.9114 ms/op 1.33
forkChoice updateHead vc 600000 bc 64 eq 1000 2.9380 ms/op 3.0449 ms/op 0.96
forkChoice updateHead vc 600000 bc 64 eq 10000 3.0899 ms/op 3.1539 ms/op 0.98
forkChoice updateHead vc 600000 bc 64 eq 300000 6.9138 ms/op 7.0715 ms/op 0.98
computeDeltas 1400000 validators 0% inactive 12.707 ms/op 13.987 ms/op 0.91
computeDeltas 1400000 validators 10% inactive 11.847 ms/op 13.231 ms/op 0.90
computeDeltas 1400000 validators 20% inactive 10.606 ms/op 12.047 ms/op 0.88
computeDeltas 1400000 validators 50% inactive 8.3435 ms/op 8.9844 ms/op 0.93
computeDeltas 2100000 validators 0% inactive 19.033 ms/op 21.088 ms/op 0.90
computeDeltas 2100000 validators 10% inactive 17.733 ms/op 19.747 ms/op 0.90
computeDeltas 2100000 validators 20% inactive 16.252 ms/op 18.409 ms/op 0.88
computeDeltas 2100000 validators 50% inactive 9.3294 ms/op 13.624 ms/op 0.68
altair processAttestation - 250000 vs - 7PWei normalcase 1.9889 ms/op 1.6824 ms/op 1.18
altair processAttestation - 250000 vs - 7PWei worstcase 2.9801 ms/op 2.4472 ms/op 1.22
altair processAttestation - setStatus - 1/6 committees join 96.588 us/op 103.00 us/op 0.94
altair processAttestation - setStatus - 1/3 committees join 202.54 us/op 210.03 us/op 0.96
altair processAttestation - setStatus - 1/2 committees join 282.85 us/op 299.83 us/op 0.94
altair processAttestation - setStatus - 2/3 committees join 357.96 us/op 379.87 us/op 0.94
altair processAttestation - setStatus - 4/5 committees join 484.11 us/op 510.79 us/op 0.95
altair processAttestation - setStatus - 100% committees join 571.02 us/op 598.49 us/op 0.95
altair processBlock - 250000 vs - 7PWei normalcase 4.0697 ms/op 2.8368 ms/op 1.43
altair processBlock - 250000 vs - 7PWei normalcase hashState 13.810 ms/op 11.494 ms/op 1.20
altair processBlock - 250000 vs - 7PWei worstcase 19.987 ms/op 20.026 ms/op 1.00
altair processBlock - 250000 vs - 7PWei worstcase hashState 41.478 ms/op 43.768 ms/op 0.95
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3191 ms/op 1.2916 ms/op 1.02
phase0 processBlock - 250000 vs - 7PWei worstcase 16.701 ms/op 17.320 ms/op 0.96
altair processEth1Data - 250000 vs - 7PWei normalcase 271.56 us/op 294.50 us/op 0.92
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.1910 us/op 3.4640 us/op 0.92
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 20.171 us/op 20.881 us/op 0.97
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.3300 us/op 6.1150 us/op 0.87
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.3370 us/op 3.7350 us/op 0.89
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 88.871 us/op 92.792 us/op 0.96
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3135 ms/op 1.4351 ms/op 0.92
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.7237 ms/op 1.8783 ms/op 0.92
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.7196 ms/op 1.8735 ms/op 0.92
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.3672 ms/op 3.7199 ms/op 0.91
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.0053 ms/op 2.1064 ms/op 0.95
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.7581 ms/op 3.9765 ms/op 0.95
Tree 40 250000 create 293.93 ms/op 328.76 ms/op 0.89
Tree 40 250000 get(125000) 88.030 ns/op 94.799 ns/op 0.93
Tree 40 250000 set(125000) 977.02 ns/op 1.0318 us/op 0.95
Tree 40 250000 toArray() 12.742 ms/op 10.088 ms/op 1.26
Tree 40 250000 iterate all - toArray() + loop 11.613 ms/op 9.8982 ms/op 1.17
Tree 40 250000 iterate all - get(i) 35.364 ms/op 38.560 ms/op 0.92
Array 250000 create 2.2701 ms/op 2.1680 ms/op 1.05
Array 250000 clone - spread 710.52 us/op 682.40 us/op 1.04
Array 250000 get(125000) 0.28700 ns/op 0.29600 ns/op 0.97
Array 250000 set(125000) 0.29200 ns/op 0.30500 ns/op 0.96
Array 250000 iterate all - loop 55.851 us/op 58.182 us/op 0.96
phase0 afterProcessEpoch - 250000 vs - 7PWei 38.853 ms/op 62.602 ms/op 0.62
Array.fill - length 1000000 2.5883 ms/op 2.3278 ms/op 1.11
Array push - length 1000000 9.3586 ms/op 8.9185 ms/op 1.05
Array.get 0.20108 ns/op 0.20692 ns/op 0.97
Uint8Array.get 0.22668 ns/op 0.25511 ns/op 0.89
phase0 beforeProcessEpoch - 250000 vs - 7PWei 16.370 ms/op 13.992 ms/op 1.17
altair processEpoch - mainnet_e81889 265.11 ms/op 260.89 ms/op 1.02
mainnet_e81889 - altair beforeProcessEpoch 21.097 ms/op 13.478 ms/op 1.57
mainnet_e81889 - altair processJustificationAndFinalization 5.4450 us/op 5.5350 us/op 0.98
mainnet_e81889 - altair processInactivityUpdates 3.3069 ms/op 3.5020 ms/op 0.94
mainnet_e81889 - altair processRewardsAndPenalties 18.311 ms/op 19.541 ms/op 0.94
mainnet_e81889 - altair processRegistryUpdates 523.00 ns/op 521.00 ns/op 1.00
mainnet_e81889 - altair processSlashings 127.00 ns/op 131.00 ns/op 0.97
mainnet_e81889 - altair processEth1DataReset 125.00 ns/op 126.00 ns/op 0.99
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.3578 ms/op 1.7641 ms/op 0.77
mainnet_e81889 - altair processSlashingsReset 677.00 ns/op 703.00 ns/op 0.96
mainnet_e81889 - altair processRandaoMixesReset 1.1470 us/op 1.1610 us/op 0.99
mainnet_e81889 - altair processHistoricalRootsUpdate 127.00 ns/op 129.00 ns/op 0.98
mainnet_e81889 - altair processParticipationFlagUpdates 409.00 ns/op 444.00 ns/op 0.92
mainnet_e81889 - altair processSyncCommitteeUpdates 103.00 ns/op 105.00 ns/op 0.98
mainnet_e81889 - altair afterProcessEpoch 40.929 ms/op 40.484 ms/op 1.01
capella processEpoch - mainnet_e217614 826.36 ms/op 896.44 ms/op 0.92
mainnet_e217614 - capella beforeProcessEpoch 63.997 ms/op 57.859 ms/op 1.11
mainnet_e217614 - capella processJustificationAndFinalization 5.9710 us/op 5.7200 us/op 1.04
mainnet_e217614 - capella processInactivityUpdates 15.222 ms/op 14.391 ms/op 1.06
mainnet_e217614 - capella processRewardsAndPenalties 90.724 ms/op 100.77 ms/op 0.90
mainnet_e217614 - capella processRegistryUpdates 4.4550 us/op 4.5930 us/op 0.97
mainnet_e217614 - capella processSlashings 133.00 ns/op 134.00 ns/op 0.99
mainnet_e217614 - capella processEth1DataReset 126.00 ns/op 130.00 ns/op 0.97
mainnet_e217614 - capella processEffectiveBalanceUpdates 14.760 ms/op 15.987 ms/op 0.92
mainnet_e217614 - capella processSlashingsReset 670.00 ns/op 698.00 ns/op 0.96
mainnet_e217614 - capella processRandaoMixesReset 1.2480 us/op 1.2880 us/op 0.97
mainnet_e217614 - capella processHistoricalRootsUpdate 126.00 ns/op 133.00 ns/op 0.95
mainnet_e217614 - capella processParticipationFlagUpdates 422.00 ns/op 438.00 ns/op 0.96
mainnet_e217614 - capella afterProcessEpoch 107.02 ms/op 109.54 ms/op 0.98
phase0 processEpoch - mainnet_e58758 318.50 ms/op 285.91 ms/op 1.11
mainnet_e58758 - phase0 beforeProcessEpoch 67.577 ms/op 55.317 ms/op 1.22
mainnet_e58758 - phase0 processJustificationAndFinalization 6.0640 us/op 5.5030 us/op 1.10
mainnet_e58758 - phase0 processRewardsAndPenalties 15.947 ms/op 15.420 ms/op 1.03
mainnet_e58758 - phase0 processRegistryUpdates 2.2560 us/op 2.2880 us/op 0.99
mainnet_e58758 - phase0 processSlashings 276.00 ns/op 389.00 ns/op 0.71
mainnet_e58758 - phase0 processEth1DataReset 130.00 ns/op 132.00 ns/op 0.98
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 910.38 us/op 846.65 us/op 1.08
mainnet_e58758 - phase0 processSlashingsReset 846.00 ns/op 1.1100 us/op 0.76
mainnet_e58758 - phase0 processRandaoMixesReset 1.2510 us/op 1.5700 us/op 0.80
mainnet_e58758 - phase0 processHistoricalRootsUpdate 130.00 ns/op 134.00 ns/op 0.97
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.1590 us/op 1.3460 us/op 0.86
mainnet_e58758 - phase0 afterProcessEpoch 34.327 ms/op 35.047 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 normalcase 991.87 us/op 1.2484 ms/op 0.79
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.5937 ms/op 1.6070 ms/op 0.99
altair processInactivityUpdates - 250000 normalcase 12.417 ms/op 10.943 ms/op 1.13
altair processInactivityUpdates - 250000 worstcase 12.188 ms/op 10.775 ms/op 1.13
phase0 processRegistryUpdates - 250000 normalcase 2.1140 us/op 2.2420 us/op 0.94
phase0 processRegistryUpdates - 250000 badcase_full_deposits 138.52 us/op 154.18 us/op 0.90
phase0 processRegistryUpdates - 250000 worstcase 0.5 62.001 ms/op 63.253 ms/op 0.98
altair processRewardsAndPenalties - 250000 normalcase 15.676 ms/op 15.051 ms/op 1.04
altair processRewardsAndPenalties - 250000 worstcase 15.140 ms/op 13.986 ms/op 1.08
phase0 getAttestationDeltas - 250000 normalcase 5.4168 ms/op 5.4072 ms/op 1.00
phase0 getAttestationDeltas - 250000 worstcase 5.3268 ms/op 5.4884 ms/op 0.97
phase0 processSlashings - 250000 worstcase 58.634 us/op 60.186 us/op 0.97
altair processSyncCommitteeUpdates - 250000 10.536 ms/op 10.255 ms/op 1.03
BeaconState.hashTreeRoot - No change 161.00 ns/op 178.00 ns/op 0.90
BeaconState.hashTreeRoot - 1 full validator 78.047 us/op 74.601 us/op 1.05
BeaconState.hashTreeRoot - 32 full validator 929.24 us/op 820.90 us/op 1.13
BeaconState.hashTreeRoot - 512 full validator 8.9471 ms/op 6.7983 ms/op 1.32
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 101.88 us/op 76.535 us/op 1.33
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.5084 ms/op 1.3131 ms/op 1.15
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 20.942 ms/op 14.181 ms/op 1.48
BeaconState.hashTreeRoot - 1 balances 83.647 us/op 67.310 us/op 1.24
BeaconState.hashTreeRoot - 32 balances 797.44 us/op 672.14 us/op 1.19
BeaconState.hashTreeRoot - 512 balances 6.5754 ms/op 5.5378 ms/op 1.19
BeaconState.hashTreeRoot - 250000 balances 142.59 ms/op 130.64 ms/op 1.09
aggregationBits - 2048 els - zipIndexesInBitList 19.464 us/op 19.977 us/op 0.97
regular array get 100000 times 22.798 us/op 22.903 us/op 1.00
wrappedArray get 100000 times 22.781 us/op 22.970 us/op 0.99
arrayWithProxy get 100000 times 11.614 ms/op 10.271 ms/op 1.13
ssz.Root.equals 21.298 ns/op 21.575 ns/op 0.99
byteArrayEquals 21.047 ns/op 21.390 ns/op 0.98
Buffer.compare 8.6530 ns/op 8.8850 ns/op 0.97
processSlot - 1 slots 10.210 us/op 8.2300 us/op 1.24
processSlot - 32 slots 2.2235 ms/op 1.8109 ms/op 1.23
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 3.7002 ms/op 3.1413 ms/op 1.18
getCommitteeAssignments - req 1 vs - 250000 vc 1.6580 ms/op 1.6794 ms/op 0.99
getCommitteeAssignments - req 100 vs - 250000 vc 3.3746 ms/op 3.4233 ms/op 0.99
getCommitteeAssignments - req 1000 vs - 250000 vc 3.6485 ms/op 3.6877 ms/op 0.99
findModifiedValidators - 10000 modified validators 743.32 ms/op 765.77 ms/op 0.97
findModifiedValidators - 1000 modified validators 495.31 ms/op 486.94 ms/op 1.02
findModifiedValidators - 100 modified validators 315.66 ms/op 298.54 ms/op 1.06
findModifiedValidators - 10 modified validators 222.65 ms/op 175.95 ms/op 1.27
findModifiedValidators - 1 modified validators 165.47 ms/op 187.54 ms/op 0.88
findModifiedValidators - no difference 169.77 ms/op 191.01 ms/op 0.89
migrate state 1500000 validators, 3400 modified, 2000 new 2.7600 s/op 2.7784 s/op 0.99
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.6000 ns/op 3.7400 ns/op 0.96
state getBlockRootAtSlot - 250000 vs - 7PWei 331.14 ns/op 279.08 ns/op 1.19
computeProposerIndex 100000 validators 1.3055 ms/op 1.3263 ms/op 0.98
getNextSyncCommitteeIndices 1000 validators 2.8177 ms/op 2.8801 ms/op 0.98
getNextSyncCommitteeIndices 10000 validators 24.791 ms/op 25.414 ms/op 0.98
getNextSyncCommitteeIndices 100000 validators 85.885 ms/op 87.248 ms/op 0.98
computeProposers - vc 250000 618.62 us/op 545.46 us/op 1.13
computeEpochShuffling - vc 250000 38.312 ms/op 39.257 ms/op 0.98
getNextSyncCommittee - vc 250000 10.542 ms/op 9.4190 ms/op 1.12
nodejs block root to RootHex using toHex 87.434 ns/op 97.349 ns/op 0.90
nodejs block root to RootHex using toRootHex 53.480 ns/op 60.717 ns/op 0.88
nodejs fromHex(blob) 737.02 us/op 768.49 us/op 0.96
nodejs fromHexInto(blob) 618.58 us/op 636.33 us/op 0.97
nodejs block root to RootHex using the deprecated toHexString 457.36 ns/op 548.86 ns/op 0.83
nodejs byteArrayEquals 32 bytes (block root) 25.578 ns/op 25.977 ns/op 0.98
nodejs byteArrayEquals 48 bytes (pubkey) 37.027 ns/op 37.579 ns/op 0.99
nodejs byteArrayEquals 96 bytes (signature) 33.211 ns/op 36.741 ns/op 0.90
nodejs byteArrayEquals 1024 bytes 40.827 ns/op 44.702 ns/op 0.91
nodejs byteArrayEquals 131072 bytes (blob) 1.7101 us/op 1.7526 us/op 0.98
browser block root to RootHex using toHex 141.62 ns/op 145.03 ns/op 0.98
browser block root to RootHex using toRootHex 130.29 ns/op 130.92 ns/op 1.00
browser fromHex(blob) 1.4713 ms/op 1.5678 ms/op 0.94
browser fromHexInto(blob) 619.87 us/op 645.98 us/op 0.96
browser block root to RootHex using the deprecated toHexString 447.49 ns/op 373.25 ns/op 1.20
browser byteArrayEquals 32 bytes (block root) 26.963 ns/op 28.278 ns/op 0.95
browser byteArrayEquals 48 bytes (pubkey) 38.035 ns/op 39.769 ns/op 0.96
browser byteArrayEquals 96 bytes (signature) 71.242 ns/op 74.276 ns/op 0.96
browser byteArrayEquals 1024 bytes 725.40 ns/op 760.09 ns/op 0.95
browser byteArrayEquals 131072 bytes (blob) 91.240 us/op 95.781 us/op 0.95

by benchmarkbot/action

@ensi321
ensi321 marked this pull request as ready for review May 22, 2026 05:03
@ensi321
ensi321 requested a review from a team as a code owner May 22, 2026 05:03
@codecov

codecov Bot commented May 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.55%. Comparing base (6c85077) to head (32ede76).

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #9396   +/-   ##
=========================================
  Coverage     52.55%   52.55%           
=========================================
  Files           848      848           
  Lines         60947    60947           
  Branches       4486     4486           
=========================================
  Hits          32032    32032           
  Misses        28853    28853           
  Partials         62       62           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@twoeths twoeths left a comment

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.

according to ethereum/consensus-specs#5222 a validator can appear multiple times per slot, it did not mention epoch

for each slot, we shuffle committees of that slot

result[i] = computePayloadTimelinessCommitteeForSlot(slotSeed, committees[i], effectiveBalanceIncrements);

so if a validator is part of a ptc committee of a slot, it's also part of committee of that slot so it cannot be in committee of another slot of same epoch, hence it cannot be in ptc committees of any other slots of same epoch

@ensi321

ensi321 commented May 23, 2026

Copy link
Copy Markdown
Member Author

according to ethereum/consensus-specs#5222 a validator can appear multiple times per slot, it did not mention epoch

for each slot, we shuffle committees of that slot

result[i] = computePayloadTimelinessCommitteeForSlot(slotSeed, committees[i], effectiveBalanceIncrements);

so if a validator is part of a ptc committee of a slot, it's also part of committee of that slot so it cannot be in committee of another slot of same epoch, hence it cannot be in ptc committees of any other slots of same epoch

Yea you are right. PTC committee is a subset of beacon committee. PTC selection allows duplicates in a slot, but a validator cannot be an attester for more than one slot per epoch. So validator can be in PTC multiple times per slot, but not per epoch but different slot.

Closing this PR as the assessment is wrong.

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.

2 participants