Skip to content

Conversation

nflaig
Copy link
Member

@nflaig nflaig commented Oct 13, 2025

No description provided.

twoeths and others added 22 commits September 30, 2025 15:43
**Motivation**

- we already have local checkpoint states in either db or file, this PR
use one of them to save syncing time the next time the node is restarted
- see
#7504 (comment)

**Description**

2 new init unfinalized state options:

- new `--lastPersistedCheckpointState`, true by default to load from the
last safe persisted checkpoint state. And the last safe persisted
checkpoint state is considered unfinalized
- use `--checkpointState` option: this is old option and support
finalized state only, this PR supports booting from unfinalized state
similar to `--lastPersistedCheckpointState` ==> to quickly sync a new
node, we can use this option. Then remove it and the next time node will
use `--lastPersistedCheckpointState` option by default
- we can configure one of our nodes with
`chain.nHistoricalStatesFileDataStore = true`
- then feed other nodes with a persisted "safe checkpoint state" from
there in `~/checkpoint_states` folder

- a persisted checkpoint state is consider to be safe to boot if
  - it should be the checkpoint state that's unique in its epoch
- its last processed block slot should be at epoch boundary or last slot
of previous epoch
   - state slot should be at epoch boundary
   - state slot should be equal to epoch * SLOTS_PER_EPOCH

other options to boot from `stateArchived` or `checkpointSyncUrl` are
considered finalized states including `wssCheckpoint`. It's not possible
to use `wssCheckpoint` option with unfinalized state for now.

**TODO**
- this is for `holesky-rescue`, consider supporting this for `unstable`
branch too
- update document in that case

---------

Co-authored-by: Tuyen Nguyen <[email protected]>
**Motivation**

- implement an api to get a node synced asap

**Description**

- new api: `eth/v1/lodestar/persisted_checkpoint_state` to return a
state based on an optional `rootHex:epoch` param
- if not specified, return the latest safe checkpoint state
- a node need to specify `--checkpointState` from the previous PR #7509

**Test**
- [x] `curl -H "Accept: application/octet-stream"
http://localhost:9596/eth/v1/lodestar/persisted_checkpoint_state -o
latest_checkpoint_state.ssz`
- [x] `curl -H "Accept: application/octet-stream"
http://localhost:9596/eth/v1/lodestar/persisted_checkpoint_state?checkpoint_id=0x4f4d4c1b81141fe77a4a1c6a376dbe64ed9baa8f123664195e4f710c9fc4238d:118936
-o state_epoch_118936.ssz`

---------

Co-authored-by: Tuyen Nguyen <[email protected]>
Co-authored-by: Nico Flaig <[email protected]>
…ied (#7542)

We have added a new api in
#7541 to serve latest
checkpoint states. However the problem is that `--checkpointState` right
now assumes that we receive a ssz-serialized state bytes, so we also
need to ensure that the REST API only returns SSZ responses by applying
the correct Accept header.

This PR includes the following changes
- Ensure we only receive SSZ responses if REST API is queried
- Consider `--forceCheckpointSync` flag and if set always prioritze
remote checkpoint from `--checkpointState` over latest safe checkpoint
in db
- Minor tweaks to logs to make it easier to understand what the node is
doing
- Misc formatting changes
Copy link

codecov bot commented Oct 13, 2025

Codecov Report

❌ Patch coverage is 17.89976% with 344 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.03%. Comparing base (598c1ec) to head (81cdcb6).
⚠️ Report is 1 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #8527      +/-   ##
============================================
- Coverage     52.21%   52.03%   -0.19%     
============================================
  Files           852      852              
  Lines         65101    65528     +427     
  Branches       4770     4782      +12     
============================================
+ Hits          33995    34098     +103     
- Misses        31037    31361     +324     
  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.

Copy link
Contributor

github-actions bot commented Oct 13, 2025

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 3eef12e Previous: d9cc6b9 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 929.83 us/op 846.92 us/op 1.10
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 33.503 us/op 34.283 us/op 0.98
BLS verify - blst 1.1623 ms/op 855.45 us/op 1.36
BLS verifyMultipleSignatures 3 - blst 2.2482 ms/op 1.3962 ms/op 1.61
BLS verifyMultipleSignatures 8 - blst 2.3153 ms/op 2.1045 ms/op 1.10
BLS verifyMultipleSignatures 32 - blst 7.2731 ms/op 4.8744 ms/op 1.49
BLS verifyMultipleSignatures 64 - blst 10.400 ms/op 8.9265 ms/op 1.17
BLS verifyMultipleSignatures 128 - blst 16.976 ms/op 16.793 ms/op 1.01
BLS deserializing 10000 signatures 675.54 ms/op 652.80 ms/op 1.03
BLS deserializing 100000 signatures 6.7468 s/op 6.3354 s/op 1.06
BLS verifyMultipleSignatures - same message - 3 - blst 862.15 us/op 973.37 us/op 0.89
BLS verifyMultipleSignatures - same message - 8 - blst 1.0675 ms/op 1.0759 ms/op 0.99
BLS verifyMultipleSignatures - same message - 32 - blst 1.7019 ms/op 1.7422 ms/op 0.98
BLS verifyMultipleSignatures - same message - 64 - blst 2.5340 ms/op 2.6207 ms/op 0.97
BLS verifyMultipleSignatures - same message - 128 - blst 4.3146 ms/op 4.2269 ms/op 1.02
BLS aggregatePubkeys 32 - blst 19.284 us/op 17.993 us/op 1.07
BLS aggregatePubkeys 128 - blst 68.673 us/op 63.471 us/op 1.08
notSeenSlots=1 numMissedVotes=1 numBadVotes=10 47.328 ms/op 39.705 ms/op 1.19
notSeenSlots=1 numMissedVotes=0 numBadVotes=4 51.761 ms/op 40.418 ms/op 1.28
notSeenSlots=2 numMissedVotes=1 numBadVotes=10 38.577 ms/op 36.556 ms/op 1.06
getSlashingsAndExits - default max 74.339 us/op 55.362 us/op 1.34
getSlashingsAndExits - 2k 277.28 us/op 301.06 us/op 0.92
isKnown best case - 1 super set check 202.00 ns/op 420.00 ns/op 0.48
isKnown normal case - 2 super set checks 200.00 ns/op 437.00 ns/op 0.46
isKnown worse case - 16 super set checks 198.00 ns/op 403.00 ns/op 0.49
InMemoryCheckpointStateCache - add get delete 2.3710 us/op 2.4320 us/op 0.97
validate api signedAggregateAndProof - struct 1.4164 ms/op 1.4289 ms/op 0.99
validate gossip signedAggregateAndProof - struct 1.4449 ms/op 1.9188 ms/op 0.75
batch validate gossip attestation - vc 640000 - chunk 32 115.11 us/op 109.40 us/op 1.05
batch validate gossip attestation - vc 640000 - chunk 64 101.71 us/op 94.017 us/op 1.08
batch validate gossip attestation - vc 640000 - chunk 128 93.255 us/op 87.125 us/op 1.07
batch validate gossip attestation - vc 640000 - chunk 256 94.350 us/op 86.663 us/op 1.09
pickEth1Vote - no votes 933.62 us/op 788.21 us/op 1.18
pickEth1Vote - max votes 5.2129 ms/op 4.1871 ms/op 1.24
pickEth1Vote - Eth1Data hashTreeRoot value x2048 10.871 ms/op 8.6273 ms/op 1.26
pickEth1Vote - Eth1Data hashTreeRoot tree x2048 18.618 ms/op 11.511 ms/op 1.62
pickEth1Vote - Eth1Data fastSerialize value x2048 450.42 us/op 351.08 us/op 1.28
pickEth1Vote - Eth1Data fastSerialize tree x2048 3.7113 ms/op 1.9720 ms/op 1.88
bytes32 toHexString 349.00 ns/op 519.00 ns/op 0.67
bytes32 Buffer.toString(hex) 252.00 ns/op 433.00 ns/op 0.58
bytes32 Buffer.toString(hex) from Uint8Array 338.00 ns/op 499.00 ns/op 0.68
bytes32 Buffer.toString(hex) + 0x 256.00 ns/op 431.00 ns/op 0.59
Object access 1 prop 0.11700 ns/op 0.30500 ns/op 0.38
Map access 1 prop 0.12100 ns/op 0.31900 ns/op 0.38
Object get x1000 5.5860 ns/op 5.1480 ns/op 1.09
Map get x1000 6.2760 ns/op 5.7450 ns/op 1.09
Object set x1000 27.494 ns/op 19.100 ns/op 1.44
Map set x1000 18.407 ns/op 16.739 ns/op 1.10
Return object 10000 times 0.27500 ns/op 0.28780 ns/op 0.96
Throw Error 10000 times 4.1151 us/op 3.5836 us/op 1.15
toHex 142.50 ns/op 99.578 ns/op 1.43
Buffer.from 118.95 ns/op 92.241 ns/op 1.29
shared Buffer 76.113 ns/op 66.314 ns/op 1.15
fastMsgIdFn sha256 / 200 bytes 2.0660 us/op 1.9390 us/op 1.07
fastMsgIdFn h32 xxhash / 200 bytes 256.00 ns/op 383.00 ns/op 0.67
fastMsgIdFn h64 xxhash / 200 bytes 262.00 ns/op 439.00 ns/op 0.60
fastMsgIdFn sha256 / 1000 bytes 7.1260 us/op 5.8570 us/op 1.22
fastMsgIdFn h32 xxhash / 1000 bytes 340.00 ns/op 513.00 ns/op 0.66
fastMsgIdFn h64 xxhash / 1000 bytes 339.00 ns/op 512.00 ns/op 0.66
fastMsgIdFn sha256 / 10000 bytes 63.305 us/op 49.221 us/op 1.29
fastMsgIdFn h32 xxhash / 10000 bytes 1.7930 us/op 1.9150 us/op 0.94
fastMsgIdFn h64 xxhash / 10000 bytes 1.1940 us/op 1.3170 us/op 0.91
send data - 1000 256B messages 15.946 ms/op 13.015 ms/op 1.23
send data - 1000 512B messages 18.733 ms/op 15.327 ms/op 1.22
send data - 1000 1024B messages 25.818 ms/op 22.818 ms/op 1.13
send data - 1000 1200B messages 23.075 ms/op 18.915 ms/op 1.22
send data - 1000 2048B messages 24.252 ms/op 19.831 ms/op 1.22
send data - 1000 4096B messages 26.081 ms/op 22.501 ms/op 1.16
send data - 1000 16384B messages 40.100 ms/op 31.560 ms/op 1.27
send data - 1000 65536B messages 113.11 ms/op 92.475 ms/op 1.22
enrSubnets - fastDeserialize 64 bits 863.00 ns/op 962.00 ns/op 0.90
enrSubnets - ssz BitVector 64 bits 339.00 ns/op 487.00 ns/op 0.70
enrSubnets - fastDeserialize 4 bits 145.00 ns/op 324.00 ns/op 0.45
enrSubnets - ssz BitVector 4 bits 335.00 ns/op 493.00 ns/op 0.68
prioritizePeers score -10:0 att 32-0.1 sync 2-0 221.87 us/op 198.69 us/op 1.12
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 251.99 us/op 236.04 us/op 1.07
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 359.81 us/op 369.90 us/op 0.97
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 680.96 us/op 679.72 us/op 1.00
prioritizePeers score 0:0 att 64-1 sync 4-1 822.22 us/op 756.57 us/op 1.09
array of 16000 items push then shift 1.5640 us/op 1.3044 us/op 1.20
LinkedList of 16000 items push then shift 6.9860 ns/op 6.4520 ns/op 1.08
array of 16000 items push then pop 73.582 ns/op 68.328 ns/op 1.08
LinkedList of 16000 items push then pop 6.8580 ns/op 6.3800 ns/op 1.07
array of 24000 items push then shift 2.3104 us/op 1.9156 us/op 1.21
LinkedList of 24000 items push then shift 7.0090 ns/op 6.3970 ns/op 1.10
array of 24000 items push then pop 97.793 ns/op 98.264 ns/op 1.00
LinkedList of 24000 items push then pop 6.9540 ns/op 6.3370 ns/op 1.10
intersect bitArray bitLen 8 6.2580 ns/op 5.5710 ns/op 1.12
intersect array and set length 8 37.131 ns/op 33.042 ns/op 1.12
intersect bitArray bitLen 128 29.246 ns/op 26.973 ns/op 1.08
intersect array and set length 128 607.79 ns/op 545.08 ns/op 1.12
bitArray.getTrueBitIndexes() bitLen 128 987.00 ns/op 1.1400 us/op 0.87
bitArray.getTrueBitIndexes() bitLen 248 1.7420 us/op 1.8910 us/op 0.92
bitArray.getTrueBitIndexes() bitLen 512 3.5610 us/op 3.7500 us/op 0.95
Buffer.concat 32 items 618.00 ns/op 860.00 ns/op 0.72
Uint8Array.set 32 items 1.0580 us/op 997.00 ns/op 1.06
Buffer.copy 2.0360 us/op 1.9480 us/op 1.05
Uint8Array.set - with subarray 1.5200 us/op 1.5300 us/op 0.99
Uint8Array.set - without subarray 987.00 ns/op 976.00 ns/op 1.01
getUint32 - dataview 201.00 ns/op 376.00 ns/op 0.53
getUint32 - manual 122.00 ns/op 326.00 ns/op 0.37
Set add up to 64 items then delete first 1.9709 us/op 2.0787 us/op 0.95
OrderedSet add up to 64 items then delete first 3.2264 us/op 3.1835 us/op 1.01
Set add up to 64 items then delete last 2.3636 us/op 1.9324 us/op 1.22
OrderedSet add up to 64 items then delete last 3.5017 us/op 3.3680 us/op 1.04
Set add up to 64 items then delete middle 2.2845 us/op 2.1494 us/op 1.06
OrderedSet add up to 64 items then delete middle 6.2440 us/op 4.9055 us/op 1.27
Set add up to 128 items then delete first 4.8260 us/op 4.4119 us/op 1.09
OrderedSet add up to 128 items then delete first 7.5327 us/op 6.8306 us/op 1.10
Set add up to 128 items then delete last 4.8145 us/op 4.1128 us/op 1.17
OrderedSet add up to 128 items then delete last 7.1644 us/op 6.5938 us/op 1.09
Set add up to 128 items then delete middle 4.7253 us/op 4.2002 us/op 1.13
OrderedSet add up to 128 items then delete middle 13.589 us/op 13.052 us/op 1.04
Set add up to 256 items then delete first 10.401 us/op 10.558 us/op 0.99
OrderedSet add up to 256 items then delete first 15.561 us/op 13.032 us/op 1.19
Set add up to 256 items then delete last 9.4323 us/op 8.0138 us/op 1.18
OrderedSet add up to 256 items then delete last 14.148 us/op 13.625 us/op 1.04
Set add up to 256 items then delete middle 9.5003 us/op 8.2851 us/op 1.15
OrderedSet add up to 256 items then delete middle 40.482 us/op 39.248 us/op 1.03
transfer serialized Status (84 B) 2.1290 us/op 1.8880 us/op 1.13
copy serialized Status (84 B) 1.0590 us/op 1.1610 us/op 0.91
transfer serialized SignedVoluntaryExit (112 B) 2.1360 us/op 1.8860 us/op 1.13
copy serialized SignedVoluntaryExit (112 B) 1.1510 us/op 1.1510 us/op 1.00
transfer serialized ProposerSlashing (416 B) 2.2360 us/op 1.9130 us/op 1.17
copy serialized ProposerSlashing (416 B) 1.2170 us/op 1.1860 us/op 1.03
transfer serialized Attestation (485 B) 2.3970 us/op 1.9110 us/op 1.25
copy serialized Attestation (485 B) 1.8750 us/op 1.2090 us/op 1.55
transfer serialized AttesterSlashing (33232 B) 2.4140 us/op 1.9300 us/op 1.25
copy serialized AttesterSlashing (33232 B) 3.9920 us/op 3.5510 us/op 1.12
transfer serialized Small SignedBeaconBlock (128000 B) 2.9870 us/op 2.0400 us/op 1.46
copy serialized Small SignedBeaconBlock (128000 B) 12.257 us/op 10.181 us/op 1.20
transfer serialized Avg SignedBeaconBlock (200000 B) 3.5590 us/op 2.1380 us/op 1.66
copy serialized Avg SignedBeaconBlock (200000 B) 12.884 us/op 14.279 us/op 0.90
transfer serialized BlobsSidecar (524380 B) 3.4750 us/op 2.6540 us/op 1.31
copy serialized BlobsSidecar (524380 B) 92.333 us/op 75.132 us/op 1.23
transfer serialized Big SignedBeaconBlock (1000000 B) 3.5320 us/op 3.1000 us/op 1.14
copy serialized Big SignedBeaconBlock (1000000 B) 270.22 us/op 126.21 us/op 2.14
pass gossip attestations to forkchoice per slot 2.6361 ms/op 2.4762 ms/op 1.06
forkChoice updateHead vc 100000 bc 64 eq 0 499.39 us/op 421.56 us/op 1.18
forkChoice updateHead vc 600000 bc 64 eq 0 3.0488 ms/op 2.5942 ms/op 1.18
forkChoice updateHead vc 1000000 bc 64 eq 0 5.2259 ms/op 5.1686 ms/op 1.01
forkChoice updateHead vc 600000 bc 320 eq 0 3.0548 ms/op 2.5075 ms/op 1.22
forkChoice updateHead vc 600000 bc 1200 eq 0 3.1006 ms/op 2.6568 ms/op 1.17
forkChoice updateHead vc 600000 bc 7200 eq 0 3.3613 ms/op 2.7503 ms/op 1.22
forkChoice updateHead vc 600000 bc 64 eq 1000 3.0977 ms/op 2.6949 ms/op 1.15
forkChoice updateHead vc 600000 bc 64 eq 10000 3.2051 ms/op 2.5760 ms/op 1.24
forkChoice updateHead vc 600000 bc 64 eq 300000 9.2734 ms/op 7.9715 ms/op 1.16
computeDeltas 1400000 validators 300 proto nodes 11.211 ms/op 9.9614 ms/op 1.13
computeDeltas 1400000 validators 1200 proto nodes 11.177 ms/op 9.8861 ms/op 1.13
computeDeltas 1400000 validators 7200 proto nodes 11.248 ms/op 9.8100 ms/op 1.15
computeDeltas 2100000 validators 300 proto nodes 16.787 ms/op 14.772 ms/op 1.14
computeDeltas 2100000 validators 1200 proto nodes 17.019 ms/op 14.782 ms/op 1.15
computeDeltas 2100000 validators 7200 proto nodes 16.958 ms/op 14.894 ms/op 1.14
altair processAttestation - 250000 vs - 7PWei normalcase 2.0480 ms/op 1.8188 ms/op 1.13
altair processAttestation - 250000 vs - 7PWei worstcase 2.9106 ms/op 3.2904 ms/op 0.88
altair processAttestation - setStatus - 1/6 committees join 126.22 us/op 176.68 us/op 0.71
altair processAttestation - setStatus - 1/3 committees join 244.64 us/op 224.80 us/op 1.09
altair processAttestation - setStatus - 1/2 committees join 338.11 us/op 325.45 us/op 1.04
altair processAttestation - setStatus - 2/3 committees join 438.93 us/op 406.03 us/op 1.08
altair processAttestation - setStatus - 4/5 committees join 573.32 us/op 502.12 us/op 1.14
altair processAttestation - setStatus - 100% committees join 680.15 us/op 602.64 us/op 1.13
altair processBlock - 250000 vs - 7PWei normalcase 5.7747 ms/op 3.7711 ms/op 1.53
altair processBlock - 250000 vs - 7PWei normalcase hashState 39.111 ms/op 31.317 ms/op 1.25
altair processBlock - 250000 vs - 7PWei worstcase 45.044 ms/op 34.735 ms/op 1.30
altair processBlock - 250000 vs - 7PWei worstcase hashState 108.82 ms/op 93.166 ms/op 1.17
phase0 processBlock - 250000 vs - 7PWei normalcase 2.2750 ms/op 1.6186 ms/op 1.41
phase0 processBlock - 250000 vs - 7PWei worstcase 29.010 ms/op 26.255 ms/op 1.10
altair processEth1Data - 250000 vs - 7PWei normalcase 331.68 us/op 260.36 us/op 1.27
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:15 9.3100 us/op 5.4470 us/op 1.71
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:219 55.831 us/op 32.323 us/op 1.73
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:42 15.959 us/op 9.6710 us/op 1.65
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:18 9.6340 us/op 3.4920 us/op 2.76
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1020 176.04 us/op 151.54 us/op 1.16
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11777 1.9737 ms/op 1.3746 ms/op 1.44
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.5995 ms/op 1.8316 ms/op 1.42
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.6927 ms/op 1.8195 ms/op 1.48
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.7429 ms/op 3.6570 ms/op 1.30
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.5318 ms/op 1.7710 ms/op 1.43
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.7668 ms/op 3.5020 ms/op 1.36
Tree 40 250000 create 485.27 ms/op 345.44 ms/op 1.40
Tree 40 250000 get(125000) 138.55 ns/op 107.35 ns/op 1.29
Tree 40 250000 set(125000) 1.4978 us/op 1.1932 us/op 1.26
Tree 40 250000 toArray() 17.879 ms/op 9.3070 ms/op 1.92
Tree 40 250000 iterate all - toArray() + loop 18.937 ms/op 9.2918 ms/op 2.04
Tree 40 250000 iterate all - get(i) 53.837 ms/op 37.736 ms/op 1.43
Array 250000 create 2.8136 ms/op 1.9175 ms/op 1.47
Array 250000 clone - spread 793.16 us/op 624.22 us/op 1.27
Array 250000 get(125000) 0.40800 ns/op 0.55800 ns/op 0.73
Array 250000 set(125000) 0.50700 ns/op 0.57900 ns/op 0.88
Array 250000 iterate all - loop 81.342 us/op 100.49 us/op 0.81
phase0 afterProcessEpoch - 250000 vs - 7PWei 40.712 ms/op 37.838 ms/op 1.08
Array.fill - length 1000000 3.3043 ms/op 2.6348 ms/op 1.25
Array push - length 1000000 12.143 ms/op 9.2668 ms/op 1.31
Array.get 0.26616 ns/op 0.24970 ns/op 1.07
Uint8Array.get 0.43040 ns/op 0.40589 ns/op 1.06
phase0 beforeProcessEpoch - 250000 vs - 7PWei 15.942 ms/op 17.904 ms/op 0.89
altair processEpoch - mainnet_e81889 289.48 ms/op 211.41 ms/op 1.37
mainnet_e81889 - altair beforeProcessEpoch 18.439 ms/op 14.827 ms/op 1.24
mainnet_e81889 - altair processJustificationAndFinalization 7.6810 us/op 4.3430 us/op 1.77
mainnet_e81889 - altair processInactivityUpdates 3.9763 ms/op 3.5414 ms/op 1.12
mainnet_e81889 - altair processRewardsAndPenalties 54.943 ms/op 26.360 ms/op 2.08
mainnet_e81889 - altair processRegistryUpdates 714.00 ns/op 867.00 ns/op 0.82
mainnet_e81889 - altair processSlashings 181.00 ns/op 410.00 ns/op 0.44
mainnet_e81889 - altair processEth1DataReset 171.00 ns/op 405.00 ns/op 0.42
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.1626 ms/op 1.1027 ms/op 1.05
mainnet_e81889 - altair processSlashingsReset 1.0180 us/op 1.2300 us/op 0.83
mainnet_e81889 - altair processRandaoMixesReset 1.0770 us/op 1.1740 us/op 0.92
mainnet_e81889 - altair processHistoricalRootsUpdate 171.00 ns/op 410.00 ns/op 0.42
mainnet_e81889 - altair processParticipationFlagUpdates 495.00 ns/op 703.00 ns/op 0.70
mainnet_e81889 - altair processSyncCommitteeUpdates 133.00 ns/op 370.00 ns/op 0.36
mainnet_e81889 - altair afterProcessEpoch 41.569 ms/op 41.249 ms/op 1.01
capella processEpoch - mainnet_e217614 1.0247 s/op 827.16 ms/op 1.24
mainnet_e217614 - capella beforeProcessEpoch 60.076 ms/op 62.518 ms/op 0.96
mainnet_e217614 - capella processJustificationAndFinalization 5.1880 us/op 4.6910 us/op 1.11
mainnet_e217614 - capella processInactivityUpdates 13.532 ms/op 12.437 ms/op 1.09
mainnet_e217614 - capella processRewardsAndPenalties 220.51 ms/op 180.59 ms/op 1.22
mainnet_e217614 - capella processRegistryUpdates 6.2530 us/op 5.3190 us/op 1.18
mainnet_e217614 - capella processSlashings 182.00 ns/op 405.00 ns/op 0.45
mainnet_e217614 - capella processEth1DataReset 171.00 ns/op 402.00 ns/op 0.43
mainnet_e217614 - capella processEffectiveBalanceUpdates 4.0221 ms/op 3.9889 ms/op 1.01
mainnet_e217614 - capella processSlashingsReset 939.00 ns/op 1.2990 us/op 0.72
mainnet_e217614 - capella processRandaoMixesReset 1.3830 us/op 1.2020 us/op 1.15
mainnet_e217614 - capella processHistoricalRootsUpdate 174.00 ns/op 405.00 ns/op 0.43
mainnet_e217614 - capella processParticipationFlagUpdates 535.00 ns/op 703.00 ns/op 0.76
mainnet_e217614 - capella afterProcessEpoch 109.67 ms/op 104.67 ms/op 1.05
phase0 processEpoch - mainnet_e58758 302.24 ms/op 244.88 ms/op 1.23
mainnet_e58758 - phase0 beforeProcessEpoch 84.757 ms/op 69.784 ms/op 1.21
mainnet_e58758 - phase0 processJustificationAndFinalization 5.4810 us/op 4.4610 us/op 1.23
mainnet_e58758 - phase0 processRewardsAndPenalties 47.864 ms/op 41.414 ms/op 1.16
mainnet_e58758 - phase0 processRegistryUpdates 3.1400 us/op 2.7210 us/op 1.15
mainnet_e58758 - phase0 processSlashings 172.00 ns/op 394.00 ns/op 0.44
mainnet_e58758 - phase0 processEth1DataReset 168.00 ns/op 381.00 ns/op 0.44
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1090 ms/op 916.56 us/op 1.21
mainnet_e58758 - phase0 processSlashingsReset 845.00 ns/op 1.0930 us/op 0.77
mainnet_e58758 - phase0 processRandaoMixesReset 1.2880 us/op 1.1970 us/op 1.08
mainnet_e58758 - phase0 processHistoricalRootsUpdate 174.00 ns/op 387.00 ns/op 0.45
mainnet_e58758 - phase0 processParticipationRecordUpdates 876.00 ns/op 1.1670 us/op 0.75
mainnet_e58758 - phase0 afterProcessEpoch 34.800 ms/op 33.066 ms/op 1.05
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.3531 ms/op 939.25 us/op 1.44
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 3.1319 ms/op 1.3830 ms/op 2.26
altair processInactivityUpdates - 250000 normalcase 22.661 ms/op 16.748 ms/op 1.35
altair processInactivityUpdates - 250000 worstcase 25.218 ms/op 18.101 ms/op 1.39
phase0 processRegistryUpdates - 250000 normalcase 10.963 us/op 4.9410 us/op 2.22
phase0 processRegistryUpdates - 250000 badcase_full_deposits 420.56 us/op 274.04 us/op 1.53
phase0 processRegistryUpdates - 250000 worstcase 0.5 114.40 ms/op 110.16 ms/op 1.04
altair processRewardsAndPenalties - 250000 normalcase 38.907 ms/op 29.761 ms/op 1.31
altair processRewardsAndPenalties - 250000 worstcase 38.068 ms/op 30.494 ms/op 1.25
phase0 getAttestationDeltas - 250000 normalcase 6.9356 ms/op 6.3507 ms/op 1.09
phase0 getAttestationDeltas - 250000 worstcase 6.6890 ms/op 6.3092 ms/op 1.06
phase0 processSlashings - 250000 worstcase 112.79 us/op 83.326 us/op 1.35
altair processSyncCommitteeUpdates - 250000 10.810 ms/op 9.8397 ms/op 1.10
BeaconState.hashTreeRoot - No change 210.00 ns/op 438.00 ns/op 0.48
BeaconState.hashTreeRoot - 1 full validator 103.79 us/op 69.708 us/op 1.49
BeaconState.hashTreeRoot - 32 full validator 1.5375 ms/op 703.41 us/op 2.19
BeaconState.hashTreeRoot - 512 full validator 10.908 ms/op 7.0407 ms/op 1.55
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 118.88 us/op 71.580 us/op 1.66
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.5969 ms/op 1.0295 ms/op 1.55
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 23.557 ms/op 14.651 ms/op 1.61
BeaconState.hashTreeRoot - 1 balances 108.67 us/op 56.647 us/op 1.92
BeaconState.hashTreeRoot - 32 balances 961.23 us/op 532.74 us/op 1.80
BeaconState.hashTreeRoot - 512 balances 10.198 ms/op 8.1674 ms/op 1.25
BeaconState.hashTreeRoot - 250000 balances 206.07 ms/op 182.27 ms/op 1.13
aggregationBits - 2048 els - zipIndexesInBitList 21.149 us/op 18.902 us/op 1.12
byteArrayEquals 32 52.948 ns/op 41.595 ns/op 1.27
Buffer.compare 32 17.460 ns/op 14.724 ns/op 1.19
byteArrayEquals 1024 1.5849 us/op 1.2171 us/op 1.30
Buffer.compare 1024 24.577 ns/op 22.641 ns/op 1.09
byteArrayEquals 16384 25.008 us/op 19.377 us/op 1.29
Buffer.compare 16384 203.80 ns/op 162.71 ns/op 1.25
byteArrayEquals 123687377 184.27 ms/op 148.05 ms/op 1.24
Buffer.compare 123687377 6.0693 ms/op 3.7746 ms/op 1.61
byteArrayEquals 32 - diff last byte 51.119 ns/op 42.781 ns/op 1.19
Buffer.compare 32 - diff last byte 18.194 ns/op 16.440 ns/op 1.11
byteArrayEquals 1024 - diff last byte 1.5519 us/op 1.2780 us/op 1.21
Buffer.compare 1024 - diff last byte 28.440 ns/op 23.316 ns/op 1.22
byteArrayEquals 16384 - diff last byte 24.762 us/op 20.147 us/op 1.23
Buffer.compare 16384 - diff last byte 190.43 ns/op 190.70 ns/op 1.00
byteArrayEquals 123687377 - diff last byte 185.84 ms/op 151.20 ms/op 1.23
Buffer.compare 123687377 - diff last byte 6.1174 ms/op 3.7148 ms/op 1.65
byteArrayEquals 32 - random bytes 4.9540 ns/op 4.9860 ns/op 0.99
Buffer.compare 32 - random bytes 17.170 ns/op 15.585 ns/op 1.10
byteArrayEquals 1024 - random bytes 4.9410 ns/op 4.8460 ns/op 1.02
Buffer.compare 1024 - random bytes 16.853 ns/op 15.414 ns/op 1.09
byteArrayEquals 16384 - random bytes 4.9480 ns/op 4.8370 ns/op 1.02
Buffer.compare 16384 - random bytes 16.847 ns/op 15.441 ns/op 1.09
byteArrayEquals 123687377 - random bytes 6.3200 ns/op 7.6300 ns/op 0.83
Buffer.compare 123687377 - random bytes 18.170 ns/op 18.610 ns/op 0.98
regular array get 100000 times 32.424 us/op 40.099 us/op 0.81
wrappedArray get 100000 times 32.366 us/op 40.059 us/op 0.81
arrayWithProxy get 100000 times 11.813 ms/op 9.6671 ms/op 1.22
ssz.Root.equals 43.982 ns/op 39.176 ns/op 1.12
byteArrayEquals 43.479 ns/op 38.577 ns/op 1.13
Buffer.compare 9.9200 ns/op 8.9110 ns/op 1.11
processSlot - 1 slots 10.448 us/op 8.3320 us/op 1.25
processSlot - 32 slots 4.1539 ms/op 1.5758 ms/op 2.64
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 2.8786 ms/op 1.9461 ms/op 1.48
getCommitteeAssignments - req 1 vs - 250000 vc 2.0414 ms/op 1.8017 ms/op 1.13
getCommitteeAssignments - req 100 vs - 250000 vc 3.9651 ms/op 3.5302 ms/op 1.12
getCommitteeAssignments - req 1000 vs - 250000 vc 4.2148 ms/op 3.7543 ms/op 1.12
findModifiedValidators - 10000 modified validators 726.90 ms/op 739.09 ms/op 0.98
findModifiedValidators - 1000 modified validators 702.40 ms/op 640.56 ms/op 1.10
findModifiedValidators - 100 modified validators 162.53 ms/op 154.93 ms/op 1.05
findModifiedValidators - 10 modified validators 162.57 ms/op 122.01 ms/op 1.33
findModifiedValidators - 1 modified validators 133.95 ms/op 124.38 ms/op 1.08
findModifiedValidators - no difference 139.45 ms/op 128.61 ms/op 1.08
compare ViewDUs 6.1305 s/op 5.9992 s/op 1.02
compare each validator Uint8Array 1.8158 s/op 1.5490 s/op 1.17
compare ViewDU to Uint8Array 911.51 ms/op 837.65 ms/op 1.09
migrate state 1000000 validators, 24 modified, 0 new 925.63 ms/op 825.61 ms/op 1.12
migrate state 1000000 validators, 1700 modified, 1000 new 1.2168 s/op 1.1185 s/op 1.09
migrate state 1000000 validators, 3400 modified, 2000 new 1.3194 s/op 1.2090 s/op 1.09
migrate state 1500000 validators, 24 modified, 0 new 972.89 ms/op 830.17 ms/op 1.17
migrate state 1500000 validators, 1700 modified, 1000 new 1.2249 s/op 1.0284 s/op 1.19
migrate state 1500000 validators, 3400 modified, 2000 new 1.2773 s/op 1.1037 s/op 1.16
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.5700 ns/op 5.6300 ns/op 0.81
state getBlockRootAtSlot - 250000 vs - 7PWei 596.08 ns/op 427.68 ns/op 1.39
naive computeProposerIndex 100000 validators 48.549 ms/op 38.471 ms/op 1.26
computeProposerIndex 100000 validators 1.4551 ms/op 1.2717 ms/op 1.14
naiveGetNextSyncCommitteeIndices 1000 validators 7.9040 s/op 5.7193 s/op 1.38
getNextSyncCommitteeIndices 1000 validators 129.64 ms/op 97.358 ms/op 1.33
naiveGetNextSyncCommitteeIndices 10000 validators 7.9720 s/op 6.0401 s/op 1.32
getNextSyncCommitteeIndices 10000 validators 119.90 ms/op 99.131 ms/op 1.21
naiveGetNextSyncCommitteeIndices 100000 validators 7.3316 s/op 5.7382 s/op 1.28
getNextSyncCommitteeIndices 100000 validators 114.03 ms/op 96.988 ms/op 1.18
naive computeShuffledIndex 100000 validators 23.435 s/op 20.431 s/op 1.15
cached computeShuffledIndex 100000 validators 539.58 ms/op 467.59 ms/op 1.15
naive computeShuffledIndex 2000000 validators 504.93 s/op 371.53 s/op 1.36
cached computeShuffledIndex 2000000 validators 37.378 s/op 25.093 s/op 1.49
computeProposers - vc 250000 595.77 us/op 531.28 us/op 1.12
computeEpochShuffling - vc 250000 41.927 ms/op 36.830 ms/op 1.14
getNextSyncCommittee - vc 250000 10.393 ms/op 8.8818 ms/op 1.17
computeSigningRoot for AttestationData 20.672 us/op 15.998 us/op 1.29
hash AttestationData serialized data then Buffer.toString(base64) 1.5966 us/op 1.1406 us/op 1.40
toHexString serialized data 1.2771 us/op 937.57 ns/op 1.36
Buffer.toString(base64) 150.17 ns/op 116.05 ns/op 1.29
nodejs block root to RootHex using toHex 156.27 ns/op 102.38 ns/op 1.53
nodejs block root to RootHex using toRootHex 88.934 ns/op 67.385 ns/op 1.32
nodejs fromhex(blob) 114.00 ms/op 106.18 ms/op 1.07
nodejs fromHexInto(blob) 95.100 ms/op 75.440 ms/op 1.26
browser block root to RootHex using the deprecated toHexString 213.08 ns/op 178.99 ns/op 1.19
browser block root to RootHex using toHex 173.33 ns/op 143.48 ns/op 1.21
browser block root to RootHex using toRootHex 170.74 ns/op 133.47 ns/op 1.28
browser fromHexInto(blob) 826.35 us/op 615.59 us/op 1.34
browser fromHex(blob) 802.09 ms/op 618.18 ms/op 1.30

by benchmarkbot/action

@nflaig
Copy link
Member Author

nflaig commented Oct 13, 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 a significant feature: the ability to sync from an unfinalized checkpoint state. This is particularly useful for faster node startup and recovery, especially during periods of non-finality. The implementation is comprehensive, touching upon API definitions, CLI options, state initialization logic, and the core fork choice mechanism. The introduction of initializeForkChoiceFromUnfinalizedState with its proto-array scaffolding is a clever approach to handle this complex scenario. The code is well-structured, and the new "unsafe" options are appropriately hidden and documented with warnings. Overall, this is a solid implementation of a complex feature. I have a couple of minor suggestions for code quality and performance improvements.

Comment on lines +273 to +283
/**
* Extract a checkpoint from a string in the format `rootHex:epoch`.
*/
export function getCheckpointFromArg(checkpointStr: string): Checkpoint {
const checkpointRegex = /^(?:0x)?([0-9a-f]{64}):([0-9]+)$/;
const match = checkpointRegex.exec(checkpointStr.toLowerCase());
if (!match) {
throw new ApiError(400, `Could not parse checkpoint string: ${checkpointStr}`);
}
return {root: fromHex(match[1]), epoch: parseInt(match[2])};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For a minor performance improvement and to follow best practices, the regular expression can be defined as a constant outside the function. This prevents it from being recompiled on every function call.

Suggested change
/**
* Extract a checkpoint from a string in the format `rootHex:epoch`.
*/
export function getCheckpointFromArg(checkpointStr: string): Checkpoint {
const checkpointRegex = /^(?:0x)?([0-9a-f]{64}):([0-9]+)$/;
const match = checkpointRegex.exec(checkpointStr.toLowerCase());
if (!match) {
throw new ApiError(400, `Could not parse checkpoint string: ${checkpointStr}`);
}
return {root: fromHex(match[1]), epoch: parseInt(match[2])};
}
const CHECKPOINT_REGEX = /^(?:0x)?([0-9a-f]{64}):([0-9]+)$/;
/**
* Extract a checkpoint from a string in the format `rootHex:epoch`.
*/
export function getCheckpointFromArg(checkpointStr: string): Checkpoint {
const match = CHECKPOINT_REGEX.exec(checkpointStr.toLowerCase());
if (!match) {
throw new ApiError(400, `Could not parse checkpoint string: ${checkpointStr}`);
}
return {root: fromHex(match[1]), epoch: parseInt(match[2])};
}

Comment on lines +82 to +85
if (datastoreKey == null) {
// should not happen
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This null check for datastoreKey is redundant. The epochsDesc array is created from the keys of dataStoreKeyByEpoch, so dataStoreKeyByEpoch.get(epoch) will never be null for any epoch in the loop. This block can be safely removed to improve code clarity.

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