Skip to content

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

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

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

Conversation

@spiral-ladder
Copy link
Contributor

@spiral-ladder spiral-ladder commented Feb 13, 2026

Depends on ChainSafe/lodestar-z#231

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

🚀🚀 Significant benchmark improvement detected

Benchmark suite Current: eea4f8e Previous: 66fe43a Ratio
fastMsgIdFn h64 xxhash / 10000 bytes 878.00 ns/op 3.1420 us/op 0.28
Full benchmark results
Benchmark suite Current: eea4f8e Previous: 66fe43a Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 413.33 us/op 1.1043 ms/op 0.37
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 36.355 us/op 37.963 us/op 0.96
BLS verify - blst 919.50 us/op 828.69 us/op 1.11
BLS verifyMultipleSignatures 3 - blst 1.0323 ms/op 1.2526 ms/op 0.82
BLS verifyMultipleSignatures 8 - blst 1.5620 ms/op 1.9753 ms/op 0.79
BLS verifyMultipleSignatures 32 - blst 4.5719 ms/op 5.9422 ms/op 0.77
BLS verifyMultipleSignatures 64 - blst 8.6133 ms/op 11.079 ms/op 0.78
BLS verifyMultipleSignatures 128 - blst 16.433 ms/op 18.928 ms/op 0.87
BLS deserializing 10000 signatures 646.02 ms/op 734.05 ms/op 0.88
BLS deserializing 100000 signatures 6.4841 s/op 7.2306 s/op 0.90
BLS verifyMultipleSignatures - same message - 3 - blst 983.22 us/op 900.22 us/op 1.09
BLS verifyMultipleSignatures - same message - 8 - blst 1.1106 ms/op 1.0368 ms/op 1.07
BLS verifyMultipleSignatures - same message - 32 - blst 1.7256 ms/op 1.7066 ms/op 1.01
BLS verifyMultipleSignatures - same message - 64 - blst 2.5579 ms/op 2.5914 ms/op 0.99
BLS verifyMultipleSignatures - same message - 128 - blst 4.2002 ms/op 4.3580 ms/op 0.96
BLS aggregatePubkeys 32 - blst 18.143 us/op 19.378 us/op 0.94
BLS aggregatePubkeys 128 - blst 65.450 us/op 68.694 us/op 0.95
getSlashingsAndExits - default max 65.767 us/op 87.536 us/op 0.75
getSlashingsAndExits - 2k 304.92 us/op 304.36 us/op 1.00
isKnown best case - 1 super set check 195.00 ns/op 257.00 ns/op 0.76
isKnown normal case - 2 super set checks 194.00 ns/op 194.00 ns/op 1.00
isKnown worse case - 16 super set checks 194.00 ns/op 197.00 ns/op 0.98
validate api signedAggregateAndProof - struct 1.1989 ms/op 1.5629 ms/op 0.77
validate gossip signedAggregateAndProof - struct 1.1816 ms/op 1.5610 ms/op 0.76
batch validate gossip attestation - vc 640000 - chunk 32 116.47 us/op 120.69 us/op 0.96
batch validate gossip attestation - vc 640000 - chunk 64 100.79 us/op 106.40 us/op 0.95
batch validate gossip attestation - vc 640000 - chunk 128 93.140 us/op 98.931 us/op 0.94
batch validate gossip attestation - vc 640000 - chunk 256 90.195 us/op 94.602 us/op 0.95
bytes32 toHexString 339.00 ns/op 359.00 ns/op 0.94
bytes32 Buffer.toString(hex) 225.00 ns/op 279.00 ns/op 0.81
bytes32 Buffer.toString(hex) from Uint8Array 311.00 ns/op 479.00 ns/op 0.65
bytes32 Buffer.toString(hex) + 0x 225.00 ns/op 240.00 ns/op 0.94
Return object 10000 times 0.22250 ns/op 0.23080 ns/op 0.96
Throw Error 10000 times 4.0885 us/op 4.1577 us/op 0.98
toHex 130.10 ns/op 131.91 ns/op 0.99
Buffer.from 122.88 ns/op 115.95 ns/op 1.06
shared Buffer 76.674 ns/op 75.278 ns/op 1.02
fastMsgIdFn sha256 / 200 bytes 1.7680 us/op 1.8150 us/op 0.97
fastMsgIdFn h32 xxhash / 200 bytes 187.00 ns/op 234.00 ns/op 0.80
fastMsgIdFn h64 xxhash / 200 bytes 246.00 ns/op 302.00 ns/op 0.81
fastMsgIdFn sha256 / 1000 bytes 5.8260 us/op 5.9560 us/op 0.98
fastMsgIdFn h32 xxhash / 1000 bytes 278.00 ns/op 407.00 ns/op 0.68
fastMsgIdFn h64 xxhash / 1000 bytes 286.00 ns/op 307.00 ns/op 0.93
fastMsgIdFn sha256 / 10000 bytes 50.403 us/op 52.974 us/op 0.95
fastMsgIdFn h32 xxhash / 10000 bytes 1.3400 us/op 3.6120 us/op 0.37
fastMsgIdFn h64 xxhash / 10000 bytes 878.00 ns/op 3.1420 us/op 0.28
send data - 1000 256B messages 4.6611 ms/op 6.3933 ms/op 0.73
send data - 1000 512B messages 3.9755 ms/op 4.8558 ms/op 0.82
send data - 1000 1024B messages 4.7477 ms/op 5.3885 ms/op 0.88
send data - 1000 1200B messages 5.4752 ms/op 5.4000 ms/op 1.01
send data - 1000 2048B messages 4.9101 ms/op 5.9762 ms/op 0.82
send data - 1000 4096B messages 6.1056 ms/op 6.7020 ms/op 0.91
send data - 1000 16384B messages 31.455 ms/op 35.479 ms/op 0.89
send data - 1000 65536B messages 96.388 ms/op 106.61 ms/op 0.90
enrSubnets - fastDeserialize 64 bits 847.00 ns/op 916.00 ns/op 0.92
enrSubnets - ssz BitVector 64 bits 328.00 ns/op 331.00 ns/op 0.99
enrSubnets - fastDeserialize 4 bits 128.00 ns/op 152.00 ns/op 0.84
enrSubnets - ssz BitVector 4 bits 335.00 ns/op 479.00 ns/op 0.70
prioritizePeers score -10:0 att 32-0.1 sync 2-0 220.84 us/op 361.18 us/op 0.61
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 250.88 us/op 262.70 us/op 0.96
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 370.31 us/op 389.91 us/op 0.95
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 682.51 us/op 737.23 us/op 0.93
prioritizePeers score 0:0 att 64-1 sync 4-1 826.35 us/op 1.1703 ms/op 0.71
array of 16000 items push then shift 1.5599 us/op 1.5424 us/op 1.01
LinkedList of 16000 items push then shift 7.1530 ns/op 7.0980 ns/op 1.01
array of 16000 items push then pop 76.622 ns/op 74.340 ns/op 1.03
LinkedList of 16000 items push then pop 6.9860 ns/op 6.8650 ns/op 1.02
array of 24000 items push then shift 2.3150 us/op 2.2973 us/op 1.01
LinkedList of 24000 items push then shift 7.2630 ns/op 7.2840 ns/op 1.00
array of 24000 items push then pop 104.86 ns/op 104.64 ns/op 1.00
LinkedList of 24000 items push then pop 6.9940 ns/op 6.8760 ns/op 1.02
intersect bitArray bitLen 8 5.5480 ns/op 5.5520 ns/op 1.00
intersect array and set length 8 32.691 ns/op 32.349 ns/op 1.01
intersect bitArray bitLen 128 27.844 ns/op 27.684 ns/op 1.01
intersect array and set length 128 533.32 ns/op 535.73 ns/op 1.00
bitArray.getTrueBitIndexes() bitLen 128 1.0070 us/op 1.2960 us/op 0.78
bitArray.getTrueBitIndexes() bitLen 248 1.7370 us/op 1.8260 us/op 0.95
bitArray.getTrueBitIndexes() bitLen 512 3.5750 us/op 3.6500 us/op 0.98
Full columns - reconstruct all 6 blobs 369.03 us/op 267.71 us/op 1.38
Full columns - reconstruct half of the blobs out of 6 112.53 us/op 110.45 us/op 1.02
Full columns - reconstruct single blob out of 6 30.365 us/op 32.204 us/op 0.94
Half columns - reconstruct all 6 blobs 253.28 ms/op 263.53 ms/op 0.96
Half columns - reconstruct half of the blobs out of 6 129.92 ms/op 133.50 ms/op 0.97
Half columns - reconstruct single blob out of 6 47.575 ms/op 48.309 ms/op 0.98
Full columns - reconstruct all 10 blobs 448.81 us/op 372.30 us/op 1.21
Full columns - reconstruct half of the blobs out of 10 195.67 us/op 156.94 us/op 1.25
Full columns - reconstruct single blob out of 10 30.795 us/op 30.805 us/op 1.00
Half columns - reconstruct all 10 blobs 421.15 ms/op 437.86 ms/op 0.96
Half columns - reconstruct half of the blobs out of 10 216.52 ms/op 220.72 ms/op 0.98
Half columns - reconstruct single blob out of 10 47.244 ms/op 48.851 ms/op 0.97
Full columns - reconstruct all 20 blobs 775.66 us/op 721.63 us/op 1.07
Full columns - reconstruct half of the blobs out of 20 398.71 us/op 413.44 us/op 0.96
Full columns - reconstruct single blob out of 20 42.550 us/op 45.762 us/op 0.93
Half columns - reconstruct all 20 blobs 844.91 ms/op 875.41 ms/op 0.97
Half columns - reconstruct half of the blobs out of 20 424.78 ms/op 449.79 ms/op 0.94
Half columns - reconstruct single blob out of 20 47.012 ms/op 49.955 ms/op 0.94
Set add up to 64 items then delete first 2.0058 us/op 2.0770 us/op 0.97
OrderedSet add up to 64 items then delete first 2.9649 us/op 3.0886 us/op 0.96
Set add up to 64 items then delete last 2.2612 us/op 2.4361 us/op 0.93
OrderedSet add up to 64 items then delete last 3.2862 us/op 3.6816 us/op 0.89
Set add up to 64 items then delete middle 2.2705 us/op 2.4368 us/op 0.93
OrderedSet add up to 64 items then delete middle 4.7543 us/op 5.2368 us/op 0.91
Set add up to 128 items then delete first 4.7213 us/op 4.8979 us/op 0.96
OrderedSet add up to 128 items then delete first 6.9884 us/op 7.0679 us/op 0.99
Set add up to 128 items then delete last 4.5719 us/op 4.9383 us/op 0.93
OrderedSet add up to 128 items then delete last 6.5947 us/op 7.1921 us/op 0.92
Set add up to 128 items then delete middle 4.4337 us/op 4.7849 us/op 0.93
OrderedSet add up to 128 items then delete middle 12.812 us/op 13.833 us/op 0.93
Set add up to 256 items then delete first 9.7947 us/op 9.8553 us/op 0.99
OrderedSet add up to 256 items then delete first 14.652 us/op 14.792 us/op 0.99
Set add up to 256 items then delete last 9.2335 us/op 9.8883 us/op 0.93
OrderedSet add up to 256 items then delete last 13.658 us/op 14.944 us/op 0.91
Set add up to 256 items then delete middle 8.9412 us/op 9.7412 us/op 0.92
OrderedSet add up to 256 items then delete middle 39.584 us/op 41.617 us/op 0.95
pass gossip attestations to forkchoice per slot 462.79 us/op 519.61 us/op 0.89
computeDeltas 1400000 validators 0% inactive 13.517 ms/op 14.508 ms/op 0.93
computeDeltas 1400000 validators 10% inactive 12.631 ms/op 13.519 ms/op 0.93
computeDeltas 1400000 validators 20% inactive 11.912 ms/op 12.580 ms/op 0.95
computeDeltas 1400000 validators 50% inactive 9.1510 ms/op 9.8278 ms/op 0.93
computeDeltas 2100000 validators 0% inactive 20.702 ms/op 22.270 ms/op 0.93
computeDeltas 2100000 validators 10% inactive 21.465 ms/op 20.790 ms/op 1.03
computeDeltas 2100000 validators 20% inactive 17.664 ms/op 18.823 ms/op 0.94
computeDeltas 2100000 validators 50% inactive 13.758 ms/op 14.745 ms/op 0.93
altair processAttestation - 250000 vs - 7PWei normalcase 1.8073 ms/op 1.9926 ms/op 0.91
altair processAttestation - 250000 vs - 7PWei worstcase 2.7615 ms/op 3.5952 ms/op 0.77
altair processAttestation - setStatus - 1/6 committees join 115.34 us/op 120.82 us/op 0.95
altair processAttestation - setStatus - 1/3 committees join 227.11 us/op 243.56 us/op 0.93
altair processAttestation - setStatus - 1/2 committees join 315.78 us/op 331.56 us/op 0.95
altair processAttestation - setStatus - 2/3 committees join 404.13 us/op 423.57 us/op 0.95
altair processAttestation - setStatus - 4/5 committees join 578.10 us/op 589.02 us/op 0.98
altair processAttestation - setStatus - 100% committees join 681.02 us/op 683.42 us/op 1.00
altair processBlock - 250000 vs - 7PWei normalcase 3.4776 ms/op 4.7244 ms/op 0.74
altair processBlock - 250000 vs - 7PWei normalcase hashState 17.874 ms/op 19.633 ms/op 0.91
altair processBlock - 250000 vs - 7PWei worstcase 24.195 ms/op 27.148 ms/op 0.89
altair processBlock - 250000 vs - 7PWei worstcase hashState 57.629 ms/op 59.605 ms/op 0.97
phase0 processBlock - 250000 vs - 7PWei normalcase 1.5905 ms/op 1.8585 ms/op 0.86
phase0 processBlock - 250000 vs - 7PWei worstcase 20.779 ms/op 26.307 ms/op 0.79
altair processEth1Data - 250000 vs - 7PWei normalcase 355.25 us/op 380.80 us/op 0.93
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 8.5590 us/op 9.2640 us/op 0.92
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 34.163 us/op 53.540 us/op 0.64
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 10.652 us/op 16.683 us/op 0.64
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 7.6930 us/op 11.137 us/op 0.69
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 128.85 us/op 231.37 us/op 0.56
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.6931 ms/op 1.6949 ms/op 1.00
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.0447 ms/op 2.0899 ms/op 0.98
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.0170 ms/op 2.0675 ms/op 0.98
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.3017 ms/op 4.3920 ms/op 0.98
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.4540 ms/op 2.5195 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.4822 ms/op 4.7074 ms/op 0.95
Tree 40 250000 create 355.03 ms/op 389.24 ms/op 0.91
Tree 40 250000 get(125000) 127.61 ns/op 129.06 ns/op 0.99
Tree 40 250000 set(125000) 1.1964 us/op 1.2535 us/op 0.95
Tree 40 250000 toArray() 12.307 ms/op 14.313 ms/op 0.86
Tree 40 250000 iterate all - toArray() + loop 12.403 ms/op 13.031 ms/op 0.95
Tree 40 250000 iterate all - get(i) 42.316 ms/op 43.449 ms/op 0.97
Array 250000 create 2.3927 ms/op 2.5123 ms/op 0.95
Array 250000 clone - spread 787.85 us/op 825.88 us/op 0.95
Array 250000 get(125000) 0.33500 ns/op 0.44400 ns/op 0.75
Array 250000 set(125000) 0.34400 ns/op 0.35800 ns/op 0.96
Array 250000 iterate all - loop 60.056 us/op 62.035 us/op 0.97
phase0 afterProcessEpoch - 250000 vs - 7PWei 40.358 ms/op 43.498 ms/op 0.93
Array.fill - length 1000000 2.8716 ms/op 3.1665 ms/op 0.91
Array push - length 1000000 10.549 ms/op 11.726 ms/op 0.90
Array.get 0.21379 ns/op 0.21920 ns/op 0.98
Uint8Array.get 0.21810 ns/op 0.22293 ns/op 0.98
phase0 beforeProcessEpoch - 250000 vs - 7PWei 14.982 ms/op 13.643 ms/op 1.10
altair processEpoch - mainnet_e81889 335.83 ms/op 309.85 ms/op 1.08
mainnet_e81889 - altair beforeProcessEpoch 16.936 ms/op 19.098 ms/op 0.89
mainnet_e81889 - altair processJustificationAndFinalization 7.1080 us/op 6.6750 us/op 1.06
mainnet_e81889 - altair processInactivityUpdates 3.6484 ms/op 3.8723 ms/op 0.94
mainnet_e81889 - altair processRewardsAndPenalties 18.401 ms/op 18.235 ms/op 1.01
mainnet_e81889 - altair processRegistryUpdates 625.00 ns/op 920.00 ns/op 0.68
mainnet_e81889 - altair processSlashings 158.00 ns/op 188.00 ns/op 0.84
mainnet_e81889 - altair processEth1DataReset 156.00 ns/op 153.00 ns/op 1.02
mainnet_e81889 - altair processEffectiveBalanceUpdates 2.0270 ms/op 1.5394 ms/op 1.32
mainnet_e81889 - altair processSlashingsReset 781.00 ns/op 1.1360 us/op 0.69
mainnet_e81889 - altair processRandaoMixesReset 1.1590 us/op 1.4670 us/op 0.79
mainnet_e81889 - altair processHistoricalRootsUpdate 158.00 ns/op 163.00 ns/op 0.97
mainnet_e81889 - altair processParticipationFlagUpdates 480.00 ns/op 518.00 ns/op 0.93
mainnet_e81889 - altair processSyncCommitteeUpdates 126.00 ns/op 134.00 ns/op 0.94
mainnet_e81889 - altair afterProcessEpoch 41.563 ms/op 43.492 ms/op 0.96
capella processEpoch - mainnet_e217614 876.96 ms/op 817.43 ms/op 1.07
mainnet_e217614 - capella beforeProcessEpoch 72.637 ms/op 61.403 ms/op 1.18
mainnet_e217614 - capella processJustificationAndFinalization 6.1350 us/op 6.0800 us/op 1.01
mainnet_e217614 - capella processInactivityUpdates 18.839 ms/op 15.102 ms/op 1.25
mainnet_e217614 - capella processRewardsAndPenalties 98.096 ms/op 108.41 ms/op 0.90
mainnet_e217614 - capella processRegistryUpdates 5.6870 us/op 5.6730 us/op 1.00
mainnet_e217614 - capella processSlashings 157.00 ns/op 155.00 ns/op 1.01
mainnet_e217614 - capella processEth1DataReset 164.00 ns/op 169.00 ns/op 0.97
mainnet_e217614 - capella processEffectiveBalanceUpdates 19.019 ms/op 11.082 ms/op 1.72
mainnet_e217614 - capella processSlashingsReset 779.00 ns/op 806.00 ns/op 0.97
mainnet_e217614 - capella processRandaoMixesReset 1.0860 us/op 1.8280 us/op 0.59
mainnet_e217614 - capella processHistoricalRootsUpdate 164.00 ns/op 258.00 ns/op 0.64
mainnet_e217614 - capella processParticipationFlagUpdates 474.00 ns/op 752.00 ns/op 0.63
mainnet_e217614 - capella afterProcessEpoch 110.48 ms/op 114.18 ms/op 0.97
phase0 processEpoch - mainnet_e58758 265.80 ms/op 253.94 ms/op 1.05
mainnet_e58758 - phase0 beforeProcessEpoch 54.521 ms/op 46.441 ms/op 1.17
mainnet_e58758 - phase0 processJustificationAndFinalization 6.4710 us/op 5.4380 us/op 1.19
mainnet_e58758 - phase0 processRewardsAndPenalties 16.950 ms/op 20.569 ms/op 0.82
mainnet_e58758 - phase0 processRegistryUpdates 2.6550 us/op 2.7890 us/op 0.95
mainnet_e58758 - phase0 processSlashings 272.00 ns/op 169.00 ns/op 1.61
mainnet_e58758 - phase0 processEth1DataReset 162.00 ns/op 199.00 ns/op 0.81
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1784 ms/op 942.03 us/op 1.25
mainnet_e58758 - phase0 processSlashingsReset 848.00 ns/op 1.1250 us/op 0.75
mainnet_e58758 - phase0 processRandaoMixesReset 1.0910 us/op 1.1970 us/op 0.91
mainnet_e58758 - phase0 processHistoricalRootsUpdate 164.00 ns/op 171.00 ns/op 0.96
mainnet_e58758 - phase0 processParticipationRecordUpdates 837.00 ns/op 835.00 ns/op 1.00
mainnet_e58758 - phase0 afterProcessEpoch 34.701 ms/op 35.588 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.3260 ms/op 1.3510 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.9295 ms/op 1.9418 ms/op 0.99
altair processInactivityUpdates - 250000 normalcase 12.046 ms/op 12.479 ms/op 0.97
altair processInactivityUpdates - 250000 worstcase 11.969 ms/op 12.490 ms/op 0.96
phase0 processRegistryUpdates - 250000 normalcase 4.5090 us/op 4.5140 us/op 1.00
phase0 processRegistryUpdates - 250000 badcase_full_deposits 181.41 us/op 361.98 us/op 0.50
phase0 processRegistryUpdates - 250000 worstcase 0.5 64.438 ms/op 68.947 ms/op 0.93
altair processRewardsAndPenalties - 250000 normalcase 16.099 ms/op 23.422 ms/op 0.69
altair processRewardsAndPenalties - 250000 worstcase 15.141 ms/op 20.164 ms/op 0.75
phase0 getAttestationDeltas - 250000 normalcase 6.4498 ms/op 6.7490 ms/op 0.96
phase0 getAttestationDeltas - 250000 worstcase 6.4884 ms/op 6.7900 ms/op 0.96
phase0 processSlashings - 250000 worstcase 76.947 us/op 120.06 us/op 0.64
altair processSyncCommitteeUpdates - 250000 10.224 ms/op 10.940 ms/op 0.93
BeaconState.hashTreeRoot - No change 187.00 ns/op 209.00 ns/op 0.89
BeaconState.hashTreeRoot - 1 full validator 97.632 us/op 89.761 us/op 1.09
BeaconState.hashTreeRoot - 32 full validator 1.0267 ms/op 848.49 us/op 1.21
BeaconState.hashTreeRoot - 512 full validator 12.557 ms/op 7.9408 ms/op 1.58
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 109.47 us/op 117.51 us/op 0.93
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 2.3323 ms/op 1.5246 ms/op 1.53
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 26.009 ms/op 21.800 ms/op 1.19
BeaconState.hashTreeRoot - 1 balances 99.065 us/op 96.508 us/op 1.03
BeaconState.hashTreeRoot - 32 balances 1.1425 ms/op 989.21 us/op 1.15
BeaconState.hashTreeRoot - 512 balances 9.8523 ms/op 5.8969 ms/op 1.67
BeaconState.hashTreeRoot - 250000 balances 180.21 ms/op 207.50 ms/op 0.87
aggregationBits - 2048 els - zipIndexesInBitList 20.297 us/op 18.320 us/op 1.11
regular array get 100000 times 23.833 us/op 24.852 us/op 0.96
wrappedArray get 100000 times 23.832 us/op 24.752 us/op 0.96
arrayWithProxy get 100000 times 13.474 ms/op 14.872 ms/op 0.91
ssz.Root.equals 22.914 ns/op 23.936 ns/op 0.96
byteArrayEquals 22.616 ns/op 23.504 ns/op 0.96
Buffer.compare 9.5860 ns/op 10.024 ns/op 0.96
processSlot - 1 slots 10.827 us/op 12.148 us/op 0.89
processSlot - 32 slots 2.5226 ms/op 2.9490 ms/op 0.86
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 4.5637 ms/op 4.2419 ms/op 1.08
getCommitteeAssignments - req 1 vs - 250000 vc 1.8338 ms/op 1.8899 ms/op 0.97
getCommitteeAssignments - req 100 vs - 250000 vc 3.6223 ms/op 3.7342 ms/op 0.97
getCommitteeAssignments - req 1000 vs - 250000 vc 3.9048 ms/op 3.9970 ms/op 0.98
findModifiedValidators - 10000 modified validators 508.45 ms/op 620.88 ms/op 0.82
findModifiedValidators - 1000 modified validators 378.72 ms/op 314.89 ms/op 1.20
findModifiedValidators - 100 modified validators 277.69 ms/op 275.39 ms/op 1.01
findModifiedValidators - 10 modified validators 242.77 ms/op 264.41 ms/op 0.92
findModifiedValidators - 1 modified validators 140.96 ms/op 152.45 ms/op 0.92
findModifiedValidators - no difference 144.52 ms/op 156.84 ms/op 0.92
migrate state 1500000 validators, 3400 modified, 2000 new 1.0609 s/op 1.0933 s/op 0.97
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.9800 ns/op 4.1800 ns/op 0.95
state getBlockRootAtSlot - 250000 vs - 7PWei 507.04 ns/op 547.95 ns/op 0.93
computeProposerIndex 100000 validators 1.4475 ms/op 1.5566 ms/op 0.93
getNextSyncCommitteeIndices 1000 validators 106.83 ms/op 119.85 ms/op 0.89
getNextSyncCommitteeIndices 10000 validators 106.93 ms/op 119.92 ms/op 0.89
getNextSyncCommitteeIndices 100000 validators 107.14 ms/op 119.89 ms/op 0.89
computeProposers - vc 250000 601.36 us/op 625.19 us/op 0.96
computeEpochShuffling - vc 250000 41.401 ms/op 42.302 ms/op 0.98
getNextSyncCommittee - vc 250000 9.8951 ms/op 10.492 ms/op 0.94
nodejs block root to RootHex using toHex 131.92 ns/op 157.30 ns/op 0.84
nodejs block root to RootHex using toRootHex 88.527 ns/op 91.670 ns/op 0.97
nodejs fromHex(blob) 435.26 us/op 297.38 us/op 1.46
nodejs fromHexInto(blob) 671.42 us/op 733.40 us/op 0.92
nodejs block root to RootHex using the deprecated toHexString 552.56 ns/op 549.98 ns/op 1.00
nodejs byteArrayEquals 32 bytes (block root) 27.975 ns/op 29.000 ns/op 0.96
nodejs byteArrayEquals 48 bytes (pubkey) 39.988 ns/op 41.050 ns/op 0.97
nodejs byteArrayEquals 96 bytes (signature) 39.453 ns/op 41.835 ns/op 0.94
nodejs byteArrayEquals 1024 bytes 44.050 ns/op 48.491 ns/op 0.91
nodejs byteArrayEquals 131072 bytes (blob) 1.8367 us/op 1.8975 us/op 0.97
browser block root to RootHex using toHex 159.17 ns/op 163.50 ns/op 0.97
browser block root to RootHex using toRootHex 160.58 ns/op 184.52 ns/op 0.87
browser fromHex(blob) 1.0974 ms/op 1.2188 ms/op 0.90
browser fromHexInto(blob) 671.68 us/op 717.32 us/op 0.94
browser block root to RootHex using the deprecated toHexString 382.12 ns/op 410.81 ns/op 0.93
browser byteArrayEquals 32 bytes (block root) 29.715 ns/op 31.442 ns/op 0.95
browser byteArrayEquals 48 bytes (pubkey) 41.363 ns/op 43.794 ns/op 0.94
browser byteArrayEquals 96 bytes (signature) 81.540 ns/op 86.296 ns/op 0.94
browser byteArrayEquals 1024 bytes 761.73 ns/op 808.25 ns/op 0.94
browser byteArrayEquals 131072 bytes (blob) 96.611 us/op 101.88 us/op 0.95

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

@twoeths
Copy link
Contributor

twoeths commented Mar 2, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results

by benchmarkbot/action

this benchmark is not great

BLS verifyMultipleSignatures 128 - blst 55.064 ms/op 19.818 ms/op 2.78

I wonder why CI still said "no regression". Also I don't think it's that bad when comparing blst-z vs blst-ts, maybe there is binding overhead or build option somewhere?

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