Skip to content

feat(shuffle): add swap-or-not shuffling module and binding - #559

Merged
wemeetagain merged 20 commits into
mainfrom
nh/swap-or-not-shuffle-parity
Aug 19, 2026
Merged

feat(shuffle): add swap-or-not shuffling module and binding#559
wemeetagain merged 20 commits into
mainfrom
nh/swap-or-not-shuffle-parity

Conversation

@nazarhussain

@nazarhussain nazarhussain commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Zig swap_or_not_shuffle module and JS bindings for the shuffle APIs Lodestar uses, so ChainSafe/lodestar can drop @chainsafe/swap-or-not-shuffle.

  • Shares one shuffle implementation between state_transition and the JS binding.
  • Matches the upstream Rust/reference behavior for implemented paths.
  • Returns explicit errors for degenerate inputs that the reference package would crash or hang on.
  • Leaves async binding support for a future zapi release, since Lodestar does not use it.

Testing

  • Ported upstream shuffle reference tests and existing lodestar-z shuffle vectors.
  • Added Zig coverage for validation order, error paths, proposer index, and sync committee selection.
  • Checked beta metrics against unstable; no performance regression found.

Follow-ups

AI assistance was used for drafting and implementation support.

Base automatically changed from nh/swap-or-not-shuffle to main August 13, 2026 11:23
@nazarhussain
nazarhussain force-pushed the nh/swap-or-not-shuffle-parity branch from 71ec60d to 01edd6b Compare August 13, 2026 11:26
Comment thread bindings/src/bindings.js Outdated
Comment on lines +6 to +11
// Namespace-level constants mirroring @chainsafe/swap-or-not-shuffle; the
// zapi DSL only auto-exports functions and classes, so values are attached
// here instead of via a native register hook.
bindings.shuffle.SHUFFLE_ROUNDS_MAINNET = 90;
bindings.shuffle.SHUFFLE_ROUNDS_MINIMAL = 10;
bindings.shuffle.ByteCount = {One: 1, Two: 2};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This will soon go away as we will support namepsace pub const and enum in the Zapi.

@nazarhussain
nazarhussain marked this pull request as ready for review August 14, 2026 06:53
@nazarhussain
nazarhussain requested a review from a team as a code owner August 14, 2026 06:53
@lodekeeper

Copy link
Copy Markdown
Contributor

Metrics check from beta vs unstable, sampled 2026-08-17 around 08:30 UTC.

Compared the PR-related beta build against same-network unstable over a 12h window, with a 24h memory trend check:

  • Beta branch build: v1.46.0/nh/zig-shuffle-integration/8c3cfb0
  • Unstable baseline: v1.46.0/ec59619
  • Matching node types checked: hoodi sas, semi, super; mainnet mainnet-fcr, mainnet-super
  • Note: beta-solo was not useful for this branch comparison; its lodestar_version sample is still v1.46.0/e2b315e.

Verdict: I do not see a performance regression from this beta deployment.

Health is clean:

  • Head distance: beta is 0-1 slots from clock, same as unstable.
  • Finality distance: 2 epochs on beta and unstable.
  • Block processor queue: 0 currently; max over 12h was 0 except one beta-semi sample at 1.
  • Reorgs over 12h match unstable exactly: hoodi 1, mainnet 7.

Block/state/epoch processing is broadly comparable:

  • beta-sas is slightly slower than unstable-sas, but only around +4-7% on the main paths:
    • received -> state transition: 0.180s vs 0.168s
    • state transition: 0.089s vs 0.085s
    • received -> fully verified: 0.467s vs 0.448s
    • epoch transition: 0.986s vs 0.943s
  • beta-semi is effectively flat: most paths around +1%, epoch transition around +5%.
  • beta-super is faster than unstable-super on most paths:
    • received -> state transition: 0.124s vs 0.131s
    • received -> fully verified: 0.384s vs 0.418s
    • epoch transition: 0.863s vs 0.921s
  • Mainnet is also fine:
    • beta-mainnet-fcr is modestly slower on a few paths, around +4-7%, with CPU only +5%.
    • beta-mainnet-super is mixed/flat; no broad slowdown.

Resources do not support a regression hypothesis:

  • CPU is similar or lower on most matched nodes:
    • hoodi sas: 3.11 vs 3.19 cores
    • hoodi semi: 0.71 vs 0.69 cores
    • hoodi super: 1.05 vs 1.21 cores
    • mainnet-fcr: 0.55 vs 0.52 cores
    • mainnet-super: 0.58 vs 0.67 cores
  • RSS is lower on beta for matched nodes in the 12h average, with the usual uptime caveat:
    • beta beacons are ~48.7h old; unstable hoodi beacons are ~56.4h old.
  • V8 heap and external memory are flat over 24h.
  • GC pause time is low, below 1% of wall time on all checked beta nodes.
  • Event-loop p99 is comparable or better except a beta-semi max spike, but no sustained queue or CPU pressure follows it.

Shuffle/cache-specific signals look clean:

  • lodestar_shuffling_cache_size is 4 everywhere.
  • lodestar_shuffling_cache_miss_count increase is 0 on beta and unstable over 12h.
  • lodestar_cp_state_cache_state_reload_shuffling_cache_miss_count increase is also 0.
  • Shuffling cache hit counts are comparable to unstable.

PTC-specific caveat:

  • vc_ptc_duties_count, vc_ptc_duties_epoch_count, and vc_published_payload_attestations_total are all 0 on both beta and unstable in this sample.
  • So there is no direct PTC-duty latency sample yet; this check mostly validates the downstream beacon-node processing/resource behavior around the native shuffle integration.

Watch item: beta-sas has slightly more validator misses than unstable-sas over the sample (about 45 vs 4, miss ratio ~0.41% vs ~0.32%), but target/source hit rates remain above 99.5%, head correctness is close to unstable, and this is not backed by CPU/memory/queue pressure. I would keep an eye on it during soak, but I would not call it a regression from the current metrics.

Recommendation: continue soak, but from current beta vs unstable metrics this looks OK.

Comment thread bindings/napi/async_task.zig Outdated
nazarhussain added a commit that referenced this pull request Aug 18, 2026
Review feedback (#559): required members (compute/resolve/deinit) are
now enforced with a comptime error, errorMessage becomes optional with
an @Errorname default, and an optional reject hook lets a task build
its own rejection value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@spiral-ladder spiral-ladder left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

mostly doc related comments, i kinda am of the opinion that we should cut down or limit the amount of comments or the quality of AI comments because they tend to write in a very roundabout way. in an isolated PR it seems fine but over the course of many PRs our codebase comments will become meaningless

Comment thread bindings/napi/async_task.zig Outdated
Comment thread bindings/napi/async_task.zig Outdated
Comment thread bindings/src/bindings.js Outdated
Comment thread bindings/napi/shuffle.zig Outdated
Comment thread bindings/src/index.d.ts Outdated
Comment thread bindings/napi/async_task.zig Outdated

@wemeetagain wemeetagain left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

Worth noting, we don't actually use the async variants of any shuffling code in production in lodestar (and I don't see a future where we ever will use it -- we'd rather just lower the entire STF!).

@wemeetagain wemeetagain left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just noticed @spiral-ladder 's comments, changing my review to red on his behalf

nazarhussain added a commit to ChainSafe/zapi that referenced this pull request Aug 18, 2026
## Motivation

While porting `@chainsafe/swap-or-not-shuffle` into lodestar-z
(ChainSafe/lodestar-z#559), scalar `pub const` decls and enums inside
exported modules were silently skipped by `js.exportModule`'s reflection
— the binding had to assign constants like `SHUFFLE_ROUNDS_MAINNET` and
`ByteCount` on the JS side. Classes already auto-export such consts as
statics (`applyStaticFields`); this extends the same behavior to
namespaces and the module root.

## Changes

- **Const export**: scalar/string `pub const` decls (int, float, bool,
`[]const u8`, string literals) export as enumerable value properties at
the module root and inside namespaces, reusing
`wrap_class.isStaticValueType`/`createStaticFieldValue` (now `pub`). A
namespace containing only consts now exports too.
- **Enum export**: `pub const X = enum {...}` exports as a **frozen**
plain object mapping each tag name (verbatim, no case conversion) to its
integer value, mirroring napi-rs `#[napi] pub enum`. Signed tag values
preserved.
- **Docs**: `TypedArray.fromExternal` doc now states it copies (pointing
at `OwnedTypedArray.fromOwnedSlice`/`intoValue` for ownership transfer)
and warns it panics outside DSL callbacks. New README "Constants and
Enums" section.

## Decisions

- Consts are **enumerable** (`setNamedProperty`), matching namespace
function properties. Verified class statics are also enumerable, so the
two paths are consistent.
- Enum objects are **frozen** — safer semantics for constants; napi-rs
leaves them mutable.
- Other const shapes (struct values, arrays) remain skipped; the
`.register` hook stays the escape hatch.

## Testing

10 new vitest cases in `examples/js_dsl` (root/namespace consts,
const-only namespace, enumerability, verbatim enum tags, signed values,
frozen-object semantics), written first and watched fail. Full suite:
`zig build test:zapi` + 130 vitest tests green.

## Consumer cleanup

Once released, lodestar-z deletes its `bindings.js` constant assignments
and declares the consts/enum in `bindings/napi/shuffle.zig` (guarded by
its "should expose the reference constants" test).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: bing <spiralladder@fastmail.com>
@wemeetagain

Copy link
Copy Markdown
Member

We decided we don't need async functions here based on our current usage of this, save the zapi enhancements for a future release

nazarhussain and others added 9 commits August 19, 2026 10:52
Port the full @chainsafe/swap-or-not-shuffle surface into the module
(exact validation order and errors, ComputeShuffledIndex, proposer and
sync committee index functions) and expose it as bindings.swapOrNotShuffle
with reference-identical names, semantics, and error messages.

Differential tests compare every function against the npm package;
2M-index sweeps show zero divergence. Perf benches show equal or faster
results than the Rust binding. Needed for ChainSafe/lodestar#9263.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Everything now lives under bindings.shuffle: the reference-parity API
plus the pre-existing in-place innerShuffleList variant. Drops the
separate swap_or_not_shuffle.zig binding file and namespace.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bindings/test/swapOrNotShuffle.test.ts merges into shuffle.test.ts and
bindings/perf/swapOrNotShuffle.test.ts becomes perf/shuffle.test.ts,
matching bindings/napi/shuffle.zig.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Confines the raw napi async-work/Deferred plumbing to one reusable
module. Upstream candidate for a zapi js.AsyncTask; transferOwnedSlice
is superseded by zapi#68 OwnedTypedArray once released.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shuffle.zig is now zapi DSL plus a small ShuffleTask; all raw napi
plumbing lives in async_task.zig. No JS-visible behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ComputeShuffledIndex.get and the committee weighting comparison now use
wrapping ops matching the Rust reference's release-mode behavior, so
out-of-range JS inputs produce reference-identical values instead of
aborting the process. Also fixes ByteCount d.ts typing, adds error-path
tests, and refreshes stale comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tation

Ports the reference repo's test suite and its pure-TS reference
implementation (deps inlined: sha256 via node:crypto, spec constants,
naive computeShuffledIndex/committee sampling) so the shuffle tests are
self-contained. Out-of-range wrapping vectors are hardcoded from
@chainsafe/swap-or-not-shuffle v1.2.1 output. Drops that package as a
devDependency; it remains only as a transitive dep of
@lodestar/state-transition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The zapi DSL only auto-exports functions and classes; rather than a
native register hook, the loader attaches the three reference constants
to the shuffle namespace. Can move back into Zig once zapi auto-exports
namespace-level consts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nazarhussain and others added 5 commits August 19, 2026 10:52
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
package.json is net-unchanged on this branch (devDependency added then
removed), so the lockfile drift from those installs (refreshed
deprecation metadata, re-resolved sax) does not belong in the diff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback (#559): required members (compute/resolve/deinit) are
now enforced with a comptime error, errorMessage becomes optional with
an @Errorname default, and an optional reject hook lets a task build
its own rejection value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zapi 4.0.0 (ChainSafe/zapi#73) auto-exports namespace-level consts and
enums, so SHUFFLE_ROUNDS_MAINNET/MINIMAL and ByteCount move next to the
functions that use them and the JS loader shim in bindings.js is gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nazarhussain
nazarhussain force-pushed the nh/swap-or-not-shuffle-parity branch from fd545e5 to 59de52e Compare August 19, 2026 05:54
Review feedback: the async variants are unused (ChainSafe/lodestar#9829
calls unshuffleList, computeProposerIndex and computeSyncCommitteeIndices),
so drop asyncShuffleList/asyncUnshuffleList and the async_task helper --
the zapi AsyncTask work waits for a future release. Also unbind the other
unused package exports (forward shuffleList, ComputeShuffledIndex, the
Electra wrappers, SHUFFLE_ROUNDS_* and ByteCount); the Zig module still
implements them, so re-binding later is a few lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nazarhussain nazarhussain changed the title feat: reference-parity swap_or_not_shuffle API and JS binding feat(shuffle): add native swap-or-not shuffling module and binding Aug 19, 2026
Review feedback: drop narration the next reader does not need (zapi
internals, package-parity restatements, per-file headers other bindings
do not carry) and keep only constraint notes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread bindings/napi/shuffle.zig Outdated
const allocator = if (builtin.mode == .Debug) gpa.allocator() else std.heap.c_allocator;

/// Verbatim reference error messages.
fn shuffleErrorMessage(err: anyerror) [:0]const u8 {

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.

Why must do there conversion for the first three error? Seems we don't need next two functions if we don't do this

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.

Good point. With the async/zapi-enhancement work out of scope, I do not think we need the extra reference-message conversion here.

I simplified the binding so it surfaces the native Zig/zapi error names consistently instead of mapping the first shuffle errors to @chainsafe/swap-or-not-shuffle prose messages, and removed the now-unneeded helper path. The tests now assert InvalidSeedLength / InvalidNumberOfRounds for these cases.

I cannot push directly to ChainSafe/lodestar-z (lodekeeper only has read permission), so I pushed the signed patch here: lodekeeper@a8148ba

Verification:

  • PATH=/tmp/zig-0.16.0:$PATH zig fmt --check bindings/napi/shuffle.zig src/swap_or_not_shuffle/root.zig
  • PATH=/tmp/zig-0.16.0:$PATH sh -c 'git ls-files "*.zig" | xargs zig fmt --check'
  • PATH=/tmp/zig-0.16.0:$PATH pnpm prepare-mainnet
  • pnpm exec vitest run bindings/test/shuffle.test.ts (18 tests)
  • pnpm exec biome check bindings/test/shuffle.test.ts
  • pnpm lint exited 0 with existing warnings in bindings/src/index.d.ts

Note: plain zig fmt --check . still reports unformatted files under downloaded zig-pkg/ dependency sources after the local build, so I used the tracked-file format check above for the actual repo files.

Review feedback: the reference message mapping existed only for parity
with the npm package's strings; nothing in Lodestar reads them, and the
other bindings here already surface error names (see blst tests). Drops
both helper functions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread bindings/napi/shuffle.zig Outdated
Comment on lines +1 to +2
//! JS binding for the `swap_or_not_shuffle` module. Function names, argument
//! order and error messages match `@chainsafe/swap-or-not-shuffle`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
//! JS binding for the `swap_or_not_shuffle` module. Function names, argument
//! order and error messages match `@chainsafe/swap-or-not-shuffle`.
//! JS binding for the `swap_or_not_shuffle` module.

Comment thread src/swap_or_not_shuffle/root.zig Outdated
Comment thread src/swap_or_not_shuffle/root.zig Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

these tests look kinda different from what we have in that repo, where are these from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These tests were not just coming from that repo, it is mix of those and existing shuffling tests.

Comment thread src/swap_or_not_shuffle/root.zig Outdated
return error.InvalidSeedLength;
}

var arena = std.heap.ArenaAllocator.init(parent_allocator);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Q: why arena?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Documented the usage in the comments.

Arena seems fit for this use case.

The caches below are written until deinit and never freed piecemeal,
so an arena keeps their many small allocations to one teardown.

Comment thread src/swap_or_not_shuffle/root.zig Outdated
nazarhussain and others added 3 commits August 19, 2026 18:07
Co-authored-by: bing <spiralladder@fastmail.com>
Review questions: the module tests are a mix of upstream unit-test
vectors, vectors carried over from committee_indices.zig, one captured
from the npm package, and error-path cases the reference cannot express.
ComputeShuffledIndex uses an arena because its caches are freed only at
deinit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wemeetagain wemeetagain changed the title feat(shuffle): add native swap-or-not shuffling module and binding feat(shuffle): add swap-or-not shuffling module and binding Aug 19, 2026

@wemeetagain wemeetagain left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good!

Can you simplify / shorten the PR description before merging to only the most important information?

@spiral-ladder

Copy link
Copy Markdown
Member

Can you simplify / shorten the PR description before merging to only the most important information?

+1, im a bit tired of reading ai pr descriptions to be completely honest, i skipped this description entirely

@lodekeeper

Copy link
Copy Markdown
Contributor

@wemeetagain @spiral-ladder agreed. I cannot edit Nazar's PR body from lodekeeper (the PR edit endpoint returns 404 for this account), but here is a shorter replacement body that keeps only the merge-relevant bits:

## Summary

Adds a Zig `swap_or_not_shuffle` module and JS bindings for the shuffle APIs Lodestar uses, so ChainSafe/lodestar can drop `@chainsafe/swap-or-not-shuffle`.

- Shares one shuffle implementation between `state_transition` and the JS binding.
- Matches the upstream Rust/reference behavior for implemented paths.
- Returns explicit errors for degenerate inputs that the reference package would crash or hang on.
- Leaves async binding support for a future zapi release, since Lodestar does not use it.

## Testing

- Ported upstream shuffle reference tests and existing lodestar-z shuffle vectors.
- Added Zig coverage for validation order, error paths, proposer index, and sync committee selection.
- Checked beta metrics against unstable; no performance regression found.

## Follow-ups

- #563 adds PTC sampling on top of this.
- Async shuffle bindings can be added later if a consumer appears.

AI assistance was used for drafting and implementation support.

@wemeetagain

Copy link
Copy Markdown
Member

thanks @lodekeeper I'm just going with that

@wemeetagain
wemeetagain merged commit c2db37c into main Aug 19, 2026
33 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Lodestar Team Coordination Aug 19, 2026
@github-actions github-actions Bot mentioned this pull request Aug 19, 2026
wemeetagain pushed a commit that referenced this pull request Aug 19, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.0.0](v0.1.2...v1.0.0)
(2026-08-19)


### Features

* add `state.getBuildersLength()` binding
([#472](#472))
([be2b5ab](be2b5ab))
* **beacon-node:** add block state cache and checkpoint datastore
([#452](#452))
([2145faa](2145faa))
* bindings to `getExpectedWithdrawals` and native tweaks
([#350](#350))
([f47bc66](f47bc66))
* **bindings:** add pubkey cache syncPubkeys
([#537](#537))
([542779f](542779f))
* **bindings:** aggregate cached public keys by validator index
([#397](#397))
([2f90603](2f90603))
* **bindings:** align `BeaconStateView` with `IBeaconStateView`
([#347](#347))
([b8ec273](b8ec273))
* **bindings:** configurable pubkey cache growth step
([#481](#481))
([133ef24](133ef24))
* **bindings:** expose more APIs for STF
([#444](#444))
([7fe2609](7fe2609))
* **bls:** add small MSM for npoints &lt; 32
([#393](#393))
([b430638](b430638))
* **blst:** use external buffers for blst operations
([#358](#358))
([78e4678](78e4678))
* **ci:** conditionally publish bindings with tag
([#355](#355))
([ea77919](ea77919))
* **clock:** add clock module for slot/epoch timing
([#354](#354))
([385b077](385b077))
* **fork_choice:** add Prometheus metrics module
([#309](#309))
([cbc9d8d](cbc9d8d))
* **forkchoice:** implement the forkchoice module
([#246](#246))
([7c62a9b](7c62a9b))
* getSyncCommitteesWitness
([#367](#367))
([ef77649](ef77649))
* implement `loadState` API and binding
([#165](#165))
([f903519](f903519)),
closes [#159](#159)
* **metrics:** metrics bindings
([#455](#455))
([dd41999](dd41999))
* migrate blst,pubkeys to use zapi js dsl
([#331](#331))
([fcd26ca](fcd26ca))
* **pubkeys:** add getPubkeyBytes binding
([#555](#555))
([4ca51cf](4ca51cf))
* publish ARM64 musl bindings
([#482](#482))
([ac764c9](ac764c9))
* **shuffle:** add swap-or-not shuffling module and binding
([#559](#559))
([c2db37c](c2db37c))
* split nextValue fn
([#464](#464))
([b47faeb](b47faeb))
* support getLatestWeakSubjectivityCheckpointEpoch
([#366](#366))
([dcf3883](dcf3883))
* update fulu deposit processing
([#442](#442))
([064335c](064335c))


### Bug Fixes

* avoid set ([#484](#484))
([2e25d97](2e25d97))
* better generation of rand scalar
([#388](#388))
([74dce77](74dce77))
* **bindings:** accept `dontTransferCache` in processSlots for backward
compatibility
([#460](#460))
([65df5af](65df5af))
* **bindings:** check signature infinity by default
([#509](#509))
([2f5f281](2f5f281))
* **bindings:** clean up failed async BLS work
([#527](#527))
([1111b00](1111b00))
* **bindings:** free metrics writer on scrape failure
([#529](#529))
([4c8d94a](4c8d94a))
* **bindings:** harden random aggregate scalars
([#528](#528))
([8e89a63](8e89a63))
* **bindings:** log level for missing fields
([#435](#435))
([08faf41](08faf41))
* **bindings:** misordering of print for cpu count
([#381](#381))
([752a972](752a972))
* **bindings:** populate epoch participation for test fixtures
([#436](#436))
([8dbdd2e](8dbdd2e))
* **bindings:** refcount Pool to fix teardown panic
([#352](#352))
([23b2f68](23b2f68))
* **bindings:** roll back partial N-API initialization
([#491](#491))
([31c5ebb](31c5ebb))
* **bindings:** size BLS thread pool by cgroup-aware CPU count
([#386](#386))
([3ae9522](3ae9522))
* **bindings:** validate class types before unwrap
([#514](#514))
([2fd2ad5](2fd2ad5))
* **bindings:** validate secret key hex length
([#517](#517))
([136e415](136e415))
* **bls:** align PublicKey.uncompress validation with
Signature.uncompress
([#508](#508))
([5a8dbe9](5a8dbe9))
* **bls:** bound randomized aggregation inputs
([#548](#548))
([779d0bf](779d0bf)),
closes [#542](#542)
* **bls:** clean up partial thread pool initialization
([#490](#490))
([d55e598](d55e598))
* **bls:** convert pippenger scratch bytes to element counts
([#513](#513))
([a12ca92](a12ca92))
* **bls:** enforce 32-byte signing roots
([#545](#545))
([72fd308](72fd308))
* **bls:** make batch cardinality structural
([#547](#547))
([a06d8b2](a06d8b2))
* **bls:** preserve aggregate outputs on failure
([#521](#521))
([e0b6dd1](e0b6dd1))
* **bls:** reject empty keygen salts
([#524](#524))
([d2a9c86](d2a9c86))
* **bls:** reject unknown BLST error codes
([#525](#525))
([9e4a6ad](9e4a6ad))
* **bls:** size pairing buffers for 32-bit targets
([#531](#531))
([dc64a27](dc64a27))
* **blst:** default signature infinity check to true if not provided
([#387](#387))
([021cdcb](021cdcb))
* **build:** remove `zig-out` from `files`
([#360](#360))
([c52af09](c52af09))
* **ci:** fix caching spec test version
([#439](#439))
([96885a1](96885a1))
* dangling state pointer in loadOtherState
([#450](#450))
([81cbd5f](81cbd5f))
* **epoch_cache:** compute missing `next_proposers`
([#447](#447))
([0088a29](0088a29))
* **epoch_cache:** populate decision roots in afterProcessEpoch
([#453](#453))
([4b70a5e](4b70a5e))
* export asyncAggregateWithRandomness through napi binding
([#371](#371))
([1d04c2b](1d04c2b))
* harden memory safety across PMT, SSZ tree views, and state transition
([#377](#377))
([d6f5897](d6f5897))
* improve atomic ordering in ThreadPool and NAPI init
([#310](#310))
([4b0a1cc](4b0a1cc))
* interface compatbility with NativeBeaconStateView
([#445](#445))
([89e13d1](89e13d1))
* missing deinits in loadOtherState
([#459](#459))
([094d278](094d278))
* missing state commits
([#454](#454))
([a432b55](a432b55))
* no-op when syncPubkeys run on a pk cache with shrinking validator set
([#432](#432))
([ed05a99](ed05a99))
* param order in BeaconBlockBody
([#348](#348))
([d8b9c06](d8b9c06))
* pendingConsolidations bindings
([#449](#449))
([b9c497e](b9c497e))
* **pmt,ssz:** harden chunked-leaf and zero-copy tree-view memory safety
([#400](#400))
([de50c53](de50c53))
* populate cache balances during rewards/penalties processing
([#474](#474))
([5bf23dc](5bf23dc))
* re-expose sizes
([#369](#369))
([64b81f3](64b81f3))
* remove `slashValidator` gating on active status
([#448](#448))
([d319a0d](d319a0d))
* **ssz:** drop redundant default-init pass in fixed-list decode
([#468](#468))
([0c757be](0c757be))
* **ssz:** publish child cache entries after lookup
([#565](#565))
([21e78c9](21e78c9))
* state transition binding exports
([#456](#456))
([895982c](895982c))
* **state-transition:** group-check signature sets
([#515](#515))
([42774e9](42774e9)),
closes [#502](#502)
* **state-transition:** isolate epoch step cache mutations
([#535](#535))
([a83741a](a83741a))
* **state-transition:** repair Pool.init call broken by
[#346](https://github.com/ChainSafe/lodestar-z/issues/346)×[#367](https://github.com/ChainSafe/lodestar-z/issues/367)
merge skew ([#394](#394))
([b42944f](b42944f))
* various fixes around config
([#433](#433))
([c4f082c](c4f082c))


### Performance Improvements

* **bindings:** drop TS BLS comparison benches and report benchmarks on
PRs ([#552](#552))
([c909c6f](c909c6f))
* **bls:** add cache-aware signature verifier
([#562](#562))
([063857e](063857e))
* **bls:** bypass worker queue for small batches
([#553](#553))
([3f8a6df](3f8a6df))
* **epoch:** replace AutoHashMap with array lookup in reward/penalty
caches ([#286](#286))
([e4e181b](e4e181b)),
closes [#243](#243)
* **pmt:** chunked-leaf packing for basic lists and container_struct
([#346](#346))
([ba156c4](ba156c4))


### Code Refactoring

* allocate `AsyncAggRandData` in one obj
([#384](#384))
([459750f](459750f))
* **bindings/pubkeys:** simplify allocation strategy for aggregate
([#518](#518))
([b82750f](b82750f))
* **bindings:** rename blst Lifecycle to State
([#516](#516))
([0a9c179](0a9c179))
* **bindings:** use zapi js.io() instead of local io module
([#469](#469))
([2b34cc0](2b34cc0))
* **bindings:** wake only required number of workers
([#383](#383))
([1db57f1](1db57f1))
* **bls:** allocations around VMAS
([#395](#395))
([dfda58c](dfda58c))
* **bls:** clean up bls
([#398](#398))
([e0f3b9b](e0f3b9b))
* **bls:** remove need for tracking results for
verifyMultipleAggregateSignatures
([#389](#389))
([6fe5c3f](6fe5c3f))
* **bls:** remove single-threaded fallback
([#390](#390))
([e057713](e057713))
* **clock:** single public Clock; internalize SlotClock
([#463](#463))
([fbab1fa](fbab1fa))
* make XXXDecisionRoot fns return `js.String`
([#342](#342))
([aef4420](aef4420))
* move shuffle into swap_or_not_shuffle module
([#558](#558))
([e56efb2](e56efb2))
* **pubkeys:** centralize the process-wide cache
([#522](#522))
([dc9669d](dc9669d))


### Miscellaneous Chores

* avoid slow tests in AGENTS.md
([#546](#546))
([c60f2a9](c60f2a9))
* bump zapi to include musl build
([#485](#485))
([0b488cc](0b488cc))
* **ci:** pin github actions with sha hashes
([#507](#507))
([167b8f5](167b8f5))
* deprecate unused blst APIs
([#575](#575))
([7b547fa](7b547fa))
* **deps:** bump zapi v2.1.0 -&gt; v2.2.0
([#376](#376))
([0c240d8](0c240d8))
* **deps:** bump zbuild
([#403](#403))
([e2545de](e2545de))
* **deps:** compile blst with ReleaseFast
([#391](#391))
([753a896](753a896))
* **deps:** update zapi to 3.1.0
([#483](#483))
([f3e5827](f3e5827))
* **deps:** use zapi v2.1.0
([#372](#372))
([88f403a](88f403a))
* disable gemini auto code review
([#382](#382))
([63e42a4](63e42a4)),
closes [#380](#380)
* **docs:** add comments section in AGENTS.md
([#566](#566))
([0c09750](0c09750))
* move state clones out of benchmark run functions
([#324](#324))
([e4035de](e4035de))
* prepare 1.0.0 release
([#576](#576))
([20b657b](20b657b))
* release v0.1.2-rc.3
([#370](#370))
([e4fc551](e4fc551))
* **release:** 0.1.2-rc.2
([#365](#365))
([7046128](7046128))
* **release:** v0.1.2-rc.10
([#477](#477))
([9a4fad5](9a4fad5))
* **release:** v0.1.2-rc.4
([#373](#373))
([09468f1](09468f1))
* **release:** v0.1.2-rc.5
([#374](#374))
([f344efa](f344efa))
* **release:** v0.1.2-rc.6
([#375](#375))
([bdf5b67](bdf5b67))
* **release:** v0.1.2-rc.8
([#401](#401))
([06f91c2](06f91c2))
* **release:** v0.1.2-rc.9
([#404](#404))
([6024800](6024800))
* remove merge transition code
([#359](#359))
([09b175d](09b175d))
* remove stale epoch cache TODOs
([#534](#534))
([27a547a](27a547a))
* rename era shortHistoricalRoot to shortEraRoot
([#473](#473))
([c75a4d3](c75a4d3))
* **scripts:** build bindings with preset
([#434](#434))
([a1b5ef7](a1b5ef7))
* silence debug log when used in release builds
([#486](#486))
([c5377d7](c5377d7))
* support dev workflow
([#364](#364))
([fcb9a78](fcb9a78))
* update gloas types to align with the latest specs
([#431](#431))
([1f065b5](1f065b5))
* update spec test version to v1.7.0-alpha.11
([#451](#451))
([5875660](5875660))
* update spec-test-version: v1.6.0-beta.2 -&gt; v1.7.0-alpha.10
([#441](#441))
([f932b1c](f932b1c))
* update zapi to 4.0.0
([#571](#571))
([de8e3fd](de8e3fd))


### Documentation

* document security threat model
([#557](#557))
([e678b87](e678b87))
* more comprehensive AGENTS.md
([#520](#520))
([c74b386](c74b386))
* **pkix:** document load provenance requirement
([#556](#556))
([37e0aa2](37e0aa2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants