Make ordinary Schnorr signing 4.2× faster - #38
Draft
josusanmartin wants to merge 2 commits into
Draft
Conversation
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
MulGtableSampleScalaras masked rejection sampling overcrypto/rand.Readinstead ofcrypto/rand.Int(the same uniform distribution over[0, n); the 319-bit order assumption is pinned byTestScalarOrderBitLength) and makeIsCanonicala constant-time limb comparisonWarmGeneratorTableso latency-sensitive callers can pay the one-time setup cost during initializationOn an Intel Xeon E-2286G (Go 1.23, linux/amd64; median of 7 benchmark runs):
main/ genericThe 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
WarmGeneratorTableat 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/WeierstrassMulAdd2verifier rewrite and does not changeValidateorMulAddG. However, both patches touch precomputation/lookup concepts, and this branch makes the existingLookupbranchless, so the report is directly relevant and should not be dismissed.Compatibility evidence added here:
LookupvsLookupVarTimechecks over every supported index, plus neutral results outside the tableMulGvs the existing generic multiplication on scalar boundaries and 2,000 deterministic scalarsgoldilocks-cryptov0.1.2, the implementation used byelliottech/p3-lighter-circuitsmain, and that Rust implementationp3-lighter-circuitstrace generation, AIR constraint checking, proof generation, and proof verification at2cdd5de(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 ./...cargo test -p p3-schnorr --features reference deterministic_reference_batch_hashes_and_tracesStacked PRs