feat: add SpendAuthG short-scalar and base-field fixed-base support#8
Closed
czarcas7ic wants to merge 3 commits into
Closed
feat: add SpendAuthG short-scalar and base-field fixed-base support#8czarcas7ic wants to merge 3 commits into
czarcas7ic wants to merge 3 commits into
Conversation
Generalizes OrchardFixedBases to support multiplying the spend
authorization generator G^Orchard by (1) full-width base-field scalars
via a new FixedPointBaseField path and (2) 64-bit signed short scalars
via a new 22-window precomputed table.
Motivation: voting-circuits implements El Gamal encryption of 16 vote
shares per voter in its vote_proof circuit, computing:
C1_i = [r_i] * G (ephemeral public key, 255-bit base-field r_i)
C2_i = [v_i] * G + [r_i]*ea_pk (masked plaintext, 30-bit range-checked v_i)
where G = SpendAuthG. The voting spec locks G to SpendAuthG because
ZKP#3 (Share Reveal) and the tally phase must use the same generator
as the encrypter.
Without these enum variants:
- [r_i]*G would need to witness G as a NonIdentityPoint and use
variable-base mul (expensive)
- [v_i]*G would need to use the 85-window full-scalar path instead of
the 22-window short-scalar path (~315 rows wasted × 16 shares)
New types:
- OrchardBaseFieldBases { NullifierK, SpendAuthGBase } — the new
FixedPoints::Base associated type for OrchardFixedBases, allowing
downstream circuits to multiply G^Orchard by a base-field scalar
via FixedPointBaseField::mul.
- OrchardShortScalarBases { ValueCommitV, SpendAuthGShort } — the new
FixedPoints::ShortScalar associated type, enabling multiplication of
G^Orchard by a 64-bit signed short scalar via FixedPointShort::mul
using 22-window precomputed tables (saves 63 window rows per
multiplication vs. the 85-window full-scalar path for 64-bit scalars).
- New Z_SHORT / U_SHORT precomputed tables in spend_auth_g.rs, verified
by the existing test_zs_and_us and test_lagrange_coeffs helpers.
Breaking change: OrchardFixedBases is now a 3-variant enum
{Full(OrchardFixedBasesFull), Base(OrchardBaseFieldBases),
Short(OrchardShortScalarBases)} instead of the previous flat form
{Full(...), NullifierK, ValueCommitV}. The NullifierK and ValueCommitV
unit structs are retained with unchanged FixedPoint impls for backward
compat but are no longer plugged into FixedPoints. Internal callers
circuit::gadget::{value_commit_orchard, derive_nullifier} are rewired
to the new variants.
Verification:
- All 59 orchard tests pass (up from 53, new routing tests added).
- circuit::tests::round_trip and circuit::tests::serialized_proof_test_case
pass unchanged — the main Orchard action circuit VK is unchanged.
- src/circuit_description/ and src/circuit_proof_test_case.bin are
byte-identical to v0.11.0.
- cargo clippy --all-features -- -D warnings passes clean.
Co-Authored-By: Claude <noreply@anthropic.com>
- cargo fmt: wrap long line in derive_nullifier, reformat U_SHORT table, drop blank lines at start of test functions in fixed_bases.rs. - Fix intra-doc link error: `[v_i]*G` in OrchardShortScalarBases doc comment was parsed as an unresolved link; wrap in backticks. Co-Authored-By: Claude <noreply@anthropic.com>
czarcas7ic
added a commit
to valargroup/voting-circuits
that referenced
this pull request
Apr 11, 2026
The upstreaming checklist it contained is superseded by the draft PRs against valargroup/orchard: - valargroup/orchard#7 (visibility widenings + assign_constant) - valargroup/orchard#8 (SpendAuthG short-scalar + base-field enums) The PR diffs and descriptions are now the source of truth for what needs to land upstream. Keeping UPSTREAM.md risks drift.
Keep NullifierK and ValueCommitV usable alongside the new enum-based fixed-base API so existing call sites can retain the old marker-style flow with explicit conversions. This narrows the public and internal churn from the SpendAuthG generalization without changing circuit behavior.
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.
Summary
Generalizes
OrchardFixedBasesto support multiplying the spend authorization generatorG^Orchardby (1) full-width base-field scalars via a newFixedPointBaseFieldpath and (2) 64-bit signed short scalars via a new 22-window precomputed table. No other generator is added — onlyG^Orchardgains these new scalar-multiplication paths.The main Orchard action circuit VK is unchanged.
src/circuit_description/andsrc/circuit_proof_test_case.binare byte-identical to v0.11.0.Motivation
voting-circuits needs
[k]*G^Orchardin its El Gamal encryption gadget for two scalar kinds:pallas::Base, 255-bit), wherekis 16 per-share ephemeral randomness values. Without aFixedPointBaseFieldpath forG^Orchard, this requires witnessing the generator as aNonIdentityPointand using variable-base multiplication — an extra 254-row variable-base mul per share plus an equality constraint pinning the witnessed point to the fixed generator.kis 16 per-share plaintext values bounded to 30 bits. Without aFixedPointShortpath forG^Orchard, this requires the 85-window full-scalar path, wasting 63 windows permulcall. Orchard'sValueCommitVbase already gets the short-scalar treatment viaFixedPointShort; this PR extends the same capability toG^Orchardfor the same reason.This is a targeted optimization for the El Gamal gadget, not a general-purpose change. The only voting-circuits call site is
voting-circuits/src/circuit/elgamal.rs(lines 175 and 183).Concrete use case: voting-circuits El Gamal encryption
The vote_proof circuit (ZKP #2, Condition 11) encrypts 16 vote shares per voter under the election authority's public key:
where:
G= SpendAuthG (voting spec locks this; ZKP Convert BLAKE2s circuit to BLAKE2b #3 Share Reveal must use the same generator)r_i= 255-bit base-field randomness (requiresOrchardBaseFieldBases::SpendAuthGBase)v_i= 30-bit range-checked share value (requiresOrchardShortScalarBases::SpendAuthGShort)Measured savings from using the short-scalar path for
[v_i]*G: 315 circuit rows (3,827 → 3,512 high-water mark, K=13 budget 8,192 rows). Documentation atvoting-circuits/src/vote_proof/circuit.rs:118-121.What changed
New enums in
src/constants/fixed_bases.rsBoth plugged into
FixedPoints<OrchardFixedBases>:Each implements
FixedPoint<pallas::Affine>dispatching through amatchon the variant, in exactly the same shape asOrchardFixedBasesFull.SpendAuthGBasereuses the existingspend_auth_g::{U, Z}full-scalar tables without change —pallas::Baseandpallas::Scalarare both 255-bit and the 85-window tables depend only on the generator and window layout, not the scalar kind.SpendAuthGShortuses new tablesspend_auth_g::{Z_SHORT, U_SHORT}(22 windows), added as ~270 lines of hex constants reproducible viahalo2_gadgets::ecc::chip::constants::find_zs_and_us.Breaking change to
OrchardFixedBasesThis is a SemVer-breaking change to the enum shape:
{Full(OrchardFixedBasesFull), NullifierK, ValueCommitV}{Full(OrchardFixedBasesFull), Base(OrchardBaseFieldBases), Short(OrchardShortScalarBases)}External callers pattern-matching on the old
NullifierK/ValueCommitVvariants (if any exist outside oforcharditself) will need to update toBase(OrchardBaseFieldBases::NullifierK)/Short(OrchardShortScalarBases::ValueCommitV).The
NullifierKandValueCommitVunit structs are retained with unchangedFixedPoint<pallas::Affine>impls and unchangedFromcoercions intoOrchardFixedBases, so code that uses them as marker types continues to compile. They are simply no longer theFixedPoints::Base/FixedPoints::ShortScalarassociated types.Internal rewiring
circuit::gadget::value_commit_orchardandcircuit::gadget::derive_nullifiernow useOrchardShortScalarBases::ValueCommitVandOrchardBaseFieldBases::NullifierKrespectively, because those are the typesFixedPointShort::from_innerandFixedPointBaseField::from_innernow expect. This is mechanical — the generator, U/Z tables, and window layout are all unchanged.Verification
cargo test --all-features --workspacepasses — 59 tests (up from 53, new routing tests added) + 1 integration test.Z_SHORT/U_SHORTtables are verified by the existingtest_zs_and_ushelper.circuit::tests::round_trip(regenerates a fresh proof and verifies it againstcircuit_proof_test_case.bin) passes unchanged.circuit::tests::serialized_proof_test_casepasses unchanged.cargo clippy --all-features -- -D warningspasses clean.Binary-identical action circuit — the main Orchard consensus circuit is unaffected:
diff -q src/circuit_description/ /path/to/orchard-0.11.0-pristine/src/circuit_description/ diff -q src/circuit_proof_test_case.bin /path/to/orchard-0.11.0-pristine/src/circuit_proof_test_case.bin # Both return no output (identical)Alternatives considered
Sibling crate
Discussed but rejected.
FixedPoints::BaseandFixedPoints::ShortScalarare single types perFixedPointsimpl. A sibling crate can't extend the set of Base/Short variants usable withorchard::constants::OrchardFixedBases; it would need to define its own parallelFixedPointsimpl with a separate enum, forking the ECC chip instance. That in turn means re-implementingderive_nullifier,value_commit_orchard,CommitIvkChip,NoteCommitChip, etc. — all parameterized byFixedPoints = OrchardFixedBases. ~500 lines of duplication that defeats the purpose.The new variants are named after the underlying math, not after voting-specific use cases. Any protocol needing a short scalar on
G^Orchardbenefits, not just voting.Full 85-window path for
[v_i]*G(fallback if this PR is rejected)If this enum refactor is not acceptable upstream, voting-circuits can fall back to using the full 85-window path for
[v_i]*G. This stays within the K=13 budget but reduces the headroom from ~57% to ~53% (3,512 → 3,827 high-water mark). The savings is "comfort margin" rather than strictly necessary. Voting-circuits would need no orchard changes for this fallback — just a two-line Cargo.toml switch to use the existingFixedPointBaseField/NullifierKrouting plus variable-base mul forG.Scope
Part of a 2-PR series targeting
voting-circuits/v0.11.0:assign_constanthelperSpendAuthGshort-scalar + base-field supportDepends on PR A merging first (conflicts on
src/circuit/gadget.rs). Draft — not for merge until both PRs are reviewed as a series.