Skip to content

perf(bls): add cache-aware signature verifier - #562

Open
wemeetagain wants to merge 2 commits into
mainfrom
cayman/bls-indexed-verifier
Open

perf(bls): add cache-aware signature verifier#562
wemeetagain wants to merge 2 commits into
mainfrom
cayman/bls-indexed-verifier

Conversation

@wemeetagain

Copy link
Copy Markdown
Member

Summary

  • Add a bounded NAPI verifier for indexed, aggregate, and raw-public-key signature sets.
  • Resolve validator-indexed public keys and aggregate Uint32Array indices directly from the native public-key cache.
  • Add randomized same-message aggregation with native per-signature fallback results.
  • Expose the verifier API, set types, and batch limits through the JavaScript bindings.

Why

Lodestar currently materializes or aggregates public keys in TypeScript before crossing the native boundary. Failed same-message aggregate checks also require another worker-queue pass to identify invalid signatures. This interface keeps validator indices through the native boundary, reuses the native public-key cache, and returns final per-signature results.

Impact

This reduces JavaScript/native serialization and repeated public-key work while providing bounded validation for every new input shape.

Paired with ChainSafe/lodestar#9820.

Written with codex assistance

@wemeetagain
wemeetagain marked this pull request as ready for review August 13, 2026 14:32
@wemeetagain
wemeetagain requested a review from a team as a code owner August 13, 2026 14:32

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

Copy link
Copy Markdown

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: 0f53b807f4

ℹ️ 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".

if (!pubkeys.state.initialized) return error.PubkeyIndexNotInitialized;
const indices = try uint32Slice(try set.getNamedProperty("indices"));
if (indices.len == 0) return error.EmptyIndices;
if (indices.len > max_indices_per_set) return error.TooManyIndices;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cap aggregate indices across the whole batch

When a caller supplies 256 aggregate sets, each set may reuse a 131,072-entry Uint32Array, so this per-set check still permits over 33 million sequential elliptic-curve additions in PubkeyCache.aggregateIndices() before verification. Because the exported NAPI function is synchronous, malformed or adversarial input can stall the calling Node.js thread for a prolonged period. Track and reject a bounded cumulative index count for the batch, rather than limiting each set independently.

AGENTS.md reference: AGENTS.md:L8-L9

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is good example of why something like #557 may be helpful

@lodekeeper-z lodekeeper-z Aug 14, 2026

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.

Correction: the measurements are valid for the direct in-process API, but my merge-blocking classification was not. Per the threat model in #557, I needed to trace a least-privileged attacker through a supported Lodestar path, account for protocol bounds and rate/peer controls, and compare against the existing cryptographic workload. This probe instead constructed the Cartesian maximum through arbitrary same-process input. I withdraw this as a blocker.

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.

why do we have bls_verifier and blst_verifier?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Its a poor name, but one is essentially shared utilities between the cache-aware verifier and low-level blst bindings, and the other is the cache-aware verifier.

@lodekeeper-z lodekeeper-z left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the native verifier and companion contract. The core design is coherent and the targeted build/tests pass. I found one additional error-classification issue below. I also confirmed the existing cumulative aggregate-index concern in the open thread: the maximum 256 x 131,072-index call took about 16.8 seconds synchronously in a local probe, so I consider a cumulative bound blocking before merge.

public_keys[i] = (try resolvePublicKey(set, set_type)) orelse
return js.Boolean.from(false);

signatures[i] = (try parseSignature(set)) orelse

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.

The early false return makes cache-error behavior order-dependent. A batch whose first set has a malformed signature returns false without resolving later sets, so a later missing validator index is hidden instead of throwing as the API contract says. I reproduced this at this head: [malformed(index 0), valid(index 99)] returned false, while reversing the same two sets threw PubkeyIndexNotFound. That can classify a local cache desynchronization as an invalid remote signature. Please resolve/validate every set's interface and cache state before returning the cryptographic result, and add an ordering regression test.

@lodekeeper-z lodekeeper-z left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved. Correction to my earlier review: after applying the repository threat model in #557, I withdraw the cumulative aggregate-work bound as a blocking finding. My probe exercised an arbitrary same-process Cartesian maximum; it did not establish a least-privileged hostile production path, account for protocol and Lodestar rate bounds, or show amplification beyond expected cryptographic work. The measured timing remains useful performance data, not a demonstrated merge blocker. The remaining cache-error ordering note is non-blocking API semantics. Native implementation, companion contract, and targeted verification look good.

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

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants