Skip to content

Make ordinary Schnorr signing 4.2× faster - #38

Draft
josusanmartin wants to merge 2 commits into
elliottech:mainfrom
josusanmartin:agent/signing-speedup
Draft

Make ordinary Schnorr signing 4.2× faster#38
josusanmartin wants to merge 2 commits into
elliottech:mainfrom
josusanmartin:agent/signing-speedup

Conversation

@josusanmartin

@josusanmartin josusanmartin commented Aug 6, 2026

Copy link
Copy Markdown

Result: 4.2× faster ordinary Schnorr signing

217.7 µs → 51.5 µs per signature on the normal signing API, with no caller-side batching or nonce preparation required. Fixed-generator multiplication itself improves from 215.3 µs to 46.4 µs (4.6×).

This is the crypto-core improvement behind the linked end-to-end signing stack's reduction from roughly 1.95 ms to 63.9 µs per normal order (~30×).

Summary

Speed up the ordinary Schnorr signing path while preserving the existing wire format and verification path.

  • add a position-weighted fixed-generator MulG table
  • remove per-signing point-window construction and allocations
  • reduce scalar/hash conversion overhead
  • rewrite SampleScalar as masked rejection sampling over crypto/rand.Read instead of crypto/rand.Int (the same uniform distribution over [0, n); the 319-bit order assumption is pinned by TestScalarOrderBitLength) and make IsCanonical a constant-time limb comparison
  • keep table lookup branchless for secret-dependent indices
  • expose WarmGeneratorTable so latency-sensitive callers can pay the one-time setup cost during initialization

On an Intel Xeon E-2286G (Go 1.23, linux/amd64; median of 7 benchmark runs):

Benchmark main / generic This branch Speedup
Schnorr sign 217.7 µs 51.5 µs 4.2×
generator multiplication 215.3 µs 46.4 µs 4.6×

The fixed table is built once. A cold build is about 2.2 ms on this host, so callers that care about first-order latency should call WarmGeneratorTable at startup.

Compatibility and PR #30 merge hold

Please keep this draft blocked until the witness/state-root concern in #30 is understood or the circuit witness suite confirms this implementation.

PR #30 has an unresolved maintainer report that its precomputed-table work caused a witness state-root mismatch. This change is independently implemented on current main: it does not include #30's Jacobian/Weierstrass MulAdd2 verifier rewrite and does not change Validate or MulAddG. However, both patches touch precomputation/lookup concepts, and this branch makes the existing Lookup branchless, so the report is directly relevant and should not be dismissed.

Compatibility evidence added here:

  • exact Lookup vs LookupVarTime checks over every supported index, plus neutral results outside the table
  • MulG vs the existing generic multiplication on scalar boundaries and 2,000 deterministic scalars
  • signed-window reconstruction tests at the scalar-order boundary, including the top digit
  • Poseidon known-answer vectors at rate/block boundaries
  • three committed public-key and signature vectors from independent Rust goldilocks-crypto v0.1.2, the implementation used by elliottech/p3-lighter-circuits
  • during development, eight deterministic signatures were byte-for-byte identical across this branch, current Go main, and that Rust implementation
  • the matching deterministic batch passed p3-lighter-circuits trace generation, AIR constraint checking, proof generation, and proof verification at 2cdd5de (deterministic_reference_batch_hashes_and_traces)

I would especially appreciate @irfanbozkurt confirming whether #30's mismatch was isolated to its verifier rewrite/table construction, or pointing this PR at the witness fixture that reproduced it.

Secondary: prepared nonces

This also adds an explicit opt-in prepared-nonce API. It moves the fixed-generator commitment out of the online signing path; the online portion benchmarks at about 3.0 µs/op. Prepared nonces are process-bound, atomically single-use, zeroed on consumption, and rejected after a fork. This is secondary to the normal signing improvement and is not used unless a caller explicitly prepares nonces.

Validation

  • go test ./...
  • go test -race ./...
  • Rust reference/public-key/signature cross-checks described above
  • cargo test -p p3-schnorr --features reference deterministic_reference_batch_hashes_and_traces

Stacked PRs

@josusanmartin josusanmartin changed the title Speed up Schnorr signing with fixed-generator multiplication Make ordinary Schnorr signing 4.2x faster Aug 6, 2026
@josusanmartin josusanmartin changed the title Make ordinary Schnorr signing 4.2x faster Make ordinary Schnorr signing 4.2× faster Aug 6, 2026
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.

1 participant