Skip to content

fix: dedupe persistent-merkle-tree to recover hashtree hasher - #9352

Merged
nflaig merged 3 commits into
unstablefrom
te/pmt_1.2.5
May 11, 2026
Merged

fix: dedupe persistent-merkle-tree to recover hashtree hasher#9352
nflaig merged 3 commits into
unstablefrom
te/pmt_1.2.5

Conversation

@twoeths

@twoeths twoeths commented May 11, 2026

Copy link
Copy Markdown
Member

Motivation

After #9211 bumped @chainsafe/ssz to ^1.4.0, the lockfile resolves two copies of @chainsafe/persistent-merkle-tree: 1.2.1 (direct pin) and 1.2.5 (transitive under ssz, which declares an empty PMT constraint).
Each PMT module carries its own hasher singleton.
packages/cli/src/applyPreset.ts only configures PMT 1.2.1 with hashtree; the 1.2.5 copy stays on the JS noble default.
Every hashTreeRoot going through ssz's internal merkleize.js now allocates per digest, causing significantly higher GC on mainnet.
Screenshot 2026-05-11 at 13 15 52

Description

  • Bump direct @chainsafe/persistent-merkle-tree pin from ^1.2.1 to ^1.2.5 across all workspace packages so pnpm dedupes to a single instance.
  • Add scripts/assert_persistent_merkle_tree_dedup.sh (wired into the lint job) to fail CI if the lockfile ever splits 1.x again.

AI Assistance Disclosure

Used Claude Code.

@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 updates the @chainsafe/persistent-merkle-tree dependency to version 1.2.5 across multiple packages and introduces a shell script to ensure only a single version of this package exists in the lockfile to prevent performance regressions. Feedback focuses on the robustness of the new script: it currently lacks integration into the CI pipeline, uses a brittle regex that may miss pre-releases or peer-dependency suffixes, and fails to verify that at least one instance of the package is found, potentially leading to false positives.

Comment thread scripts/assert_persistent_merkle_tree_dedup.sh
Comment thread scripts/assert_persistent_merkle_tree_dedup.sh Outdated
Comment thread scripts/assert_persistent_merkle_tree_dedup.sh Outdated
- match any version (drop hardcoded 1.x major), so a future 1.x+2.x split is also caught
- handle pre-release tags and pnpm peer-dep suffixes in lockfile keys
- exempt @ethereumjs/util's legacy persistent-merkle-tree@0.6.1 (predates setHasher API)
- fail on zero matches to catch lockfile-format drift
@github-actions

Copy link
Copy Markdown
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: d0b20ec Previous: 6f485b1 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 859.19 us/op 903.74 us/op 0.95
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 39.348 us/op 39.050 us/op 1.01
BLS verify - blst 773.23 us/op 630.06 us/op 1.23
BLS verifyMultipleSignatures 3 - blst 1.3811 ms/op 1.3129 ms/op 1.05
BLS verifyMultipleSignatures 8 - blst 2.1955 ms/op 2.0777 ms/op 1.06
BLS verifyMultipleSignatures 32 - blst 6.9857 ms/op 6.4444 ms/op 1.08
BLS verifyMultipleSignatures 64 - blst 13.121 ms/op 12.658 ms/op 1.04
BLS verifyMultipleSignatures 128 - blst 25.670 ms/op 24.527 ms/op 1.05
BLS deserializing 10000 signatures 631.73 ms/op 622.17 ms/op 1.02
BLS deserializing 100000 signatures 6.3015 s/op 6.2463 s/op 1.01
BLS verifyMultipleSignatures - same message - 3 - blst 812.59 us/op 686.67 us/op 1.18
BLS verifyMultipleSignatures - same message - 8 - blst 940.35 us/op 892.55 us/op 1.05
BLS verifyMultipleSignatures - same message - 32 - blst 1.5417 ms/op 1.5040 ms/op 1.03
BLS verifyMultipleSignatures - same message - 64 - blst 2.3528 ms/op 2.3098 ms/op 1.02
BLS verifyMultipleSignatures - same message - 128 - blst 4.0147 ms/op 3.9373 ms/op 1.02
BLS aggregatePubkeys 32 - blst 17.655 us/op 16.976 us/op 1.04
BLS aggregatePubkeys 128 - blst 63.317 us/op 60.366 us/op 1.05
getSlashingsAndExits - default max 47.586 us/op 43.306 us/op 1.10
getSlashingsAndExits - 2k 323.04 us/op 351.28 us/op 0.92
proposeBlockBody type=full, size=empty 627.43 us/op 712.57 us/op 0.88
isKnown best case - 1 super set check 168.00 ns/op 165.00 ns/op 1.02
isKnown normal case - 2 super set checks 163.00 ns/op 163.00 ns/op 1.00
isKnown worse case - 16 super set checks 165.00 ns/op 163.00 ns/op 1.01
validate api signedAggregateAndProof - struct 1.5199 ms/op 1.4289 ms/op 1.06
validate gossip signedAggregateAndProof - struct 1.5159 ms/op 1.4342 ms/op 1.06
batch validate gossip attestation - vc 640000 - chunk 32 105.10 us/op 104.91 us/op 1.00
batch validate gossip attestation - vc 640000 - chunk 64 92.108 us/op 89.948 us/op 1.02
batch validate gossip attestation - vc 640000 - chunk 128 85.179 us/op 84.487 us/op 1.01
batch validate gossip attestation - vc 640000 - chunk 256 81.169 us/op 82.347 us/op 0.99
bytes32 toHexString 292.00 ns/op 272.00 ns/op 1.07
bytes32 Buffer.toString(hex) 166.00 ns/op 165.00 ns/op 1.01
bytes32 Buffer.toString(hex) from Uint8Array 234.00 ns/op 235.00 ns/op 1.00
bytes32 Buffer.toString(hex) + 0x 168.00 ns/op 165.00 ns/op 1.02
Return object 10000 times 0.20920 ns/op 0.20710 ns/op 1.01
Throw Error 10000 times 3.3008 us/op 3.2532 us/op 1.01
toHex 98.366 ns/op 91.847 ns/op 1.07
Buffer.from 88.664 ns/op 87.823 ns/op 1.01
shared Buffer 60.007 ns/op 61.229 ns/op 0.98
fastMsgIdFn sha256 / 200 bytes 1.4710 us/op 1.4340 us/op 1.03
fastMsgIdFn h32 xxhash / 200 bytes 150.00 ns/op 145.00 ns/op 1.03
fastMsgIdFn h64 xxhash / 200 bytes 200.00 ns/op 194.00 ns/op 1.03
fastMsgIdFn sha256 / 1000 bytes 4.7790 us/op 4.6100 us/op 1.04
fastMsgIdFn h32 xxhash / 1000 bytes 237.00 ns/op 232.00 ns/op 1.02
fastMsgIdFn h64 xxhash / 1000 bytes 246.00 ns/op 238.00 ns/op 1.03
fastMsgIdFn sha256 / 10000 bytes 41.982 us/op 40.704 us/op 1.03
fastMsgIdFn h32 xxhash / 10000 bytes 1.2610 us/op 1.2510 us/op 1.01
fastMsgIdFn h64 xxhash / 10000 bytes 814.00 ns/op 792.00 ns/op 1.03
send data - 1000 256B messages 3.9154 ms/op 4.0599 ms/op 0.96
send data - 1000 512B messages 4.0196 ms/op 4.1768 ms/op 0.96
send data - 1000 1024B messages 4.2788 ms/op 4.3512 ms/op 0.98
send data - 1000 1200B messages 4.4638 ms/op 4.6816 ms/op 0.95
send data - 1000 2048B messages 4.4707 ms/op 4.8143 ms/op 0.93
send data - 1000 4096B messages 5.1500 ms/op 5.3882 ms/op 0.96
send data - 1000 16384B messages 25.630 ms/op 14.866 ms/op 1.72
send data - 1000 65536B messages 204.69 ms/op 169.84 ms/op 1.21
enrSubnets - fastDeserialize 64 bits 725.00 ns/op 761.00 ns/op 0.95
enrSubnets - ssz BitVector 64 bits 260.00 ns/op 266.00 ns/op 0.98
enrSubnets - fastDeserialize 4 bits 100.00 ns/op 103.00 ns/op 0.97
enrSubnets - ssz BitVector 4 bits 265.00 ns/op 265.00 ns/op 1.00
prioritizePeers score -10:0 att 32-0.1 sync 2-0 208.78 us/op 208.57 us/op 1.00
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 233.79 us/op 248.44 us/op 0.94
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 338.70 us/op 370.67 us/op 0.91
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 593.57 us/op 612.62 us/op 0.97
prioritizePeers score 0:0 att 64-1 sync 4-1 691.40 us/op 719.81 us/op 0.96
array of 16000 items push then shift 1.2847 us/op 1.2645 us/op 1.02
LinkedList of 16000 items push then shift 6.6340 ns/op 7.9990 ns/op 0.83
array of 16000 items push then pop 66.610 ns/op 73.460 ns/op 0.91
LinkedList of 16000 items push then pop 5.9950 ns/op 6.2660 ns/op 0.96
array of 24000 items push then shift 1.8997 us/op 1.8995 us/op 1.00
LinkedList of 24000 items push then shift 6.2650 ns/op 8.0430 ns/op 0.78
array of 24000 items push then pop 92.383 ns/op 98.340 ns/op 0.94
LinkedList of 24000 items push then pop 6.0240 ns/op 6.2350 ns/op 0.97
intersect bitArray bitLen 8 4.6890 ns/op 4.7680 ns/op 0.98
intersect array and set length 8 31.395 ns/op 29.505 ns/op 1.06
intersect bitArray bitLen 128 23.802 ns/op 23.769 ns/op 1.00
intersect array and set length 128 507.93 ns/op 494.59 ns/op 1.03
bitArray.getTrueBitIndexes() bitLen 128 1.1000 us/op 1.0240 us/op 1.07
bitArray.getTrueBitIndexes() bitLen 248 1.7920 us/op 1.7790 us/op 1.01
bitArray.getTrueBitIndexes() bitLen 512 3.5270 us/op 3.5700 us/op 0.99
Full columns - reconstruct all 6 blobs 128.25 us/op 119.11 us/op 1.08
Full columns - reconstruct half of the blobs out of 6 61.191 us/op 93.860 us/op 0.65
Full columns - reconstruct single blob out of 6 62.687 us/op 32.587 us/op 1.92
Half columns - reconstruct all 6 blobs 375.20 ms/op 386.15 ms/op 0.97
Half columns - reconstruct half of the blobs out of 6 188.19 ms/op 187.19 ms/op 1.01
Half columns - reconstruct single blob out of 6 66.650 ms/op 65.541 ms/op 1.02
Full columns - reconstruct all 10 blobs 640.14 us/op 434.55 us/op 1.47
Full columns - reconstruct half of the blobs out of 10 91.455 us/op 106.92 us/op 0.86
Full columns - reconstruct single blob out of 10 35.386 us/op 30.360 us/op 1.17
Half columns - reconstruct all 10 blobs 620.45 ms/op 615.30 ms/op 1.01
Half columns - reconstruct half of the blobs out of 10 313.80 ms/op 309.39 ms/op 1.01
Half columns - reconstruct single blob out of 10 66.446 ms/op 66.101 ms/op 1.01
Full columns - reconstruct all 20 blobs 551.39 us/op 555.76 us/op 0.99
Full columns - reconstruct half of the blobs out of 20 308.33 us/op 204.81 us/op 1.51
Full columns - reconstruct single blob out of 20 33.051 us/op 40.691 us/op 0.81
Half columns - reconstruct all 20 blobs 1.2402 s/op 1.2244 s/op 1.01
Half columns - reconstruct half of the blobs out of 20 627.04 ms/op 617.83 ms/op 1.01
Half columns - reconstruct single blob out of 20 67.993 ms/op 65.326 ms/op 1.04
Set add up to 64 items then delete first 2.1748 us/op 2.0704 us/op 1.05
OrderedSet add up to 64 items then delete first 3.3934 us/op 3.3073 us/op 1.03
Set add up to 64 items then delete last 2.1783 us/op 2.0889 us/op 1.04
OrderedSet add up to 64 items then delete last 3.3362 us/op 3.3591 us/op 0.99
Set add up to 64 items then delete middle 2.1820 us/op 2.1046 us/op 1.04
OrderedSet add up to 64 items then delete middle 4.7776 us/op 4.7951 us/op 1.00
Set add up to 128 items then delete first 4.3440 us/op 4.0967 us/op 1.06
OrderedSet add up to 128 items then delete first 6.5703 us/op 5.9415 us/op 1.11
Set add up to 128 items then delete last 3.9676 us/op 3.8038 us/op 1.04
OrderedSet add up to 128 items then delete last 5.8300 us/op 5.9389 us/op 0.98
Set add up to 128 items then delete middle 3.9652 us/op 3.8458 us/op 1.03
OrderedSet add up to 128 items then delete middle 11.797 us/op 11.616 us/op 1.02
Set add up to 256 items then delete first 8.0738 us/op 7.5170 us/op 1.07
OrderedSet add up to 256 items then delete first 12.296 us/op 11.467 us/op 1.07
Set add up to 256 items then delete last 7.9020 us/op 7.5634 us/op 1.04
OrderedSet add up to 256 items then delete last 11.798 us/op 11.787 us/op 1.00
Set add up to 256 items then delete middle 7.9137 us/op 7.6074 us/op 1.04
OrderedSet add up to 256 items then delete middle 35.020 us/op 35.133 us/op 1.00
pass gossip attestations to forkchoice per slot 2.4969 ms/op 2.5186 ms/op 0.99
forkChoice updateHead vc 100000 bc 64 eq 0 464.74 us/op 382.52 us/op 1.21
forkChoice updateHead vc 600000 bc 64 eq 0 2.7819 ms/op 2.3379 ms/op 1.19
forkChoice updateHead vc 1000000 bc 64 eq 0 4.6501 ms/op 3.8508 ms/op 1.21
forkChoice updateHead vc 600000 bc 320 eq 0 2.7937 ms/op 2.3169 ms/op 1.21
forkChoice updateHead vc 600000 bc 1200 eq 0 2.8122 ms/op 2.5975 ms/op 1.08
forkChoice updateHead vc 600000 bc 7200 eq 0 3.1220 ms/op 2.6176 ms/op 1.19
forkChoice updateHead vc 600000 bc 64 eq 1000 3.4418 ms/op 2.9234 ms/op 1.18
forkChoice updateHead vc 600000 bc 64 eq 10000 3.5664 ms/op 2.9672 ms/op 1.20
forkChoice updateHead vc 600000 bc 64 eq 300000 7.2028 ms/op 6.9016 ms/op 1.04
computeDeltas 1400000 validators 0% inactive 14.454 ms/op 12.477 ms/op 1.16
computeDeltas 1400000 validators 10% inactive 12.810 ms/op 11.673 ms/op 1.10
computeDeltas 1400000 validators 20% inactive 11.936 ms/op 10.706 ms/op 1.11
computeDeltas 1400000 validators 50% inactive 9.3036 ms/op 8.2528 ms/op 1.13
computeDeltas 2100000 validators 0% inactive 24.891 ms/op 18.892 ms/op 1.32
computeDeltas 2100000 validators 10% inactive 19.230 ms/op 17.556 ms/op 1.10
computeDeltas 2100000 validators 20% inactive 17.813 ms/op 16.323 ms/op 1.09
computeDeltas 2100000 validators 50% inactive 13.970 ms/op 9.2507 ms/op 1.51
altair processAttestation - 250000 vs - 7PWei normalcase 1.6813 ms/op 1.8763 ms/op 0.90
altair processAttestation - 250000 vs - 7PWei worstcase 2.5044 ms/op 2.6804 ms/op 0.93
altair processAttestation - setStatus - 1/6 committees join 103.45 us/op 103.71 us/op 1.00
altair processAttestation - setStatus - 1/3 committees join 206.53 us/op 196.92 us/op 1.05
altair processAttestation - setStatus - 1/2 committees join 296.79 us/op 276.57 us/op 1.07
altair processAttestation - setStatus - 2/3 committees join 375.73 us/op 355.21 us/op 1.06
altair processAttestation - setStatus - 4/5 committees join 514.50 us/op 508.59 us/op 1.01
altair processAttestation - setStatus - 100% committees join 610.09 us/op 608.40 us/op 1.00
altair processBlock - 250000 vs - 7PWei normalcase 3.4490 ms/op 4.4641 ms/op 0.77
altair processBlock - 250000 vs - 7PWei normalcase hashState 11.747 ms/op 17.588 ms/op 0.67
altair processBlock - 250000 vs - 7PWei worstcase 19.556 ms/op 20.703 ms/op 0.94
altair processBlock - 250000 vs - 7PWei worstcase hashState 38.461 ms/op 44.487 ms/op 0.86
phase0 processBlock - 250000 vs - 7PWei normalcase 1.2895 ms/op 1.4959 ms/op 0.86
phase0 processBlock - 250000 vs - 7PWei worstcase 17.354 ms/op 17.113 ms/op 1.01
altair processEth1Data - 250000 vs - 7PWei normalcase 300.69 us/op 287.14 us/op 1.05
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 8.3050 us/op 3.2400 us/op 2.56
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 20.443 us/op 19.679 us/op 1.04
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.6590 us/op 5.6140 us/op 1.01
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.5870 us/op 3.5400 us/op 1.01
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 92.211 us/op 90.213 us/op 1.02
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3748 ms/op 1.4026 ms/op 0.98
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.7997 ms/op 1.8689 ms/op 0.96
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8095 ms/op 1.8109 ms/op 1.00
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.6961 ms/op 3.9591 ms/op 0.93
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.0562 ms/op 2.0880 ms/op 0.98
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.9970 ms/op 4.1893 ms/op 0.95
Tree 40 250000 create 316.61 ms/op 334.46 ms/op 0.95
Tree 40 250000 get(125000) 103.44 ns/op 90.989 ns/op 1.14
Tree 40 250000 set(125000) 1.0877 us/op 971.30 ns/op 1.12
Tree 40 250000 toArray() 9.3266 ms/op 14.671 ms/op 0.64
Tree 40 250000 iterate all - toArray() + loop 9.3514 ms/op 16.344 ms/op 0.57
Tree 40 250000 iterate all - get(i) 34.121 ms/op 38.963 ms/op 0.88
Array 250000 create 1.9754 ms/op 2.2938 ms/op 0.86
Array 250000 clone - spread 632.83 us/op 696.38 us/op 0.91
Array 250000 get(125000) 0.29400 ns/op 0.30200 ns/op 0.97
Array 250000 set(125000) 0.29600 ns/op 0.30300 ns/op 0.98
Array 250000 iterate all - loop 56.447 us/op 57.666 us/op 0.98
phase0 afterProcessEpoch - 250000 vs - 7PWei 39.161 ms/op 44.224 ms/op 0.89
Array.fill - length 1000000 2.2661 ms/op 3.3020 ms/op 0.69
Array push - length 1000000 7.3482 ms/op 15.653 ms/op 0.47
Array.get 0.20316 ns/op 0.21966 ns/op 0.92
Uint8Array.get 0.24526 ns/op 0.24214 ns/op 1.01
phase0 beforeProcessEpoch - 250000 vs - 7PWei 11.034 ms/op 22.642 ms/op 0.49
altair processEpoch - mainnet_e81889 237.70 ms/op 350.63 ms/op 0.68
mainnet_e81889 - altair beforeProcessEpoch 13.759 ms/op 30.615 ms/op 0.45
mainnet_e81889 - altair processJustificationAndFinalization 4.6550 us/op 7.5920 us/op 0.61
mainnet_e81889 - altair processInactivityUpdates 3.6343 ms/op 7.8153 ms/op 0.47
mainnet_e81889 - altair processRewardsAndPenalties 16.962 ms/op 24.073 ms/op 0.70
mainnet_e81889 - altair processRegistryUpdates 504.00 ns/op 601.00 ns/op 0.84
mainnet_e81889 - altair processSlashings 128.00 ns/op 137.00 ns/op 0.93
mainnet_e81889 - altair processEth1DataReset 125.00 ns/op 136.00 ns/op 0.92
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.6527 ms/op 7.7362 ms/op 0.21
mainnet_e81889 - altair processSlashingsReset 671.00 ns/op 770.00 ns/op 0.87
mainnet_e81889 - altair processRandaoMixesReset 1000.0 ns/op 1.6910 us/op 0.59
mainnet_e81889 - altair processHistoricalRootsUpdate 129.00 ns/op 136.00 ns/op 0.95
mainnet_e81889 - altair processParticipationFlagUpdates 410.00 ns/op 715.00 ns/op 0.57
mainnet_e81889 - altair processSyncCommitteeUpdates 104.00 ns/op 120.00 ns/op 0.87
mainnet_e81889 - altair afterProcessEpoch 41.607 ms/op 43.925 ms/op 0.95
capella processEpoch - mainnet_e217614 712.23 ms/op 1.0757 s/op 0.66
mainnet_e217614 - capella beforeProcessEpoch 68.371 ms/op 75.064 ms/op 0.91
mainnet_e217614 - capella processJustificationAndFinalization 4.9980 us/op 7.2170 us/op 0.69
mainnet_e217614 - capella processInactivityUpdates 11.696 ms/op 21.118 ms/op 0.55
mainnet_e217614 - capella processRewardsAndPenalties 86.791 ms/op 110.53 ms/op 0.79
mainnet_e217614 - capella processRegistryUpdates 4.4470 us/op 4.6690 us/op 0.95
mainnet_e217614 - capella processSlashings 135.00 ns/op 137.00 ns/op 0.99
mainnet_e217614 - capella processEth1DataReset 126.00 ns/op 136.00 ns/op 0.93
mainnet_e217614 - capella processEffectiveBalanceUpdates 5.6135 ms/op 18.827 ms/op 0.30
mainnet_e217614 - capella processSlashingsReset 665.00 ns/op 714.00 ns/op 0.93
mainnet_e217614 - capella processRandaoMixesReset 1.1720 us/op 1.6850 us/op 0.70
mainnet_e217614 - capella processHistoricalRootsUpdate 126.00 ns/op 137.00 ns/op 0.92
mainnet_e217614 - capella processParticipationFlagUpdates 416.00 ns/op 454.00 ns/op 0.92
mainnet_e217614 - capella afterProcessEpoch 105.98 ms/op 118.10 ms/op 0.90
phase0 processEpoch - mainnet_e58758 242.70 ms/op 368.55 ms/op 0.66
mainnet_e58758 - phase0 beforeProcessEpoch 47.366 ms/op 71.648 ms/op 0.66
mainnet_e58758 - phase0 processJustificationAndFinalization 4.7780 us/op 6.3340 us/op 0.75
mainnet_e58758 - phase0 processRewardsAndPenalties 14.653 ms/op 18.386 ms/op 0.80
mainnet_e58758 - phase0 processRegistryUpdates 2.2320 us/op 2.3730 us/op 0.94
mainnet_e58758 - phase0 processSlashings 238.00 ns/op 135.00 ns/op 1.76
mainnet_e58758 - phase0 processEth1DataReset 130.00 ns/op 135.00 ns/op 0.96
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 900.86 us/op 1.5324 ms/op 0.59
mainnet_e58758 - phase0 processSlashingsReset 784.00 ns/op 961.00 ns/op 0.82
mainnet_e58758 - phase0 processRandaoMixesReset 992.00 ns/op 1.2340 us/op 0.80
mainnet_e58758 - phase0 processHistoricalRootsUpdate 153.00 ns/op 137.00 ns/op 1.12
mainnet_e58758 - phase0 processParticipationRecordUpdates 950.00 ns/op 1.1940 us/op 0.80
mainnet_e58758 - phase0 afterProcessEpoch 32.069 ms/op 34.861 ms/op 0.92
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.0160 ms/op 1.0282 ms/op 0.99
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.5715 ms/op 1.5988 ms/op 0.98
altair processInactivityUpdates - 250000 normalcase 10.318 ms/op 15.606 ms/op 0.66
altair processInactivityUpdates - 250000 worstcase 10.295 ms/op 16.526 ms/op 0.62
phase0 processRegistryUpdates - 250000 normalcase 2.2860 us/op 5.0280 us/op 0.45
phase0 processRegistryUpdates - 250000 badcase_full_deposits 149.87 us/op 162.12 us/op 0.92
phase0 processRegistryUpdates - 250000 worstcase 0.5 60.235 ms/op 72.621 ms/op 0.83
altair processRewardsAndPenalties - 250000 normalcase 13.385 ms/op 17.972 ms/op 0.74
altair processRewardsAndPenalties - 250000 worstcase 13.303 ms/op 18.957 ms/op 0.70
phase0 getAttestationDeltas - 250000 normalcase 5.3427 ms/op 5.8136 ms/op 0.92
phase0 getAttestationDeltas - 250000 worstcase 6.6774 ms/op 5.8941 ms/op 1.13
phase0 processSlashings - 250000 worstcase 60.900 us/op 68.439 us/op 0.89
altair processSyncCommitteeUpdates - 250000 10.050 ms/op 14.394 ms/op 0.70
BeaconState.hashTreeRoot - No change 184.00 ns/op 308.00 ns/op 0.60
BeaconState.hashTreeRoot - 1 full validator 60.792 us/op 120.46 us/op 0.50
BeaconState.hashTreeRoot - 32 full validator 675.08 us/op 1.0203 ms/op 0.66
BeaconState.hashTreeRoot - 512 full validator 6.2387 ms/op 14.622 ms/op 0.43
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 72.463 us/op 146.66 us/op 0.49
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.0944 ms/op 1.6394 ms/op 0.67
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 12.956 ms/op 22.422 ms/op 0.58
BeaconState.hashTreeRoot - 1 balances 58.956 us/op 102.23 us/op 0.58
BeaconState.hashTreeRoot - 32 balances 577.09 us/op 787.16 us/op 0.73
BeaconState.hashTreeRoot - 512 balances 4.7725 ms/op 9.7204 ms/op 0.49
BeaconState.hashTreeRoot - 250000 balances 110.39 ms/op 170.97 ms/op 0.65
aggregationBits - 2048 els - zipIndexesInBitList 19.166 us/op 23.713 us/op 0.81
regular array get 100000 times 23.138 us/op 24.262 us/op 0.95
wrappedArray get 100000 times 23.416 us/op 24.154 us/op 0.97
arrayWithProxy get 100000 times 17.719 ms/op 10.007 ms/op 1.77
ssz.Root.equals 21.639 ns/op 22.523 ns/op 0.96
byteArrayEquals 21.471 ns/op 22.374 ns/op 0.96
Buffer.compare 8.9220 ns/op 9.2680 ns/op 0.96
processSlot - 1 slots 7.9580 us/op 11.140 us/op 0.71
processSlot - 32 slots 1.6818 ms/op 2.9072 ms/op 0.58
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 2.7933 ms/op 7.8511 ms/op 0.36
getCommitteeAssignments - req 1 vs - 250000 vc 1.6770 ms/op 1.7608 ms/op 0.95
getCommitteeAssignments - req 100 vs - 250000 vc 3.3962 ms/op 3.5900 ms/op 0.95
getCommitteeAssignments - req 1000 vs - 250000 vc 3.6592 ms/op 3.9034 ms/op 0.94
findModifiedValidators - 10000 modified validators 636.38 ms/op 1.0501 s/op 0.61
findModifiedValidators - 1000 modified validators 544.55 ms/op 612.70 ms/op 0.89
findModifiedValidators - 100 modified validators 284.11 ms/op 426.01 ms/op 0.67
findModifiedValidators - 10 modified validators 236.86 ms/op 302.17 ms/op 0.78
findModifiedValidators - 1 modified validators 232.73 ms/op 239.10 ms/op 0.97
findModifiedValidators - no difference 155.08 ms/op 192.93 ms/op 0.80
migrate state 1500000 validators, 3400 modified, 2000 new 2.6451 s/op 3.4404 s/op 0.77
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.7300 ns/op 3.9500 ns/op 0.94
state getBlockRootAtSlot - 250000 vs - 7PWei 282.34 ns/op 447.97 ns/op 0.63
computeProposerIndex 100000 validators 1.3518 ms/op 1.3685 ms/op 0.99
getNextSyncCommitteeIndices 1000 validators 2.8526 ms/op 2.9175 ms/op 0.98
getNextSyncCommitteeIndices 10000 validators 26.290 ms/op 25.914 ms/op 1.01
getNextSyncCommitteeIndices 100000 validators 88.226 ms/op 91.897 ms/op 0.96
computeProposers - vc 250000 539.80 us/op 552.28 us/op 0.98
computeEpochShuffling - vc 250000 39.962 ms/op 39.289 ms/op 1.02
getNextSyncCommittee - vc 250000 9.2651 ms/op 9.8430 ms/op 0.94
nodejs block root to RootHex using toHex 103.90 ns/op 100.25 ns/op 1.04
nodejs block root to RootHex using toRootHex 59.259 ns/op 58.355 ns/op 1.02
nodejs fromHex(blob) 734.10 us/op 849.46 us/op 0.86
nodejs fromHexInto(blob) 661.56 us/op 625.56 us/op 1.06
nodejs block root to RootHex using the deprecated toHexString 505.90 ns/op 503.50 ns/op 1.00
nodejs byteArrayEquals 32 bytes (block root) 25.641 ns/op 25.446 ns/op 1.01
nodejs byteArrayEquals 48 bytes (pubkey) 37.083 ns/op 36.447 ns/op 1.02
nodejs byteArrayEquals 96 bytes (signature) 40.305 ns/op 33.115 ns/op 1.22
nodejs byteArrayEquals 1024 bytes 44.497 ns/op 39.383 ns/op 1.13
nodejs byteArrayEquals 131072 bytes (blob) 1.7412 us/op 1.7462 us/op 1.00
browser block root to RootHex using toHex 145.50 ns/op 141.49 ns/op 1.03
browser block root to RootHex using toRootHex 131.10 ns/op 128.38 ns/op 1.02
browser fromHex(blob) 1.5357 ms/op 1.5090 ms/op 1.02
browser fromHexInto(blob) 658.22 us/op 615.61 us/op 1.07
browser block root to RootHex using the deprecated toHexString 353.41 ns/op 506.96 ns/op 0.70
browser byteArrayEquals 32 bytes (block root) 27.789 ns/op 27.803 ns/op 1.00
browser byteArrayEquals 48 bytes (pubkey) 39.149 ns/op 39.350 ns/op 0.99
browser byteArrayEquals 96 bytes (signature) 73.415 ns/op 73.927 ns/op 0.99
browser byteArrayEquals 1024 bytes 748.77 ns/op 741.84 ns/op 1.01
browser byteArrayEquals 131072 bytes (blob) 94.049 us/op 94.184 us/op 1.00

by benchmarkbot/action

@twoeths
twoeths marked this pull request as ready for review May 11, 2026 06:14
@twoeths
twoeths requested a review from a team as a code owner May 11, 2026 06:14
@twoeths

twoeths commented May 11, 2026

Copy link
Copy Markdown
Member Author

please review @lodekeeper

@lodekeeper lodekeeper 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.

LGTM. The dep bumps + CI guard are exactly what's needed to keep the PMT hasher singleton intact across the workspace.

Root-cause coverage: After #9211 bumped @chainsafe/ssz to ^1.4.0 (whose package.json carries an empty PMT constraint), pnpm resolved both 1.2.1 (direct pin) and 1.2.5 (transitive). The 1.2.1 copy got setHasher(hashtree) via applyPreset.ts; ssz's internal merkleize.js digested through the 1.2.5 copy, silently falling back to JS noble. The screenshot in the PR description (mainnet GC regression) is the smoking gun. Bumping every workspace pin to 1.2.5 collapses the resolution graph to a single instance, and the CI guard prevents a regression on future ssz / PMT bumps.

Workspace pins audited: api, beacon-node, cli, light-client, prover, state-transition — all bumped from ^1.2.1 to ^1.2.5. Lockfile dedupes to a single 1.2.5 entry (modulo the exempt legacy 0.6.1 from @ethereumjs/util → ssz@0.11.1).

CI guard correctness: After commit 8307690bbe, the script handles pre-release tags, pnpm peer-dep suffixes, any future major version (no hardcoded MAJOR=1), and explicitly fails on zero matches to catch lockfile-format drift / dependency removal. Wired into .github/workflows/test.yml:55-56 ahead of Lint Code, so a split fails CI early.

Exemption justification: EXEMPT_VERSIONS=(0.6.1) is well-documented in the script — it comes from @ethereumjs/util → ssz@0.11.1 (pre-setHasher), so it can't share a singleton with the 1.x line anyway and would never have benefited from the hashtree configuration.

@gemini-code-assist — all three of your concerns (CI wiring, regex brittleness, zero-match false positive) are addressed by the hardening commit; replies posted in-thread.

@nflaig
nflaig merged commit 899087b into unstable May 11, 2026
20 checks passed
@nflaig
nflaig deleted the te/pmt_1.2.5 branch May 11, 2026 07:12
@twoeths

twoeths commented May 11, 2026

Copy link
Copy Markdown
Member Author

confirmed the fix after merge, here's unstable mainnet super node

Screenshot 2026-05-11 at 16 03 46

@lodekeeper

Copy link
Copy Markdown
Contributor

Thanks for the confirmation @twoeths! Good to see the dedup lint catching the issue and the fix holding up on mainnet. 🌟

@wemeetagain

Copy link
Copy Markdown
Member

🎉 This PR is included in v1.43.0 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants