Skip to content

feat(blst): replace blst and pubkeys with lodestar-z#8900

Open
spiral-ladder wants to merge 15 commits intounstablefrom
bing/blst-z
Open

feat(blst): replace blst and pubkeys with lodestar-z#8900
spiral-ladder wants to merge 15 commits intounstablefrom
bing/blst-z

Conversation

@spiral-ladder
Copy link
Contributor

Motivation

Replacing usages of blst with a lodestar-z bindings to blst-z for usage in the beacon node.

@spiral-ladder spiral-ladder changed the title Bing/blst z wip: replace blst with blst-z where possible Feb 13, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @spiral-ladder, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request updates the beacon node's cryptographic backend by replacing the existing @chainsafe/blst library with new lodestar-z bindings to @chainsafe/blst-z. This change is intended to leverage an alternative or optimized implementation for BLS signature operations, which are critical for the aggregation of attestations and sync committee messages within the Ethereum consensus layer. The update involves modifying dependencies, adjusting function calls, and updating build configurations to support the new library.

Highlights

  • BLS Library Migration: Replaced usages of the @chainsafe/blst library with lodestar-z bindings to @chainsafe/blst-z across the beacon node for BLS cryptographic operations.
  • Dependency Updates: Added @chainsafe/blst-z as a local file dependency and @chainsafe/zapi to the project's yarn.lock file, reflecting the new BLS implementation.
  • Signature Aggregation Parameter Change: Modified calls to aggregateSignatures in several operational pools to include a false parameter, likely related to specific validation or optimization settings within the new BLS library.
  • Partial BLS Function Import: Maintained the import of aggregateVerify from the original @chainsafe/blst library in specific test files, while other BLS functions were migrated to @chainsafe/blst-z.
  • Build System Integration: Updated the .gitignore file to exclude zig-deps/, accommodating the new Zig-based dependencies.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • .gitignore
    • Added zig-deps/ to the ignore list.
  • packages/beacon-node/package.json
    • Introduced @chainsafe/blst-z as a local file dependency.
  • packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts
    • Updated BLS import to @chainsafe/blst-z.
    • Modified aggregateSignatures calls to include a false parameter.
  • packages/beacon-node/src/chain/opPools/attestationPool.ts
    • Updated BLS import to @chainsafe/blst-z.
    • Modified aggregateSignatures calls to include a false parameter.
  • packages/beacon-node/src/chain/opPools/syncCommitteeMessagePool.ts
    • Updated BLS import to @chainsafe/blst-z.
    • Modified aggregateSignatureInto call to include a false parameter.
  • packages/beacon-node/src/chain/opPools/syncContributionAndProofPool.ts
    • Updated BLS import to @chainsafe/blst-z.
    • Modified aggregate function call to include a false parameter.
  • packages/beacon-node/src/chain/opPools/utils.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/e2e/api/impl/lightclient/endpoint.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/e2e/chain/bls/multithread.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/mocks/mockedBls.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/perf/bls/bls.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/spec/bls/bls.ts
    • Updated BLS imports, specifically re-importing aggregateVerify from @chainsafe/blst.
  • packages/beacon-node/test/spec/general/bls.ts
    • Updated BLS imports, specifically re-importing aggregateVerify from @chainsafe/blst.
  • packages/beacon-node/test/unit-minimal/chain/genesis/genesis.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/unit-minimal/chain/opPools/aggregatedAttestationPool.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/unit/chain/bls/bls.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/unit/chain/opPools/syncCommittee.test.ts
    • Updated BLS import to @chainsafe/bls-z.
  • packages/beacon-node/test/unit/chain/opPools/syncCommitteeContribution.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/unit/chain/validation/blsToExecutionChange.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/unit/chain/validation/voluntaryExit.test.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/utils/cache.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/utils/node/validator.ts
    • Updated BLS import to @chainsafe/blst-z.
  • packages/beacon-node/test/utils/state.ts
    • Updated BLS import to @chainsafe/blst-z.
  • yarn.lock
    • Added new entries for @chainsafe/blst-z and @chainsafe/zapi dependencies.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@spiral-ladder spiral-ladder changed the title wip: replace blst with blst-z where possible feat(blst): replace blst with blst-z where possible Feb 13, 2026
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 replaces the @chainsafe/blst dependency with @chainsafe/blst-z in most of the codebase. The changes are mostly import replacements, but also include an update to the aggregateSignatures function calls, adding a false argument to likely disable signature verification for performance. In two spec test files, aggregateVerify is still imported from the old @chainsafe/blst library, which seems intentional. My review includes suggestions to add comments explaining this mixed dependency for better maintainability.

@spiral-ladder spiral-ladder force-pushed the bing/blst-z branch 2 times, most recently from 5604001 to 59688b7 Compare February 13, 2026 14:57
@wemeetagain
Copy link
Member

You should be able to either pnpm link or use a url version of the dependency instead of vendoring the whole lodestar-z codebase.

@spiral-ladder spiral-ladder force-pushed the bing/blst-z branch 4 times, most recently from ec6bb4d to 6c95d69 Compare February 14, 2026 02:46
@wemeetagain wemeetagain changed the title feat(blst): replace blst with blst-z where possible feat(blst): replace blst with lodestarz/blst Feb 16, 2026
@wemeetagain wemeetagain changed the title feat(blst): replace blst with lodestarz/blst feat(blst): replace blst with lodestar-z/blst Feb 17, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 17, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: d723fb3 Previous: afd4462 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 575.78 us/op 1.2370 ms/op 0.47
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 39.653 us/op 40.746 us/op 0.97
BLS verify - blst 971.40 us/op 1.0909 ms/op 0.89
BLS verifyMultipleSignatures 3 - blst 1.8223 ms/op 1.2888 ms/op 1.41
BLS verifyMultipleSignatures 8 - blst 3.9036 ms/op 1.9184 ms/op 2.03
BLS verifyMultipleSignatures 32 - blst 14.063 ms/op 6.0069 ms/op 2.34
BLS verifyMultipleSignatures 64 - blst 27.650 ms/op 11.009 ms/op 2.51
BLS verifyMultipleSignatures 128 - blst 55.064 ms/op 19.818 ms/op 2.78
BLS deserializing 10000 signatures 686.00 ms/op 713.75 ms/op 0.96
BLS deserializing 100000 signatures 6.8673 s/op 7.2284 s/op 0.95
BLS verifyMultipleSignatures - same message - 3 - blst 1.0299 ms/op 972.46 us/op 1.06
BLS verifyMultipleSignatures - same message - 8 - blst 1.1575 ms/op 1.1251 ms/op 1.03
BLS verifyMultipleSignatures - same message - 32 - blst 1.7924 ms/op 1.7889 ms/op 1.00
BLS verifyMultipleSignatures - same message - 64 - blst 2.6488 ms/op 2.7497 ms/op 0.96
BLS verifyMultipleSignatures - same message - 128 - blst 4.3777 ms/op 4.5246 ms/op 0.97
BLS aggregatePubkeys 32 - blst 19.045 us/op 19.954 us/op 0.95
BLS aggregatePubkeys 128 - blst 69.115 us/op 71.515 us/op 0.97
getSlashingsAndExits - default max 71.754 us/op 80.336 us/op 0.89
getSlashingsAndExits - 2k 339.21 us/op 386.82 us/op 0.88
isKnown best case - 1 super set check 205.00 ns/op 225.00 ns/op 0.91
isKnown normal case - 2 super set checks 206.00 ns/op 204.00 ns/op 1.01
isKnown worse case - 16 super set checks 264.00 ns/op 202.00 ns/op 1.31
validate api signedAggregateAndProof - struct 1.9634 ms/op 1.4172 ms/op 1.39
validate gossip signedAggregateAndProof - struct 1.9197 ms/op 1.6354 ms/op 1.17
batch validate gossip attestation - vc 640000 - chunk 32 116.20 us/op 122.27 us/op 0.95
batch validate gossip attestation - vc 640000 - chunk 64 102.86 us/op 109.87 us/op 0.94
batch validate gossip attestation - vc 640000 - chunk 128 93.560 us/op 110.54 us/op 0.85
batch validate gossip attestation - vc 640000 - chunk 256 91.422 us/op 100.76 us/op 0.91
bytes32 toHexString 338.00 ns/op 435.00 ns/op 0.78
bytes32 Buffer.toString(hex) 315.00 ns/op 258.00 ns/op 1.22
bytes32 Buffer.toString(hex) from Uint8Array 353.00 ns/op 334.00 ns/op 1.06
bytes32 Buffer.toString(hex) + 0x 256.00 ns/op 245.00 ns/op 1.04
Return object 10000 times 0.23360 ns/op 0.23650 ns/op 0.99
Throw Error 10000 times 4.2374 us/op 4.4499 us/op 0.95
toHex 135.57 ns/op 146.72 ns/op 0.92
Buffer.from 125.68 ns/op 125.17 ns/op 1.00
shared Buffer 75.724 ns/op 80.263 ns/op 0.94
fastMsgIdFn sha256 / 200 bytes 1.8440 us/op 2.0500 us/op 0.90
fastMsgIdFn h32 xxhash / 200 bytes 246.00 ns/op 197.00 ns/op 1.25
fastMsgIdFn h64 xxhash / 200 bytes 277.00 ns/op 268.00 ns/op 1.03
fastMsgIdFn sha256 / 1000 bytes 5.9010 us/op 6.0030 us/op 0.98
fastMsgIdFn h32 xxhash / 1000 bytes 426.00 ns/op 313.00 ns/op 1.36
fastMsgIdFn h64 xxhash / 1000 bytes 370.00 ns/op 349.00 ns/op 1.06
fastMsgIdFn sha256 / 10000 bytes 52.502 us/op 52.847 us/op 0.99
fastMsgIdFn h32 xxhash / 10000 bytes 1.3940 us/op 1.4020 us/op 0.99
fastMsgIdFn h64 xxhash / 10000 bytes 900.00 ns/op 1.0300 us/op 0.87
send data - 1000 256B messages 4.3849 ms/op 5.2109 ms/op 0.84
send data - 1000 512B messages 4.4433 ms/op 4.9269 ms/op 0.90
send data - 1000 1024B messages 4.8813 ms/op 6.1836 ms/op 0.79
send data - 1000 1200B messages 5.8674 ms/op 5.6052 ms/op 1.05
send data - 1000 2048B messages 6.3407 ms/op 5.9128 ms/op 1.07
send data - 1000 4096B messages 7.2494 ms/op 7.9565 ms/op 0.91
send data - 1000 16384B messages 36.172 ms/op 43.420 ms/op 0.83
send data - 1000 65536B messages 109.32 ms/op 111.12 ms/op 0.98
enrSubnets - fastDeserialize 64 bits 922.00 ns/op 966.00 ns/op 0.95
enrSubnets - ssz BitVector 64 bits 500.00 ns/op 368.00 ns/op 1.36
enrSubnets - fastDeserialize 4 bits 184.00 ns/op 171.00 ns/op 1.08
enrSubnets - ssz BitVector 4 bits 429.00 ns/op 383.00 ns/op 1.12
prioritizePeers score -10:0 att 32-0.1 sync 2-0 288.47 us/op 245.01 us/op 1.18
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 291.92 us/op 283.83 us/op 1.03
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 478.37 us/op 395.04 us/op 1.21
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 722.95 us/op 737.71 us/op 0.98
prioritizePeers score 0:0 att 64-1 sync 4-1 866.07 us/op 888.80 us/op 0.97
array of 16000 items push then shift 1.6779 us/op 1.6573 us/op 1.01
LinkedList of 16000 items push then shift 8.3290 ns/op 8.1030 ns/op 1.03
array of 16000 items push then pop 76.403 ns/op 86.953 ns/op 0.88
LinkedList of 16000 items push then pop 7.7090 ns/op 7.5040 ns/op 1.03
array of 24000 items push then shift 2.4716 us/op 2.4673 us/op 1.00
LinkedList of 24000 items push then shift 7.7140 ns/op 8.0500 ns/op 0.96
array of 24000 items push then pop 110.33 ns/op 116.22 ns/op 0.95
LinkedList of 24000 items push then pop 7.3130 ns/op 7.7300 ns/op 0.95
intersect bitArray bitLen 8 5.8240 ns/op 5.8710 ns/op 0.99
intersect array and set length 8 33.983 ns/op 34.811 ns/op 0.98
intersect bitArray bitLen 128 29.198 ns/op 29.144 ns/op 1.00
intersect array and set length 128 559.81 ns/op 557.40 ns/op 1.00
bitArray.getTrueBitIndexes() bitLen 128 1.0380 us/op 1.0660 us/op 0.97
bitArray.getTrueBitIndexes() bitLen 248 1.8250 us/op 1.8510 us/op 0.99
bitArray.getTrueBitIndexes() bitLen 512 3.7200 us/op 4.3720 us/op 0.85
Full columns - reconstruct all 6 blobs 378.42 us/op 237.71 us/op 1.59
Full columns - reconstruct half of the blobs out of 6 130.44 us/op 119.22 us/op 1.09
Full columns - reconstruct single blob out of 6 31.485 us/op 31.654 us/op 0.99
Half columns - reconstruct all 6 blobs 270.69 ms/op 286.04 ms/op 0.95
Half columns - reconstruct half of the blobs out of 6 135.26 ms/op 140.35 ms/op 0.96
Half columns - reconstruct single blob out of 6 50.046 ms/op 52.428 ms/op 0.95
Full columns - reconstruct all 10 blobs 494.36 us/op 354.93 us/op 1.39
Full columns - reconstruct half of the blobs out of 10 176.26 us/op 156.27 us/op 1.13
Full columns - reconstruct single blob out of 10 39.652 us/op 32.195 us/op 1.23
Half columns - reconstruct all 10 blobs 454.70 ms/op 456.63 ms/op 1.00
Half columns - reconstruct half of the blobs out of 10 225.77 ms/op 227.80 ms/op 0.99
Half columns - reconstruct single blob out of 10 49.138 ms/op 51.764 ms/op 0.95
Full columns - reconstruct all 20 blobs 823.45 us/op 690.45 us/op 1.19
Full columns - reconstruct half of the blobs out of 20 307.09 us/op 278.24 us/op 1.10
Full columns - reconstruct single blob out of 20 36.650 us/op 31.274 us/op 1.17
Half columns - reconstruct all 20 blobs 894.63 ms/op 903.63 ms/op 0.99
Half columns - reconstruct half of the blobs out of 20 465.41 ms/op 456.79 ms/op 1.02
Half columns - reconstruct single blob out of 20 50.986 ms/op 50.882 ms/op 1.00
Set add up to 64 items then delete first 2.0655 us/op 2.0700 us/op 1.00
OrderedSet add up to 64 items then delete first 3.0738 us/op 3.1361 us/op 0.98
Set add up to 64 items then delete last 2.3384 us/op 2.3384 us/op 1.00
OrderedSet add up to 64 items then delete last 3.5906 us/op 3.5407 us/op 1.01
Set add up to 64 items then delete middle 2.3704 us/op 2.3909 us/op 0.99
OrderedSet add up to 64 items then delete middle 5.1363 us/op 5.0982 us/op 1.01
Set add up to 128 items then delete first 4.8685 us/op 4.8109 us/op 1.01
OrderedSet add up to 128 items then delete first 6.8512 us/op 6.9530 us/op 0.99
Set add up to 128 items then delete last 4.7500 us/op 4.8298 us/op 0.98
OrderedSet add up to 128 items then delete last 7.0265 us/op 7.0880 us/op 0.99
Set add up to 128 items then delete middle 4.6396 us/op 4.7277 us/op 0.98
OrderedSet add up to 128 items then delete middle 13.641 us/op 13.740 us/op 0.99
Set add up to 256 items then delete first 10.148 us/op 10.099 us/op 1.00
OrderedSet add up to 256 items then delete first 14.934 us/op 15.417 us/op 0.97
Set add up to 256 items then delete last 9.7443 us/op 9.7773 us/op 1.00
OrderedSet add up to 256 items then delete last 14.952 us/op 15.045 us/op 0.99
Set add up to 256 items then delete middle 9.4684 us/op 9.8131 us/op 0.96
OrderedSet add up to 256 items then delete middle 41.484 us/op 42.651 us/op 0.97
pass gossip attestations to forkchoice per slot 2.6452 ms/op 2.5316 ms/op 1.04
forkChoice updateHead vc 100000 bc 64 eq 0 513.53 us/op 509.94 us/op 1.01
forkChoice updateHead vc 600000 bc 64 eq 0 3.0588 ms/op 3.0398 ms/op 1.01
forkChoice updateHead vc 1000000 bc 64 eq 0 5.1505 ms/op 5.0379 ms/op 1.02
forkChoice updateHead vc 600000 bc 320 eq 0 3.1414 ms/op 3.0504 ms/op 1.03
forkChoice updateHead vc 600000 bc 1200 eq 0 3.1726 ms/op 3.0996 ms/op 1.02
forkChoice updateHead vc 600000 bc 7200 eq 0 3.7341 ms/op 3.5642 ms/op 1.05
forkChoice updateHead vc 600000 bc 64 eq 1000 3.4819 ms/op 3.5034 ms/op 0.99
forkChoice updateHead vc 600000 bc 64 eq 10000 3.6396 ms/op 3.6664 ms/op 0.99
forkChoice updateHead vc 600000 bc 64 eq 300000 9.4579 ms/op 9.8214 ms/op 0.96
computeDeltas 1400000 validators 0% inactive 14.924 ms/op 14.936 ms/op 1.00
computeDeltas 1400000 validators 10% inactive 15.362 ms/op 13.992 ms/op 1.10
computeDeltas 1400000 validators 20% inactive 13.033 ms/op 12.997 ms/op 1.00
computeDeltas 1400000 validators 50% inactive 10.203 ms/op 10.198 ms/op 1.00
computeDeltas 2100000 validators 0% inactive 22.319 ms/op 22.590 ms/op 0.99
computeDeltas 2100000 validators 10% inactive 20.860 ms/op 20.998 ms/op 0.99
computeDeltas 2100000 validators 20% inactive 19.488 ms/op 19.587 ms/op 0.99
computeDeltas 2100000 validators 50% inactive 15.395 ms/op 15.274 ms/op 1.01
altair processAttestation - 250000 vs - 7PWei normalcase 2.2438 ms/op 2.1761 ms/op 1.03
altair processAttestation - 250000 vs - 7PWei worstcase 4.6401 ms/op 3.0886 ms/op 1.50
altair processAttestation - setStatus - 1/6 committees join 123.78 us/op 119.53 us/op 1.04
altair processAttestation - setStatus - 1/3 committees join 244.12 us/op 235.85 us/op 1.04
altair processAttestation - setStatus - 1/2 committees join 336.27 us/op 329.27 us/op 1.02
altair processAttestation - setStatus - 2/3 committees join 441.26 us/op 416.10 us/op 1.06
altair processAttestation - setStatus - 4/5 committees join 588.89 us/op 587.28 us/op 1.00
altair processAttestation - setStatus - 100% committees join 697.65 us/op 690.58 us/op 1.01
altair processBlock - 250000 vs - 7PWei normalcase 4.3268 ms/op 4.4032 ms/op 0.98
altair processBlock - 250000 vs - 7PWei normalcase hashState 18.212 ms/op 17.137 ms/op 1.06
altair processBlock - 250000 vs - 7PWei worstcase 27.196 ms/op 31.505 ms/op 0.86
altair processBlock - 250000 vs - 7PWei worstcase hashState 66.033 ms/op 60.864 ms/op 1.08
phase0 processBlock - 250000 vs - 7PWei normalcase 1.6855 ms/op 1.9204 ms/op 0.88
phase0 processBlock - 250000 vs - 7PWei worstcase 22.250 ms/op 25.278 ms/op 0.88
altair processEth1Data - 250000 vs - 7PWei normalcase 387.02 us/op 370.56 us/op 1.04
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 6.7040 us/op 9.4980 us/op 0.71
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 37.880 us/op 44.282 us/op 0.86
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 12.211 us/op 12.560 us/op 0.97
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 7.3250 us/op 8.2600 us/op 0.89
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 172.67 us/op 251.71 us/op 0.69
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 2.6444 ms/op 1.9333 ms/op 1.37
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.7609 ms/op 2.4239 ms/op 1.14
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.3668 ms/op 2.6770 ms/op 0.88
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.8974 ms/op 5.2221 ms/op 0.94
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.8198 ms/op 2.8283 ms/op 1.00
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 5.9684 ms/op 4.8750 ms/op 1.22
Tree 40 250000 create 407.50 ms/op 416.14 ms/op 0.98
Tree 40 250000 get(125000) 131.10 ns/op 127.53 ns/op 1.03
Tree 40 250000 set(125000) 1.3224 us/op 1.2300 us/op 1.08
Tree 40 250000 toArray() 17.948 ms/op 14.964 ms/op 1.20
Tree 40 250000 iterate all - toArray() + loop 17.717 ms/op 16.151 ms/op 1.10
Tree 40 250000 iterate all - get(i) 49.649 ms/op 47.496 ms/op 1.05
Array 250000 create 3.0377 ms/op 2.5148 ms/op 1.21
Array 250000 clone - spread 899.86 us/op 818.06 us/op 1.10
Array 250000 get(125000) 0.41300 ns/op 0.45400 ns/op 0.91
Array 250000 set(125000) 0.42300 ns/op 0.39400 ns/op 1.07
Array 250000 iterate all - loop 65.724 us/op 62.301 us/op 1.05
phase0 afterProcessEpoch - 250000 vs - 7PWei 44.932 ms/op 43.588 ms/op 1.03
Array.fill - length 1000000 3.5627 ms/op 3.4312 ms/op 1.04
Array push - length 1000000 12.743 ms/op 10.647 ms/op 1.20
Array.get 0.23190 ns/op 0.22302 ns/op 1.04
Uint8Array.get 0.24013 ns/op 0.22300 ns/op 1.08
phase0 beforeProcessEpoch - 250000 vs - 7PWei 19.031 ms/op 14.986 ms/op 1.27
altair processEpoch - mainnet_e81889 462.07 ms/op 306.82 ms/op 1.51
mainnet_e81889 - altair beforeProcessEpoch 27.027 ms/op 23.511 ms/op 1.15
mainnet_e81889 - altair processJustificationAndFinalization 9.4230 us/op 6.8210 us/op 1.38
mainnet_e81889 - altair processInactivityUpdates 7.2994 ms/op 3.9347 ms/op 1.86
mainnet_e81889 - altair processRewardsAndPenalties 24.875 ms/op 19.606 ms/op 1.27
mainnet_e81889 - altair processRegistryUpdates 1.0450 us/op 732.00 ns/op 1.43
mainnet_e81889 - altair processSlashings 319.00 ns/op 175.00 ns/op 1.82
mainnet_e81889 - altair processEth1DataReset 282.00 ns/op 167.00 ns/op 1.69
mainnet_e81889 - altair processEffectiveBalanceUpdates 8.2048 ms/op 4.8718 ms/op 1.68
mainnet_e81889 - altair processSlashingsReset 1.4550 us/op 892.00 ns/op 1.63
mainnet_e81889 - altair processRandaoMixesReset 2.1840 us/op 1.4230 us/op 1.53
mainnet_e81889 - altair processHistoricalRootsUpdate 258.00 ns/op 169.00 ns/op 1.53
mainnet_e81889 - altair processParticipationFlagUpdates 793.00 ns/op 577.00 ns/op 1.37
mainnet_e81889 - altair processSyncCommitteeUpdates 203.00 ns/op 132.00 ns/op 1.54
mainnet_e81889 - altair afterProcessEpoch 48.084 ms/op 46.965 ms/op 1.02
capella processEpoch - mainnet_e217614 999.00 ms/op 888.69 ms/op 1.12
mainnet_e217614 - capella beforeProcessEpoch 83.813 ms/op 67.856 ms/op 1.24
mainnet_e217614 - capella processJustificationAndFinalization 9.0810 us/op 5.9670 us/op 1.52
mainnet_e217614 - capella processInactivityUpdates 19.793 ms/op 18.819 ms/op 1.05
mainnet_e217614 - capella processRewardsAndPenalties 121.36 ms/op 115.20 ms/op 1.05
mainnet_e217614 - capella processRegistryUpdates 6.1430 us/op 5.9620 us/op 1.03
mainnet_e217614 - capella processSlashings 184.00 ns/op 188.00 ns/op 0.98
mainnet_e217614 - capella processEth1DataReset 172.00 ns/op 205.00 ns/op 0.84
mainnet_e217614 - capella processEffectiveBalanceUpdates 17.253 ms/op 14.068 ms/op 1.23
mainnet_e217614 - capella processSlashingsReset 910.00 ns/op 949.00 ns/op 0.96
mainnet_e217614 - capella processRandaoMixesReset 1.3570 us/op 1.2310 us/op 1.10
mainnet_e217614 - capella processHistoricalRootsUpdate 197.00 ns/op 207.00 ns/op 0.95
mainnet_e217614 - capella processParticipationFlagUpdates 637.00 ns/op 577.00 ns/op 1.10
mainnet_e217614 - capella afterProcessEpoch 122.51 ms/op 118.07 ms/op 1.04
phase0 processEpoch - mainnet_e58758 381.40 ms/op 257.55 ms/op 1.48
mainnet_e58758 - phase0 beforeProcessEpoch 96.643 ms/op 53.983 ms/op 1.79
mainnet_e58758 - phase0 processJustificationAndFinalization 8.9380 us/op 5.7640 us/op 1.55
mainnet_e58758 - phase0 processRewardsAndPenalties 26.708 ms/op 21.563 ms/op 1.24
mainnet_e58758 - phase0 processRegistryUpdates 4.2870 us/op 3.1190 us/op 1.37
mainnet_e58758 - phase0 processSlashings 238.00 ns/op 222.00 ns/op 1.07
mainnet_e58758 - phase0 processEth1DataReset 196.00 ns/op 183.00 ns/op 1.07
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.0417 ms/op 1.2490 ms/op 0.83
mainnet_e58758 - phase0 processSlashingsReset 1.2450 us/op 1.0240 us/op 1.22
mainnet_e58758 - phase0 processRandaoMixesReset 1.6220 us/op 1.1810 us/op 1.37
mainnet_e58758 - phase0 processHistoricalRootsUpdate 216.00 ns/op 188.00 ns/op 1.15
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.0950 us/op 1.0120 us/op 1.08
mainnet_e58758 - phase0 afterProcessEpoch 37.426 ms/op 38.243 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.3241 ms/op 2.0251 ms/op 0.65
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 4.4838 ms/op 3.3585 ms/op 1.34
altair processInactivityUpdates - 250000 normalcase 15.850 ms/op 15.608 ms/op 1.02
altair processInactivityUpdates - 250000 worstcase 14.470 ms/op 14.190 ms/op 1.02
phase0 processRegistryUpdates - 250000 normalcase 7.5640 us/op 6.3910 us/op 1.18
phase0 processRegistryUpdates - 250000 badcase_full_deposits 232.66 us/op 318.59 us/op 0.73
phase0 processRegistryUpdates - 250000 worstcase 0.5 67.262 ms/op 96.767 ms/op 0.70
altair processRewardsAndPenalties - 250000 normalcase 20.573 ms/op 22.601 ms/op 0.91
altair processRewardsAndPenalties - 250000 worstcase 23.162 ms/op 21.176 ms/op 1.09
phase0 getAttestationDeltas - 250000 normalcase 6.9482 ms/op 7.1808 ms/op 0.97
phase0 getAttestationDeltas - 250000 worstcase 6.8237 ms/op 7.3698 ms/op 0.93
phase0 processSlashings - 250000 worstcase 120.57 us/op 123.70 us/op 0.97
altair processSyncCommitteeUpdates - 250000 10.972 ms/op 12.112 ms/op 0.91
BeaconState.hashTreeRoot - No change 307.00 ns/op 229.00 ns/op 1.34
BeaconState.hashTreeRoot - 1 full validator 90.702 us/op 98.605 us/op 0.92
BeaconState.hashTreeRoot - 32 full validator 1.0039 ms/op 1.0828 ms/op 0.93
BeaconState.hashTreeRoot - 512 full validator 9.6893 ms/op 9.0606 ms/op 1.07
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 119.60 us/op 108.82 us/op 1.10
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.7522 ms/op 2.5802 ms/op 0.68
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 23.917 ms/op 19.776 ms/op 1.21
BeaconState.hashTreeRoot - 1 balances 95.575 us/op 105.75 us/op 0.90
BeaconState.hashTreeRoot - 32 balances 900.75 us/op 1.2914 ms/op 0.70
BeaconState.hashTreeRoot - 512 balances 7.3575 ms/op 7.0853 ms/op 1.04
BeaconState.hashTreeRoot - 250000 balances 156.90 ms/op 202.20 ms/op 0.78
aggregationBits - 2048 els - zipIndexesInBitList 20.909 us/op 23.926 us/op 0.87
regular array get 100000 times 25.584 us/op 25.997 us/op 0.98
wrappedArray get 100000 times 25.578 us/op 26.055 us/op 0.98
arrayWithProxy get 100000 times 15.053 ms/op 21.154 ms/op 0.71
ssz.Root.equals 24.950 ns/op 25.088 ns/op 0.99
byteArrayEquals 24.573 ns/op 24.685 ns/op 1.00
Buffer.compare 10.512 ns/op 10.379 ns/op 1.01
processSlot - 1 slots 12.264 us/op 10.426 us/op 1.18
processSlot - 32 slots 2.8719 ms/op 2.4462 ms/op 1.17
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 3.6840 ms/op 6.1048 ms/op 0.60
getCommitteeAssignments - req 1 vs - 250000 vc 1.8935 ms/op 2.0038 ms/op 0.94
getCommitteeAssignments - req 100 vs - 250000 vc 3.6973 ms/op 3.9545 ms/op 0.93
getCommitteeAssignments - req 1000 vs - 250000 vc 3.9572 ms/op 4.2553 ms/op 0.93
findModifiedValidators - 10000 modified validators 510.04 ms/op 667.17 ms/op 0.76
findModifiedValidators - 1000 modified validators 337.88 ms/op 453.67 ms/op 0.74
findModifiedValidators - 100 modified validators 329.65 ms/op 324.56 ms/op 1.02
findModifiedValidators - 10 modified validators 260.59 ms/op 250.54 ms/op 1.04
findModifiedValidators - 1 modified validators 165.83 ms/op 160.03 ms/op 1.04
findModifiedValidators - no difference 165.79 ms/op 154.38 ms/op 1.07
migrate state 1500000 validators, 3400 modified, 2000 new 1.1748 s/op 1.1334 s/op 1.04
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 5.3900 ns/op 4.1600 ns/op 1.30
state getBlockRootAtSlot - 250000 vs - 7PWei 598.41 ns/op 432.54 ns/op 1.38
computeProposerIndex 100000 validators 1.5017 ms/op 1.6468 ms/op 0.91
getNextSyncCommitteeIndices 1000 validators 110.31 ms/op 122.63 ms/op 0.90
getNextSyncCommitteeIndices 10000 validators 110.09 ms/op 123.01 ms/op 0.89
getNextSyncCommitteeIndices 100000 validators 111.29 ms/op 124.48 ms/op 0.89
computeProposers - vc 250000 624.10 us/op 639.14 us/op 0.98
computeEpochShuffling - vc 250000 41.579 ms/op 43.285 ms/op 0.96
getNextSyncCommittee - vc 250000 10.186 ms/op 11.382 ms/op 0.89
nodejs block root to RootHex using toHex 132.15 ns/op 142.27 ns/op 0.93
nodejs block root to RootHex using toRootHex 94.671 ns/op 84.983 ns/op 1.11
nodejs fromHex(blob) 377.64 us/op 327.43 us/op 1.15
nodejs fromHexInto(blob) 711.90 us/op 735.58 us/op 0.97
nodejs block root to RootHex using the deprecated toHexString 552.99 ns/op 595.48 ns/op 0.93
nodejs byteArrayEquals 32 bytes (block root) 28.865 ns/op 29.199 ns/op 0.99
nodejs byteArrayEquals 48 bytes (pubkey) 41.322 ns/op 41.694 ns/op 0.99
nodejs byteArrayEquals 96 bytes (signature) 40.743 ns/op 43.871 ns/op 0.93
nodejs byteArrayEquals 1024 bytes 45.639 ns/op 48.862 ns/op 0.93
nodejs byteArrayEquals 131072 bytes (blob) 1.8959 us/op 1.9998 us/op 0.95
browser block root to RootHex using toHex 164.52 ns/op 297.77 ns/op 0.55
browser block root to RootHex using toRootHex 156.47 ns/op 162.26 ns/op 0.96
browser fromHex(blob) 1.0751 ms/op 1.2147 ms/op 0.89
browser fromHexInto(blob) 709.85 us/op 760.81 us/op 0.93
browser block root to RootHex using the deprecated toHexString 384.80 ns/op 862.04 ns/op 0.45
browser byteArrayEquals 32 bytes (block root) 31.367 ns/op 32.629 ns/op 0.96
browser byteArrayEquals 48 bytes (pubkey) 43.906 ns/op 45.360 ns/op 0.97
browser byteArrayEquals 96 bytes (signature) 86.327 ns/op 88.492 ns/op 0.98
browser byteArrayEquals 1024 bytes 806.03 ns/op 828.23 ns/op 0.97
browser byteArrayEquals 131072 bytes (blob) 102.34 us/op 105.06 us/op 0.97

by benchmarkbot/action

nflaig pushed a commit that referenced this pull request Feb 17, 2026
## Description

During finalized sync, blocks are imported in a tight loop without
yielding to the event loop. When BLS verification is async (not awaiting
the execution engine's `newPayload`), this prevents the checkpoint state
cache's `processState()` cleanup from running, causing unbounded state
accumulation and OOM on memory-constrained hosts.

This adds `nextEventLoop()` after each `importBlock` to allow pending
async cleanup (state serialization, cache eviction) to execute between
block imports.

### Root Cause

In `processBlocks()`, the import loop previously had a natural yield
point when `importBlock` awaited the execution engine. With async BLS
via `@chainsafe/blst` (PR #8900), `importBlock` no longer blocks on the
EL call, so blocks blast through without yielding. The checkpoint state
cache's `processState()` — which runs as fire-and-forget async — never
gets a chance to serialize and evict old states, causing memory to climb
until OOM.

### Fix

```typescript
for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
  await importBlock.call(this, fullyVerifiedBlock, opts);
  await nextEventLoop(); // Allow processState() cleanup to run
}
```

`nextEventLoop()` is an existing utility (`sleep(0)`) that yields to the
event loop's timers phase, giving pending microtasks and macrotasks a
chance to execute.

### Testing

Deployed as commit 983d923 on feat3 infrastructure nodes — resolved OOM
crash loops on nodes with sufficient memory. Memory-constrained hosts
(~16GB) may need additional tuning (reduced `maxBlockStates`, queue
depth limits).

Co-authored-by: Cayman <caymannava@gmail.com>

---
> [!NOTE]
> This PR was authored with AI assistance (Claude). All code has been
reviewed and validated.

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
Co-authored-by: Cayman <caymannava@gmail.com>
@wemeetagain wemeetagain marked this pull request as ready for review February 18, 2026 16:43
@wemeetagain wemeetagain requested a review from a team as a code owner February 18, 2026 16:43
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9152e822a4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 38 to 39
version: ${{ env.ZIG_VERSION }}
cache-key: ${{ env.ZIG_VERSION }}

Choose a reason for hiding this comment

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

P1 Badge Define ZIG_VERSION before invoking setup-zig

This workflow now calls mlugg/setup-zig@v2 with version: ${{ env.ZIG_VERSION }} and cache-key: ${{ env.ZIG_VERSION }}, but test-bun.yml does not define ZIG_VERSION at workflow/job scope (unlike the other updated workflows). On Bun CI runs this yields an empty version input, which can make the Zig setup step fail or become non-deterministic by falling back to an implicit default, blocking or destabilizing the Bun test job.

Useful? React with 👍 / 👎.

@wemeetagain wemeetagain changed the title feat(blst): replace blst with lodestar-z/blst feat(blst): replace blst and pubkeys with lodestar-z Feb 23, 2026
spiral-ladder added a commit that referenced this pull request Feb 25, 2026
**Motivation**

as previously discussed, remove bun tests entirely. Extracted from #8900
cc @nazarhussain
@spiral-ladder
Copy link
Contributor Author

/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 replaces the @chainsafe/blst dependency with @chainsafe/lodestar-z across the entire codebase. This is a significant refactoring that also introduces a new native pubkey cache for better performance, replacing createPubkeyCache with a singleton getPubkeyCache. The changes are extensive but appear to be applied consistently and correctly. The CI configuration is also updated to install Zig, which is a new build dependency. Overall, this is a great improvement. I have one minor suggestion regarding a pre-existing issue in pnpm-workspace.yaml.

Comment on lines +30 to +34
overrides:
dns-over-http-resolver": ^2.1.1
elliptic: '>=6.6.1'
loupe": ^2.3.6
nan": ^2.19.0
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

It seems there's a pre-existing formatting issue in the overrides section. Several keys have a trailing double quote, which makes them invalid YAML keys. While this PR only moves this block, it would be a good opportunity to fix it. The keys should be properly quoted.

overrides:
  "dns-over-http-resolver": ^2.1.1
  elliptic: '>=6.6.1'
  "loupe": ^2.3.6
  "nan": ^2.19.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.

3 participants