Skip to content

fix: checkpoint sync for skipped slot#9329

Merged
nflaig merged 10 commits intounstablefrom
te/skipped_cp_state_sync_e2e
May 7, 2026
Merged

fix: checkpoint sync for skipped slot#9329
nflaig merged 10 commits intounstablefrom
te/skipped_cp_state_sync_e2e

Conversation

@twoeths
Copy link
Copy Markdown
Contributor

@twoeths twoeths commented May 6, 2026

Motivation

Lodestar is not able to do checkpoint sync from skipped slot checkpoint because it does not download the parent payload envelope to feed forkchoice

Description

  • The first batch should download the parent payload envelope + columns, enhance Batch for that
    • Batch.getRequest() decides which requests to download
    • update Batch.downloadSuccess() handler: need to make sure parent envelope is fully downloaded
  • remove downloadByRange heuristic, it needs to download whatever specified in Batch.getRequest()
  • allow to create PayloadEnvelopeInput from the anchor state's bid
  • use anchor state to validate the 1st envelope
  • the regular checkpoint sync is unchanged (do not download parent envelope)
  • confirmed via checkpointSync.test.ts e2e

Closes #9319

AI Assistance Disclosure

Create with the help of Claude

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 introduces support for skipped-slot checkpoint synchronization, ensuring that the first batch of a sync chain can correctly fetch the parent payload envelope when necessary. Key changes include updates to the Batch and SyncChain logic, the addition of a createFromBid method for cache seeding, and improved validation for range sync responses. The reviewer suggested a minor cleanup in packages/beacon-node/src/chain/blocks/index.ts to remove a redundant variable, which is a valid improvement.

Comment thread packages/beacon-node/src/chain/blocks/index.ts Outdated
@twoeths twoeths force-pushed the te/skipped_cp_state_sync_e2e branch from 28465ae to b1ed8b8 Compare May 6, 2026 10:41
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 910d2ab Previous: ff409c4 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 885.76 us/op 879.93 us/op 1.01
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 39.272 us/op 39.179 us/op 1.00
BLS verify - blst 685.28 us/op 748.65 us/op 0.92
BLS verifyMultipleSignatures 3 - blst 1.3175 ms/op 1.3409 ms/op 0.98
BLS verifyMultipleSignatures 8 - blst 2.0949 ms/op 2.1636 ms/op 0.97
BLS verifyMultipleSignatures 32 - blst 6.9457 ms/op 6.8401 ms/op 1.02
BLS verifyMultipleSignatures 64 - blst 12.927 ms/op 13.066 ms/op 0.99
BLS verifyMultipleSignatures 128 - blst 25.551 ms/op 25.190 ms/op 1.01
BLS deserializing 10000 signatures 632.75 ms/op 627.22 ms/op 1.01
BLS deserializing 100000 signatures 6.2236 s/op 6.2658 s/op 0.99
BLS verifyMultipleSignatures - same message - 3 - blst 755.67 us/op 796.68 us/op 0.95
BLS verifyMultipleSignatures - same message - 8 - blst 879.95 us/op 888.31 us/op 0.99
BLS verifyMultipleSignatures - same message - 32 - blst 1.4375 ms/op 1.4996 ms/op 0.96
BLS verifyMultipleSignatures - same message - 64 - blst 2.2275 ms/op 2.3682 ms/op 0.94
BLS verifyMultipleSignatures - same message - 128 - blst 3.8473 ms/op 4.0160 ms/op 0.96
BLS aggregatePubkeys 32 - blst 16.973 us/op 17.415 us/op 0.97
BLS aggregatePubkeys 128 - blst 59.937 us/op 62.153 us/op 0.96
getSlashingsAndExits - default max 46.492 us/op 48.835 us/op 0.95
getSlashingsAndExits - 2k 342.27 us/op 342.04 us/op 1.00
proposeBlockBody type=full, size=empty 700.86 us/op 854.32 us/op 0.82
isKnown best case - 1 super set check 158.00 ns/op 165.00 ns/op 0.96
isKnown normal case - 2 super set checks 169.00 ns/op 165.00 ns/op 1.02
isKnown worse case - 16 super set checks 156.00 ns/op 167.00 ns/op 0.93
validate api signedAggregateAndProof - struct 1.4058 ms/op 1.5110 ms/op 0.93
validate gossip signedAggregateAndProof - struct 1.4021 ms/op 1.4999 ms/op 0.93
batch validate gossip attestation - vc 640000 - chunk 32 102.16 us/op 105.93 us/op 0.96
batch validate gossip attestation - vc 640000 - chunk 64 90.052 us/op 92.814 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 128 83.751 us/op 88.720 us/op 0.94
batch validate gossip attestation - vc 640000 - chunk 256 79.754 us/op 83.085 us/op 0.96
bytes32 toHexString 275.00 ns/op 293.00 ns/op 0.94
bytes32 Buffer.toString(hex) 175.00 ns/op 174.00 ns/op 1.01
bytes32 Buffer.toString(hex) from Uint8Array 235.00 ns/op 259.00 ns/op 0.91
bytes32 Buffer.toString(hex) + 0x 176.00 ns/op 173.00 ns/op 1.02
Return object 10000 times 0.20370 ns/op 0.21070 ns/op 0.97
Throw Error 10000 times 3.1454 us/op 3.2783 us/op 0.96
toHex 93.611 ns/op 103.45 ns/op 0.90
Buffer.from 78.860 ns/op 88.032 ns/op 0.90
shared Buffer 52.630 ns/op 61.790 ns/op 0.85
fastMsgIdFn sha256 / 200 bytes 1.4040 us/op 1.4700 us/op 0.96
fastMsgIdFn h32 xxhash / 200 bytes 149.00 ns/op 152.00 ns/op 0.98
fastMsgIdFn h64 xxhash / 200 bytes 195.00 ns/op 198.00 ns/op 0.98
fastMsgIdFn sha256 / 1000 bytes 4.5430 us/op 4.7550 us/op 0.96
fastMsgIdFn h32 xxhash / 1000 bytes 233.00 ns/op 243.00 ns/op 0.96
fastMsgIdFn h64 xxhash / 1000 bytes 244.00 ns/op 253.00 ns/op 0.96
fastMsgIdFn sha256 / 10000 bytes 40.084 us/op 41.651 us/op 0.96
fastMsgIdFn h32 xxhash / 10000 bytes 1.2300 us/op 1.2520 us/op 0.98
fastMsgIdFn h64 xxhash / 10000 bytes 792.00 ns/op 811.00 ns/op 0.98
send data - 1000 256B messages 4.1509 ms/op 4.2228 ms/op 0.98
send data - 1000 512B messages 4.2444 ms/op 4.3586 ms/op 0.97
send data - 1000 1024B messages 4.5689 ms/op 4.5796 ms/op 1.00
send data - 1000 1200B messages 4.8011 ms/op 4.6925 ms/op 1.02
send data - 1000 2048B messages 4.8666 ms/op 4.8669 ms/op 1.00
send data - 1000 4096B messages 5.5778 ms/op 5.9774 ms/op 0.93
send data - 1000 16384B messages 14.829 ms/op 19.849 ms/op 0.75
send data - 1000 65536B messages 121.54 ms/op 203.23 ms/op 0.60
enrSubnets - fastDeserialize 64 bits 709.00 ns/op 740.00 ns/op 0.96
enrSubnets - ssz BitVector 64 bits 263.00 ns/op 263.00 ns/op 1.00
enrSubnets - fastDeserialize 4 bits 104.00 ns/op 105.00 ns/op 0.99
enrSubnets - ssz BitVector 4 bits 265.00 ns/op 259.00 ns/op 1.02
prioritizePeers score -10:0 att 32-0.1 sync 2-0 195.59 us/op 203.95 us/op 0.96
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 223.33 us/op 233.69 us/op 0.96
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 329.75 us/op 341.86 us/op 0.96
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 582.17 us/op 602.61 us/op 0.97
prioritizePeers score 0:0 att 64-1 sync 4-1 701.85 us/op 697.61 us/op 1.01
array of 16000 items push then shift 1.2408 us/op 1.2840 us/op 0.97
LinkedList of 16000 items push then shift 8.1660 ns/op 6.9210 ns/op 1.18
array of 16000 items push then pop 70.507 ns/op 69.845 ns/op 1.01
LinkedList of 16000 items push then pop 6.2630 ns/op 6.0200 ns/op 1.04
array of 24000 items push then shift 1.8218 us/op 1.9132 us/op 0.95
LinkedList of 24000 items push then shift 7.8980 ns/op 6.6610 ns/op 1.19
array of 24000 items push then pop 98.423 ns/op 95.202 ns/op 1.03
LinkedList of 24000 items push then pop 6.1230 ns/op 5.9050 ns/op 1.04
intersect bitArray bitLen 8 4.6480 ns/op 4.7100 ns/op 0.99
intersect array and set length 8 29.033 ns/op 29.428 ns/op 0.99
intersect bitArray bitLen 128 23.429 ns/op 24.131 ns/op 0.97
intersect array and set length 128 488.11 ns/op 495.48 ns/op 0.99
bitArray.getTrueBitIndexes() bitLen 128 967.00 ns/op 1.0520 us/op 0.92
bitArray.getTrueBitIndexes() bitLen 248 1.7190 us/op 1.7580 us/op 0.98
bitArray.getTrueBitIndexes() bitLen 512 3.5660 us/op 3.6060 us/op 0.99
Full columns - reconstruct all 6 blobs 149.49 us/op 168.49 us/op 0.89
Full columns - reconstruct half of the blobs out of 6 76.474 us/op 87.007 us/op 0.88
Full columns - reconstruct single blob out of 6 34.904 us/op 35.220 us/op 0.99
Half columns - reconstruct all 6 blobs 393.29 ms/op 395.29 ms/op 0.99
Half columns - reconstruct half of the blobs out of 6 189.90 ms/op 195.94 ms/op 0.97
Half columns - reconstruct single blob out of 6 67.612 ms/op 69.979 ms/op 0.97
Full columns - reconstruct all 10 blobs 418.96 us/op 263.76 us/op 1.59
Full columns - reconstruct half of the blobs out of 10 127.54 us/op 98.136 us/op 1.30
Full columns - reconstruct single blob out of 10 46.190 us/op 35.358 us/op 1.31
Half columns - reconstruct all 10 blobs 625.31 ms/op 654.22 ms/op 0.96
Half columns - reconstruct half of the blobs out of 10 315.25 ms/op 328.40 ms/op 0.96
Half columns - reconstruct single blob out of 10 68.151 ms/op 69.619 ms/op 0.98
Full columns - reconstruct all 20 blobs 2.4705 ms/op 1.9107 ms/op 1.29
Full columns - reconstruct half of the blobs out of 20 211.53 us/op 204.26 us/op 1.04
Full columns - reconstruct single blob out of 20 30.356 us/op 31.563 us/op 0.96
Half columns - reconstruct all 20 blobs 1.2592 s/op 1.3007 s/op 0.97
Half columns - reconstruct half of the blobs out of 20 627.06 ms/op 652.08 ms/op 0.96
Half columns - reconstruct single blob out of 20 67.792 ms/op 70.044 ms/op 0.97
Set add up to 64 items then delete first 2.0397 us/op 2.5413 us/op 0.80
OrderedSet add up to 64 items then delete first 3.2443 us/op 3.3083 us/op 0.98
Set add up to 64 items then delete last 2.0513 us/op 2.3333 us/op 0.88
OrderedSet add up to 64 items then delete last 3.2933 us/op 3.2473 us/op 1.01
Set add up to 64 items then delete middle 2.0441 us/op 2.1247 us/op 0.96
OrderedSet add up to 64 items then delete middle 4.7226 us/op 4.7063 us/op 1.00
Set add up to 128 items then delete first 3.9442 us/op 4.2107 us/op 0.94
OrderedSet add up to 128 items then delete first 5.8368 us/op 6.5215 us/op 0.90
Set add up to 128 items then delete last 3.7002 us/op 3.8681 us/op 0.96
OrderedSet add up to 128 items then delete last 5.7633 us/op 5.7642 us/op 1.00
Set add up to 128 items then delete middle 3.6791 us/op 3.8537 us/op 0.95
OrderedSet add up to 128 items then delete middle 11.590 us/op 11.581 us/op 1.00
Set add up to 256 items then delete first 7.2566 us/op 7.8542 us/op 0.92
OrderedSet add up to 256 items then delete first 11.095 us/op 12.136 us/op 0.91
Set add up to 256 items then delete last 7.3653 us/op 7.6676 us/op 0.96
OrderedSet add up to 256 items then delete last 11.391 us/op 11.412 us/op 1.00
Set add up to 256 items then delete middle 7.3117 us/op 7.6571 us/op 0.95
OrderedSet add up to 256 items then delete middle 34.759 us/op 34.667 us/op 1.00
pass gossip attestations to forkchoice per slot 2.5283 ms/op 2.7184 ms/op 0.93
forkChoice updateHead vc 100000 bc 64 eq 0 376.96 us/op 437.96 us/op 0.86
forkChoice updateHead vc 600000 bc 64 eq 0 2.2669 ms/op 2.6202 ms/op 0.87
forkChoice updateHead vc 1000000 bc 64 eq 0 4.2595 ms/op 4.3195 ms/op 0.99
forkChoice updateHead vc 600000 bc 320 eq 0 2.6723 ms/op 2.7547 ms/op 0.97
forkChoice updateHead vc 600000 bc 1200 eq 0 2.4061 ms/op 2.6824 ms/op 0.90
forkChoice updateHead vc 600000 bc 7200 eq 0 2.9532 ms/op 3.0246 ms/op 0.98
forkChoice updateHead vc 600000 bc 64 eq 1000 2.8156 ms/op 3.1013 ms/op 0.91
forkChoice updateHead vc 600000 bc 64 eq 10000 2.9378 ms/op 3.1987 ms/op 0.92
forkChoice updateHead vc 600000 bc 64 eq 300000 6.7961 ms/op 7.1059 ms/op 0.96
computeDeltas 1400000 validators 0% inactive 12.230 ms/op 13.068 ms/op 0.94
computeDeltas 1400000 validators 10% inactive 11.397 ms/op 12.333 ms/op 0.92
computeDeltas 1400000 validators 20% inactive 10.447 ms/op 11.245 ms/op 0.93
computeDeltas 1400000 validators 50% inactive 8.0253 ms/op 8.3493 ms/op 0.96
computeDeltas 2100000 validators 0% inactive 18.246 ms/op 19.651 ms/op 0.93
computeDeltas 2100000 validators 10% inactive 17.198 ms/op 18.474 ms/op 0.93
computeDeltas 2100000 validators 20% inactive 15.715 ms/op 17.160 ms/op 0.92
computeDeltas 2100000 validators 50% inactive 9.1309 ms/op 9.8874 ms/op 0.92
altair processAttestation - 250000 vs - 7PWei normalcase 1.9183 ms/op 1.8298 ms/op 1.05
altair processAttestation - 250000 vs - 7PWei worstcase 2.7864 ms/op 2.4205 ms/op 1.15
altair processAttestation - setStatus - 1/6 committees join 98.750 us/op 109.18 us/op 0.90
altair processAttestation - setStatus - 1/3 committees join 191.69 us/op 201.14 us/op 0.95
altair processAttestation - setStatus - 1/2 committees join 274.45 us/op 292.72 us/op 0.94
altair processAttestation - setStatus - 2/3 committees join 358.54 us/op 364.47 us/op 0.98
altair processAttestation - setStatus - 4/5 committees join 488.79 us/op 516.23 us/op 0.95
altair processAttestation - setStatus - 100% committees join 571.98 us/op 638.82 us/op 0.90
altair processBlock - 250000 vs - 7PWei normalcase 3.9956 ms/op 4.4731 ms/op 0.89
altair processBlock - 250000 vs - 7PWei normalcase hashState 16.305 ms/op 17.155 ms/op 0.95
altair processBlock - 250000 vs - 7PWei worstcase 20.605 ms/op 22.890 ms/op 0.90
altair processBlock - 250000 vs - 7PWei worstcase hashState 41.445 ms/op 46.193 ms/op 0.90
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3779 ms/op 1.3673 ms/op 1.01
phase0 processBlock - 250000 vs - 7PWei worstcase 16.223 ms/op 17.454 ms/op 0.93
altair processEth1Data - 250000 vs - 7PWei normalcase 286.12 us/op 292.32 us/op 0.98
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.7790 us/op 4.0300 us/op 0.94
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 21.996 us/op 19.865 us/op 1.11
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.4740 us/op 5.9410 us/op 0.92
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.6190 us/op 3.7280 us/op 0.97
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 94.705 us/op 93.817 us/op 1.01
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3612 ms/op 1.4030 ms/op 0.97
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.7028 ms/op 1.8046 ms/op 0.94
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.7583 ms/op 1.8142 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.6952 ms/op 3.9135 ms/op 0.94
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 1.9830 ms/op 2.0406 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.0050 ms/op 4.3060 ms/op 0.93
Tree 40 250000 create 310.03 ms/op 344.43 ms/op 0.90
Tree 40 250000 get(125000) 86.513 ns/op 98.940 ns/op 0.87
Tree 40 250000 set(125000) 954.68 ns/op 1.0179 us/op 0.94
Tree 40 250000 toArray() 12.233 ms/op 12.091 ms/op 1.01
Tree 40 250000 iterate all - toArray() + loop 13.412 ms/op 10.794 ms/op 1.24
Tree 40 250000 iterate all - get(i) 36.915 ms/op 38.744 ms/op 0.95
Array 250000 create 2.1293 ms/op 2.1738 ms/op 0.98
Array 250000 clone - spread 670.57 us/op 669.47 us/op 1.00
Array 250000 get(125000) 0.28500 ns/op 0.28600 ns/op 1.00
Array 250000 set(125000) 0.29300 ns/op 0.29100 ns/op 1.01
Array 250000 iterate all - loop 56.458 us/op 56.808 us/op 0.99
phase0 afterProcessEpoch - 250000 vs - 7PWei 48.146 ms/op 40.383 ms/op 1.19
Array.fill - length 1000000 2.0307 ms/op 2.3415 ms/op 0.87
Array push - length 1000000 8.7315 ms/op 8.2690 ms/op 1.06
Array.get 0.20186 ns/op 0.20354 ns/op 0.99
Uint8Array.get 0.22803 ns/op 0.23836 ns/op 0.96
phase0 beforeProcessEpoch - 250000 vs - 7PWei 14.301 ms/op 14.397 ms/op 0.99
altair processEpoch - mainnet_e81889 272.85 ms/op 269.97 ms/op 1.01
mainnet_e81889 - altair beforeProcessEpoch 16.018 ms/op 16.180 ms/op 0.99
mainnet_e81889 - altair processJustificationAndFinalization 5.8280 us/op 4.8550 us/op 1.20
mainnet_e81889 - altair processInactivityUpdates 3.9256 ms/op 4.3200 ms/op 0.91
mainnet_e81889 - altair processRewardsAndPenalties 19.344 ms/op 17.375 ms/op 1.11
mainnet_e81889 - altair processRegistryUpdates 526.00 ns/op 522.00 ns/op 1.01
mainnet_e81889 - altair processSlashings 140.00 ns/op 142.00 ns/op 0.99
mainnet_e81889 - altair processEth1DataReset 136.00 ns/op 134.00 ns/op 1.01
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.1375 ms/op 1.1894 ms/op 0.96
mainnet_e81889 - altair processSlashingsReset 674.00 ns/op 695.00 ns/op 0.97
mainnet_e81889 - altair processRandaoMixesReset 1.2790 us/op 1.0420 us/op 1.23
mainnet_e81889 - altair processHistoricalRootsUpdate 142.00 ns/op 136.00 ns/op 1.04
mainnet_e81889 - altair processParticipationFlagUpdates 431.00 ns/op 420.00 ns/op 1.03
mainnet_e81889 - altair processSyncCommitteeUpdates 120.00 ns/op 115.00 ns/op 1.04
mainnet_e81889 - altair afterProcessEpoch 40.863 ms/op 41.251 ms/op 0.99
capella processEpoch - mainnet_e217614 823.21 ms/op 739.19 ms/op 1.11
mainnet_e217614 - capella beforeProcessEpoch 60.907 ms/op 63.224 ms/op 0.96
mainnet_e217614 - capella processJustificationAndFinalization 5.9630 us/op 5.0580 us/op 1.18
mainnet_e217614 - capella processInactivityUpdates 16.552 ms/op 13.291 ms/op 1.25
mainnet_e217614 - capella processRewardsAndPenalties 88.694 ms/op 89.450 ms/op 0.99
mainnet_e217614 - capella processRegistryUpdates 4.2740 us/op 4.3770 us/op 0.98
mainnet_e217614 - capella processSlashings 141.00 ns/op 149.00 ns/op 0.95
mainnet_e217614 - capella processEth1DataReset 135.00 ns/op 127.00 ns/op 1.06
mainnet_e217614 - capella processEffectiveBalanceUpdates 13.337 ms/op 5.3021 ms/op 2.52
mainnet_e217614 - capella processSlashingsReset 674.00 ns/op 656.00 ns/op 1.03
mainnet_e217614 - capella processRandaoMixesReset 1.2340 us/op 1.0390 us/op 1.19
mainnet_e217614 - capella processHistoricalRootsUpdate 136.00 ns/op 132.00 ns/op 1.03
mainnet_e217614 - capella processParticipationFlagUpdates 423.00 ns/op 403.00 ns/op 1.05
mainnet_e217614 - capella afterProcessEpoch 104.35 ms/op 107.45 ms/op 0.97
phase0 processEpoch - mainnet_e58758 309.43 ms/op 262.74 ms/op 1.18
mainnet_e58758 - phase0 beforeProcessEpoch 65.339 ms/op 54.276 ms/op 1.20
mainnet_e58758 - phase0 processJustificationAndFinalization 5.9580 us/op 4.7990 us/op 1.24
mainnet_e58758 - phase0 processRewardsAndPenalties 16.758 ms/op 15.613 ms/op 1.07
mainnet_e58758 - phase0 processRegistryUpdates 2.2220 us/op 2.2030 us/op 1.01
mainnet_e58758 - phase0 processSlashings 143.00 ns/op 135.00 ns/op 1.06
mainnet_e58758 - phase0 processEth1DataReset 262.00 ns/op 125.00 ns/op 2.10
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 782.14 us/op 954.60 us/op 0.82
mainnet_e58758 - phase0 processSlashingsReset 870.00 ns/op 920.00 ns/op 0.95
mainnet_e58758 - phase0 processRandaoMixesReset 1.1310 us/op 1.2850 us/op 0.88
mainnet_e58758 - phase0 processHistoricalRootsUpdate 145.00 ns/op 131.00 ns/op 1.11
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.1660 us/op 1.1480 us/op 1.02
mainnet_e58758 - phase0 afterProcessEpoch 33.283 ms/op 33.263 ms/op 1.00
phase0 processEffectiveBalanceUpdates - 250000 normalcase 962.09 us/op 1.0147 ms/op 0.95
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.1660 ms/op 1.5695 ms/op 0.74
altair processInactivityUpdates - 250000 normalcase 12.731 ms/op 15.068 ms/op 0.84
altair processInactivityUpdates - 250000 worstcase 11.706 ms/op 12.271 ms/op 0.95
phase0 processRegistryUpdates - 250000 normalcase 2.3630 us/op 2.5270 us/op 0.94
phase0 processRegistryUpdates - 250000 badcase_full_deposits 148.42 us/op 147.75 us/op 1.00
phase0 processRegistryUpdates - 250000 worstcase 0.5 63.923 ms/op 60.572 ms/op 1.06
altair processRewardsAndPenalties - 250000 normalcase 17.798 ms/op 14.928 ms/op 1.19
altair processRewardsAndPenalties - 250000 worstcase 17.831 ms/op 13.661 ms/op 1.31
phase0 getAttestationDeltas - 250000 normalcase 5.4504 ms/op 5.3503 ms/op 1.02
phase0 getAttestationDeltas - 250000 worstcase 5.4095 ms/op 5.3456 ms/op 1.01
phase0 processSlashings - 250000 worstcase 60.159 us/op 59.755 us/op 1.01
altair processSyncCommitteeUpdates - 250000 10.299 ms/op 9.8633 ms/op 1.04
BeaconState.hashTreeRoot - No change 201.00 ns/op 186.00 ns/op 1.08
BeaconState.hashTreeRoot - 1 full validator 67.946 us/op 62.652 us/op 1.08
BeaconState.hashTreeRoot - 32 full validator 834.45 us/op 666.13 us/op 1.25
BeaconState.hashTreeRoot - 512 full validator 6.7049 ms/op 6.6864 ms/op 1.00
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 87.907 us/op 77.694 us/op 1.13
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.3114 ms/op 1.1422 ms/op 1.15
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 14.623 ms/op 13.383 ms/op 1.09
BeaconState.hashTreeRoot - 1 balances 78.235 us/op 60.064 us/op 1.30
BeaconState.hashTreeRoot - 32 balances 732.69 us/op 593.28 us/op 1.23
BeaconState.hashTreeRoot - 512 balances 5.2405 ms/op 4.7947 ms/op 1.09
BeaconState.hashTreeRoot - 250000 balances 133.50 ms/op 109.49 ms/op 1.22
aggregationBits - 2048 els - zipIndexesInBitList 19.421 us/op 19.107 us/op 1.02
regular array get 100000 times 22.591 us/op 22.410 us/op 1.01
wrappedArray get 100000 times 22.532 us/op 22.406 us/op 1.01
arrayWithProxy get 100000 times 9.1698 ms/op 13.179 ms/op 0.70
ssz.Root.equals 20.856 ns/op 21.139 ns/op 0.99
byteArrayEquals 20.720 ns/op 20.962 ns/op 0.99
Buffer.compare 8.5900 ns/op 8.8100 ns/op 0.98
processSlot - 1 slots 8.4500 us/op 8.2450 us/op 1.02
processSlot - 32 slots 1.9609 ms/op 1.5962 ms/op 1.23
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 4.4291 ms/op 3.7375 ms/op 1.19
getCommitteeAssignments - req 1 vs - 250000 vc 1.6227 ms/op 1.6261 ms/op 1.00
getCommitteeAssignments - req 100 vs - 250000 vc 3.3101 ms/op 3.3659 ms/op 0.98
getCommitteeAssignments - req 1000 vs - 250000 vc 3.5714 ms/op 3.6199 ms/op 0.99
findModifiedValidators - 10000 modified validators 622.43 ms/op 776.99 ms/op 0.80
findModifiedValidators - 1000 modified validators 437.51 ms/op 552.67 ms/op 0.79
findModifiedValidators - 100 modified validators 346.58 ms/op 317.73 ms/op 1.09
findModifiedValidators - 10 modified validators 253.47 ms/op 155.68 ms/op 1.63
findModifiedValidators - 1 modified validators 169.01 ms/op 185.21 ms/op 0.91
findModifiedValidators - no difference 171.66 ms/op 156.93 ms/op 1.09
migrate state 1500000 validators, 3400 modified, 2000 new 2.7950 s/op 3.0253 s/op 0.92
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.6900 ns/op 3.6800 ns/op 1.00
state getBlockRootAtSlot - 250000 vs - 7PWei 343.83 ns/op 392.09 ns/op 0.88
computeProposerIndex 100000 validators 1.2786 ms/op 1.3495 ms/op 0.95
getNextSyncCommitteeIndices 1000 validators 2.7128 ms/op 2.8908 ms/op 0.94
getNextSyncCommitteeIndices 10000 validators 24.161 ms/op 25.671 ms/op 0.94
getNextSyncCommitteeIndices 100000 validators 82.706 ms/op 88.174 ms/op 0.94
computeProposers - vc 250000 521.37 us/op 626.18 us/op 0.83
computeEpochShuffling - vc 250000 38.653 ms/op 39.796 ms/op 0.97
getNextSyncCommittee - vc 250000 9.1407 ms/op 11.702 ms/op 0.78
nodejs block root to RootHex using toHex 88.973 ns/op 98.178 ns/op 0.91
nodejs block root to RootHex using toRootHex 53.430 ns/op 61.015 ns/op 0.88
nodejs fromHex(blob) 720.50 us/op 965.61 us/op 0.75
nodejs fromHexInto(blob) 613.69 us/op 633.81 us/op 0.97
nodejs block root to RootHex using the deprecated toHexString 445.59 ns/op 361.23 ns/op 1.23
nodejs byteArrayEquals 32 bytes (block root) 24.986 ns/op 25.766 ns/op 0.97
nodejs byteArrayEquals 48 bytes (pubkey) 36.122 ns/op 37.190 ns/op 0.97
nodejs byteArrayEquals 96 bytes (signature) 32.716 ns/op 37.980 ns/op 0.86
nodejs byteArrayEquals 1024 bytes 39.849 ns/op 44.645 ns/op 0.89
nodejs byteArrayEquals 131072 bytes (blob) 1.7695 us/op 1.7345 us/op 1.02
browser block root to RootHex using toHex 138.29 ns/op 144.53 ns/op 0.96
browser block root to RootHex using toRootHex 124.97 ns/op 130.45 ns/op 0.96
browser fromHex(blob) 1.3964 ms/op 1.5882 ms/op 0.88
browser fromHexInto(blob) 601.64 us/op 643.92 us/op 0.93
browser block root to RootHex using the deprecated toHexString 445.26 ns/op 488.50 ns/op 0.91
browser byteArrayEquals 32 bytes (block root) 26.785 ns/op 28.156 ns/op 0.95
browser byteArrayEquals 48 bytes (pubkey) 37.760 ns/op 39.654 ns/op 0.95
browser byteArrayEquals 96 bytes (signature) 70.966 ns/op 74.288 ns/op 0.96
browser byteArrayEquals 1024 bytes 719.67 ns/op 752.92 ns/op 0.96
browser byteArrayEquals 131072 bytes (blob) 91.131 us/op 96.433 us/op 0.95

by benchmarkbot/action

@twoeths twoeths marked this pull request as ready for review May 6, 2026 12:18
@twoeths twoeths requested a review from a team as a code owner May 6, 2026 12:18
@twoeths
Copy link
Copy Markdown
Contributor Author

twoeths commented May 6, 2026

@lodekeeper please review

Copy link
Copy Markdown
Contributor

@lodekeeper lodekeeper left a comment

Choose a reason for hiding this comment

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

Main logic looks sound overall. I left one sync-robustness note on the new parent-by-root column path. I also agree with the Gemini cleanup on the redundant blockSlots temporary in processBlocks.


private downloadByRange: SyncChainFns["downloadByRange"] = async (peer, batch) => {
const batchBlocks = batch.getBlocks();
const requests = batch.getRequestsForPeer(peer);
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.

🟡 Parent by-root column requests still bypass peer custody filtering

getRequestsForPeer() only filters columnsRequest, but this new skipped-checkpoint path can also carry sampled columns in parentPayloadRequest.columns. That means we can still select a peer that did not advertise some of those columns, then immediately ask it for them by-root in the first batch. In the best case that just churns retries; in the worst case it can stall the very path this PR is trying to unblock.

Could we run parentPayloadRequest.columns through the same peer.custodyColumns filter before spreading requests into downloadByRange()?

@lodekeeper
Copy link
Copy Markdown
Contributor

Reviewed — left one note on the new parent-by-root column request path, and I agree with the Gemini cleanup on the redundant blockSlots temporary.

Comment on lines -126 to -130
const blockState = await this.regen.getBlockSlotState(
protoBlock,
protoBlock.slot,
{dontTransferCache: true},
RegenCaller.processBlock
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.

can do

 const blockState = await this.regen.getBlockSlotState(
    protoBlock,
    protoBlock.slot,
    {dontTransferCache: true},
    RegenCaller.processBlock
).catch(() =>
  // only happen at the 1st batch of skipped slot checkpoint sync
  this.regen.getClosestHeadState(protoBlock)
);

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.

Behavior-wise this is equivalent to the current wrapError(...) branch. I slightly prefer the expanded form here because this is a pretty unusual path: we are intentionally swallowing a regen failure and falling back to getClosestHeadState() only for the skipped-checkpoint case, so keeping the err branch explicit makes that special-case easier to spot (and easier to tighten later if we ever want to narrow which regen failures are allowed to fall through). So I think the current form is fine as-is.

Comment thread packages/beacon-node/src/chain/chain.ts Outdated
Comment thread packages/beacon-node/src/chain/chain.ts Outdated
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.

LGTM

@nflaig nflaig enabled auto-merge (squash) May 7, 2026 11:47
@nflaig nflaig merged commit 05a33e5 into unstable May 7, 2026
19 checks passed
@nflaig nflaig deleted the te/skipped_cp_state_sync_e2e branch May 7, 2026 12:02
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.

BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN in Range Sync

4 participants