Terminology
The key words "MUST", "REQUIRED", "MUST NOT", "SHOULD", and "MAY" in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.
The character § is used when referring to sections of the Zcash Protocol Specification. 2
The terms below are to be interpreted as follows:
- Ballot
- A unit of voting weight derived from a zatoshi balance. The conversion from zatoshi to ballots is defined in [Ballot Scaling].
- Vote Authority Note (VAN)
- A commitment inserted into the Vote Commitment Tree that represents spendable voting authority. A VAN binds a voting hotkey, a ballot count, a voting round identifier, and a proposal authority bitmask.
- Vote Commitment (VC)
- A commitment inserted into the Vote Commitment Tree that binds a voter's encrypted share distribution, proposal choice, and vote decision for a single proposal. A VC is created when a VAN is consumed to cast a vote.
- Vote Commitment Tree (VCT)
- An append-only Poseidon Merkle tree maintained by the vote chain that stores both VANs and VCs as leaves. A separate VCT is maintained per voting round; trees from different rounds are fully isolated.
- Vote share
- One of encrypted portions of a voter's ballot count within a Vote Commitment. Each share is submitted independently for homomorphic accumulation.
- Voting round
- A bounded period during which a set of proposals are open for voting. Each round is identified by a unique and is associated with a pool snapshot, an election authority public key, and a set of proposals.
- Election authority (EA)
- The set of validators that collectively hold Shamir shares of the El Gamal secret key for a voting round.
- Submission server
- An untrusted server to which a voter delegates the construction and submission of Vote Reveal Proofs.
- Governance hotkey
- An Orchard key hierarchy, distinct from the holder's spending key, that provides the key material for all voting operations after delegation. The hotkey is generated on a general-purpose device capable of ZKP construction.
- Governance nullifier
- An alternate nullifier (as defined in 3) scoped to the governance domain, published during delegation to prevent double-delegation of the same Orchard note within a voting round.
- VAN nullifier
- A nullifier derived from a VAN commitment and published when the VAN is consumed (to cast a vote or delegate), preventing double-spending of voting authority.
- Share nullifier
- A nullifier derived from a vote commitment and share index, published when a share is revealed, preventing double-counting.
Abstract
This ZIP specifies a shielded voting protocol that allows holders of Orchard notes to cast stake-weighted votes on proposals without revealing their identity, individual balances, or vote allocations.
The protocol proceeds in three proving phases. First, a delegation proof (building on the Orchard Proof-of-Balance 3) converts proven Orchard balance into a Vote Authority Note on a purpose-built vote chain. Second, a vote proof consumes a VAN to produce a Vote Commitment containing El Gamal-encrypted shares of the voter's ballot count, split across vote options. Third, a vote reveal proof, constructed by an untrusted submission server, opens individual encrypted shares for homomorphic accumulation, without revealing which Vote Commitment the share originated from.
After the voting window closes, anyone can publicly aggregate the revealed El Gamal ciphertexts per proposal option. Validators holding Shamir shares of the election authority key cooperate to produce partial decryptions; the results are combined via Lagrange interpolation and the aggregate total is publicly verified. Individual vote amounts are never revealed.
Motivation
Stake-weighted voting in privacy-preserving systems faces a fundamental tension: demonstrating voting power requires proving a balance, but linking that balance to a vote destroys the privacy that shielded transactions provide.
This ZIP addresses that tension for Zcash's Orchard shielded pool. The Orchard Proof-of-Balance 3 provides the foundational primitive, proving note ownership without revealing standard nullifiers. This ZIP builds on that primitive to specify a complete voting protocol with the following properties:
- Unlinkable delegation. A holder delegates voting power to a locally-generated hotkey via a zero-knowledge proof. The delegation is unlinkable to the holder's on-chain identity.
- Private vote splitting. Votes are decomposed into El Gamal- encrypted shares submitted independently, preventing balance reconstruction via timing or amount analysis.
- Homomorphic tallying. Encrypted shares are accumulated on-chain via component-wise point addition. Only the aggregate total per proposal option is ever decrypted.
The protocol is motivated by coinholder governance in the Zcash ecosystem, where participants vote on proposals weighted by their ZEC holdings. The same mechanism applies to any stake-weighted polling system over an Orchard-like shielded pool.
Privacy Implications
Unlinkability to on-chain identity. The delegation phase moves voting authority from the holder's Orchard spending key to an unlinkable governance hotkey. All subsequent voting transactions use this hotkey. An observer who sees both the governance nullifiers (published during delegation) and the standard nullifiers (published when notes are later spent on-chain) cannot link them without knowledge of . This follows from the alternate nullifier unlinkability property established in 3.
Balance hiding via vote splitting. A voter's total ballot count is decomposed into shares encrypted under the election authority's public key. Each share is submitted independently (potentially via different submission servers at randomized delays), preventing an observer from reconstructing the voter's total weight from individual submissions.
Individual vote amounts hidden. Each share is an El Gamal ciphertext whose plaintext value is never revealed. Only the aggregate total per (proposal, decision) pair is decrypted at tally time.
Vote commitment unlinkability. The Vote Reveal Proof proves that a revealed share belongs to some valid Vote Commitment in the VCT without revealing which one. Blinded per-share commitments prevent observers from recomputing from on-chain ciphertexts and linking revealed shares back to a specific VC.
Trust assumptions. After the key ceremony, no single party holds ; each validator holds only a Shamir share. An adversary must compromise at least validators (where ) to reconstruct the key and decrypt individual share ciphertexts. Even with access to the full key, privacy against the EA relies on vote splitting: the EA would see encrypted shares but cannot link them to specific voters or vote commitments. Submission servers learn the encrypted share ciphertext, blind factor, and blinded share commitments for each share they submit, along with the proposal identifier and vote decision, but cannot decrypt plaintext amounts or link shares to voter identities. The primary trust requirement on submission servers is not leaking timing metadata; using multiple independent servers with randomized delays for submission mitigates this risk. See 4.
Non-membership tree queries. Obtaining exclusion proofs for the nullifier non-membership tree during delegation requires querying a data source. Private information retrieval mitigates this leakage; see 5.
Requirements
- A holder's on-chain identity (spending key, standard nullifiers) is not linkable to their voting activity.
- The number of user-facing signatures required for delegation is minimized.
- No double-delegation for the same note within a voting round.
- No double voting for the same voting share within the same proposal.
- Individual vote amounts are not revealed at any point; only aggregate totals per (proposal, decision) pair are recoverable.
- The aggregate tally is publicly verifiable: any party can confirm the homomorphic accumulation.
- The delegation phase is compatible with hardware wallets that support only the standard Orchard PCZT 6 signing flow, without requiring firmware changes specific to the voting protocol.
Non-requirements
- The consensus mechanism and operational parameters of the vote chain.
- The election authority key ceremony (generation, threshold sharing, and distribution of shares) is specified separately in 7.
- The operational process for conducting a coinholder vote (validator setup, poll creation, deadlines) is out of scope; it is specified in 8.
- Post-quantum security of the El Gamal encryption layer is out of scope.
- Privacy-preserving retrieval of nullifier non-membership proofs is specified separately in 5.
High-level summary
This section is non-normative.
The protocol proceeds in five phases within a voting round.
Phase 1: Delegation. A holder proves ownership of unspent Orchard notes at a pool snapshot (using the Claim circuit from 3) and delegates voting authority to a locally-generated governance hotkey. The delegation produces a Vote Authority Note (VAN) that is inserted into the Vote Commitment Tree on the vote chain. Governance nullifiers are published to prevent double-delegation.
Phase 2: Voting. The governance hotkey consumes a VAN by publishing its VAN nullifier, and produces two new VCT leaves: a replacement VAN with the voted proposal's authority bit cleared, and a Vote Commitment containing El Gamal-encrypted shares of the voter's ballot count.
Phase 3: Share submission. The voter sends each encrypted share as an independent payload to one or more submission servers 4. Each payload includes a client-chosen timestamp and the data necessary for the server to construct a Vote Reveal Proof. If the voter is casting near the end of the voting window (within the last-moment buffer defined in 4), the voter places the full ballot count into a single share and requests immediate submission.
Phase 4: Share reveal. Each submission server constructs a Vote Reveal Proof (proving the share belongs to a valid VC in the VCT without revealing which one) and submits it to the vote chain at the client-specified time. The chain accumulates the revealed El Gamal ciphertexts homomorphically.
Phase 5: Tally. After the voting window closes, at least validators produce partial decryptions of the aggregate ciphertext per (proposal, decision) pair. The partial decryptions are stored on-chain and combined via Lagrange interpolation to recover the total ballot count (via the bounded discrete-log recovery procedure defined in 7). Correctness is publicly verifiable: anyone can recompute the Lagrange combination from the on-chain partial decryptions.
Specification
El Gamal Encryption on Pallas
The protocol uses additively homomorphic El Gamal encryption over the Pallas curve to encrypt vote share amounts. The scheme — including setup, encryption, additive homomorphism, and decryption — is defined in 7. This ZIP uses the notation established there: is the Pallas generator 9, is the election authority's public key for the current voting round, and a ciphertext is a pair of Pallas points where .
Data Structures
Voting Round Identifier
Each voting round is identified by a 32-byte , derived deterministically from the round's setup parameters by Poseidon hashing. Validators and verifiers compute it from the same inputs and check that it matches the on-chain identifier.
The hash uses the variant of the Poseidon instantiation specified in [Poseidon Instantiation].
where:
- — the Zcash mainnet block height of the chosen snapshot, encoded as a Pallas base field element.
- — the low and high 128-bit halves of in little-endian byte order, each encoded as a Pallas base field element.
- — the low and high 128-bit halves of in little-endian byte order.
- — Unix timestamp (seconds) after which votes are no longer accepted, encoded as a Pallas base field element.
- — root of the nullifier non-membership IMT at the snapshot height. MUST be a canonical Pallas base field element.
- — Orchard note commitment tree root at the snapshot height. MUST be a canonical Pallas base field element.
and are split into two 128-bit limbs because they are not necessarily canonical Pallas field elements. and are themselves Poseidon-derived in their respective trees and are therefore already canonical.
Vote Authority Note (VAN)
A VAN represents spendable voting authority on the vote chain. Its commitment is computed in two layers:
The first layer binds the structural fields; the second layer blinds the commitment with randomness. These are two separate Poseidon invocations ( then ), not a single 7-input sponge absorption.
where:
- — domain tag distinguishing VANs from VCs in the shared VCT.
- — of the diversified base of the governance hotkey address.
- — of the diversified transmission key of the governance hotkey address.
- — total voting weight in ballots.
- — scopes this VAN to a specific voting round.
- — bitmask encoding which proposals this VAN is authorized to vote on. values 1–15 map to bits 1–15; bit 0 is reserved (see [Why Proposal Identifiers Start at 1]). Full authority is .
- — commitment randomness.
VAN nullifier. When a VAN is consumed (to vote or delegate), its nullifier is:
where is the field-element encoding of the domain
separator "vote authority spend" and is the nullifier
deriving key from the governance hotkey's full viewing key.
A VAN MUST be created during delegation (Phase 1) and consumed during voting (Phase 2), which MUST produce a replacement VAN with updated .
The VAN model is designed to support future extensions such as partial delegation (splitting across multiple delegates), but this ZIP specifies only the delegation and voting operations.
Vote Commitment (VC)
A VC commits to a vote on a specific proposal:
where:
- — domain tag.
- — a hash over blinded commitments to all encrypted shares (see [Shares Hash]).
- — which proposal this vote targets.
- — the voter's choice (0-indexed into the proposal's declared options).
A VC MUST be created during voting (Phase 2) and opened during share reveal (Phase 4/5).
The VC hash MUST be posted on-chain as a public input of the Vote Proof and inserted into the VCT.
Its preimage fields ( and ) MUST be private witnesses in that proof.
During share reveal, the Vote Reveal Proof MUST prove membership in the VCT without exposing which VC is being opened.
Vote Share
A vote share is one of encrypted portions of a voter's ballot count within a VC. The value 16 is chosen as a sufficiently high number to ensure amount privacy through share decomposition. This ZIP constrains only that the shares sum to and that each share is in ; the strategy for how the client decomposes the ballot count across shares (e.g., uniform random vs. powers-of-two denominations) affects temporal mixing effectiveness and is specified in 4.
For share index :
- — plaintext share amount in ballots (private, never revealed).
- — El Gamal encryption randomness (Pallas scalar, private).
- — per-share blind factor for the blinded share commitment (independent of ).
- — El Gamal ciphertext.
Blinded share commitment:
where , denote the -coordinates and , denote the -coordinates of the ciphertext points. See [Why Share Commitments Bind Full Curve Points].
Shares Hash
The shares hash aggregates all blinded share commitments:
This is an -input Poseidon sponge hash.
Share Nullifier
When a share is revealed, its nullifier is:
where is the field-element encoding of
"share spend", is the vote commitment (private), and
is the blind factor for the revealed share.
The share nullifier MUST be posted on-chain as a public input of the Vote Reveal Proof and added to the share nullifier set to prevent double-counting.
Vote Commitment Tree
The vote chain MUST maintain a separate VCT per voting round. Each round's VCT MUST be an incremental Merkle tree 10 of depth that stores both VANs and VCs as leaves. Leaves, roots, and tree state from one round MUST NOT carry over into another. The tree MUST use the same append-only data structure as the Orchard note commitment tree, but with Poseidon over the Pallas scalar field for internal node hashing instead of Sinsemilla (see [Poseidon Instantiation]).
Domain separation between VANs and VCs is achieved structurally: the first Poseidon input is for VANs and for VCs, making it impossible for a valid VAN preimage to produce the same hash as a valid VC preimage.
Leaves MUST be inserted in transaction order: a delegation transaction inserts one VAN; a vote transaction inserts both a new VAN and a VC.
Nullifier Sets
The vote chain MUST maintain three disjoint nullifier sets:
- Governance nullifiers: prevent double-delegation of mainchain Orchard notes within a voting round.
- VAN nullifiers: prevent double-spending of voting authority.
- Share nullifiers: prevent double-counting of revealed shares.
Each set SHOULD BE append-only within a voting round. The vote chain MUST reject any transaction that publishes a nullifier already present in the corresponding set.
The protocol defines three tags:
| Tag constant | String | Length |
|---|---|---|
"governance authorization" | 24 bytes | |
"vote authority spend" | 20 bytes | |
"share spend" | 11 bytes |
Governance Hotkey
The governance hotkey is a separate Orchard key hierarchy generated on a general-purpose device (e.g., a mobile phone). It is distinct from the holder's Orchard spending key, which may reside on a hardware wallet. The hotkey provides the key material for all voting operations after delegation.
A governance hotkey consists of:
- A fresh spend-authorizing key , from which is derived.
- A nullifier deriving key , derived from the hotkey's spending key via the standard Orchard key hierarchy.
- A CommitIvk trapdoor , derived from the hotkey's spending key.
- A diversified address at a chosen diversifier index, consisting of the diversified base point and the diversified transmission key point, where the transmission key satisfies with . The VAN commitment (see [Vote Authority Note (VAN)]) stores the -coordinates and ; the full curve points are used in the diversified address integrity check (see [Vote Proof]).
The Delegation Proof does not constrain the hotkey address to match the holder's key; the output address is bound to the delegation transitively through the VAN commitment and the rho binding (see [Delegation Proof]), which the holder's hardware wallet authenticates via the spend authorization signature.
The hotkey MUST be derived deterministically from a seed . The wallet MUST generate by creating a fresh BIP 39 mnemonic 11, converting it to a 64-byte BIP 39 seed, and storing the mnemonic in local secure storage (e.g., the platform keychain). The mnemonic MUST be generated independently of the holder's wallet mnemonic and MUST NOT be exported or backed up; it is needed only for the duration of the voting round.
The wallet computes
interpreted as a Pallas scalar via , and then derives , , and from following § 4.2.3 'Orchard Key Components' 12. See [Why Deterministic Hotkey Derivation].
Per-Vote Secret Derivation
The El Gamal encryption randomness , per-share blind factors , and spend authorization randomizer used in the Vote Proof MUST be derived deterministically from the hotkey seed. This ensures that all per-vote secrets can be reconstructed from the BIP 39 mnemonic if the app is terminated between delegation and voting.
For a vote on proposal consuming VAN commitment in voting round , the wallet MUST derive a 64-byte per-vote root:
where denotes concatenation of canonical little-endian encodings (32 bytes for field elements, 1 byte for ).
From , the wallet MUST derive each per-share secret:
where is the share index encoded as a single byte. The spend authorization randomizer is:
All Blake2b invocations above use the first argument as the personalization parameter (truncated or padded to 16 bytes per the Blake2b spec) and the second argument as the input message. interprets a 64-byte input as a Pallas scalar. 13
See [Why Deterministic Hotkey Derivation].
Ballot Scaling
Orchard note values are denominated in zatoshi. The Delegation Proof MUST convert zatoshi to ballots:
where are the values of the delegated Orchard notes. One ballot equals 0.125 ZEC.
The prover MUST witness and a remainder . The Delegation Proof circuit MUST enforce:
- (see [Why a 24-bit Remainder Range])
The 30-bit upper bound accommodates up to million ZEC, well above the 21 million ZEC supply cap. The minimum of 1 ballot ensures that holdings below 0.125 ZEC MUST NOT produce voting authority.
Delegation Phase
Delegation Proof
The Delegation Proof establishes that a holder owns unspent Orchard notes at a pool snapshot and converts the proven balance into a VAN on the vote chain.
The Delegation Proof circuit MUST enforce the same per-note ownership checks (note commitment integrity, Merkle path validity, nullifier derivation, diversified address integrity, and nullifier non-membership) as the Batched Claim circuit defined in 3. This section specifies only the conditions that extend beyond the Balance Proof.
Public Inputs
Given a primary input:
- — nullifier of the dummy signed note (for spend authorization binding).
- — randomized spend authorization verification key.
- — Orchard note commitment tree root at the snapshot height.
- — nullifier non-membership tree root at the snapshot height.
- — the initial VAN commitment.
- — governance nullifiers for each note slot.
- — output note commitment to the governance hotkey address (PCZT scaffolding; not inserted into any on-chain tree).
Auxiliary Inputs
The prover knows, in addition to the per-note witnesses defined in 3:
- — governance hotkey diversified address.
- — VAN commitment randomness.
- Signed note data: (value = 0).
- Output note data (PCZT scaffolding): (address = hotkey). is not constrained by the circuit.
Conditions
Per-note conditions (5 note slots, with padding). For each note , the circuit MUST enforce the following conditions from the Batched Claim circuit 3:
- Note commitment integrity.
- Merkle path validity in (skipped for padded notes).
- Diversified address integrity (same MUST own all notes).
- Standard nullifier derivation (kept private).
- Nullifier non-membership in (skipped for padded notes).
- Padded notes MUST have value 0.
Governance nullifier derivation. For each real note , the circuit MUST enforce:
where is the field-element encoding of "governance authorization" and is the note's standard nullifier (computed in-circuit but never revealed). This is an instantiation of the alternate nullifier derivation defined in 3, with and .
Signed note integrity. The signed note MUST be a dummy note with value 0. The circuit MUST enforce that is correctly constructed and that is correctly derived from it.
Rho binding. The circuit MUST enforce that is deterministically bound to the delegation context:
This makes the spend authorization signature non-replayable and scoped to the exact delegation context.
Spend authority. The circuit MUST enforce that .
Diversified address integrity for signed note. The circuit MUST enforce that the signed note's address belongs to .
Output note commitment. The circuit MUST enforce that is a correctly constructed note commitment to an output note addressed to the governance hotkey. The output note exists solely to satisfy the PCZT signing flow: a standard Orchard Action requires exactly one spend and one output, so the governance PCZT includes a minimal output to the hotkey address.
The vote chain SHOULD NOT insert into any commitment tree or use it beyond proof verification; it serves only to make the hardware wallet's signed sighash commit to a complete Action structure.
See 3 for the PCZT construction details and [Why a Dummy Signed Note] for the design rationale.
VAN integrity. The circuit MUST enforce that the public VAN commitment matches the claimed governance hotkey, ballot count, round, and full proposal authority (using the two-layer construction defined in [Vote Authority Note (VAN)]):
where .
Ballot scaling. The circuit MUST enforce that with , as defined in [Ballot Scaling].
Out-of-Circuit Verification
A verifier that receives a Delegation Proof together with a spend authorization signature MUST perform the following checks:
- Verify against the public inputs.
- Verify that is exactly 32 bytes.
- Verify as a valid signature on , under .
- Verify that and correspond to the published pool snapshot for .
- Verify that no appears in the governance nullifier set. If any does, reject as a double-delegation.
- Verify that matches an active round.
- Insert into the VCT.
- Add all to the governance nullifier set.
Delegation Sighash
The delegation sighash is a client-provided 32-byte value included in the delegation message. For hardware wallet flows, the client computes it as the ZIP 244 14 shielded transaction sighash of a governance PCZT 6; the construction of this PCZT is specified in 3. For software wallets, the client signs the sighash directly without PCZT construction.
The vote chain verifies the against this client-provided sighash without recomputing it. The sighash does not need to be independently reconstructible by the vote chain because the Delegation Proof provides the governance data binding: the ZKP proves that is a valid rerandomization of the holder's spend authorization key, that the holder owns the claimed notes, and that the VAN commitment is correctly constructed. An attacker who substitutes a different sighash cannot produce a valid signature under without knowledge of the holder's spending key.
Delegation Message
A delegation transaction submitted to the vote chain MUST contain:
| Field | Type | Description |
|---|---|---|
| Proof | The Delegation Proof | |
| Signature | SpendAuthSig under | |
| 32 bytes | Client-computed sighash (see [Delegation Sighash]) | |
| Pallas scalar | Dummy note nullifier | |
| Pallas point | Randomized verification key | |
| Pallas scalar | Note commitment tree root | |
| Pallas scalar | Non-membership tree root | |
| Pallas scalar | VAN commitment | |
| Pallas scalar each | Governance nullifiers | |
| Pallas scalar | Round identifier | |
| Pallas scalar | Output note commitment |
Vote Phase
Vote Proof
The Vote Proof demonstrates that a holder of a valid VAN is casting a vote: consuming the old VAN, producing a new VAN with decremented proposal authority, and constructing a Vote Commitment that binds El Gamal-encrypted shares to the chosen proposal and decision. The Vote Proof circuit MUST enforce all conditions specified below.
Public Inputs
Given a primary input:
- — nullifier of the old VAN (prevents double-voting).
- — randomized voting public key.
- — the new VAN commitment with decremented proposal authority.
- — the vote commitment.
- — root of the Vote Commitment Tree.
- — VCT snapshot height.
- — which proposal.
- — election authority public key (x and y coordinates).
Auxiliary Inputs
The prover knows:
- — the voter's choice.
- — voting spend authorization validating key.
- — nullifier deriving key from the governance hotkey's full viewing key.
- — CommitIvk randomness for the voting key.
- — spend authorization randomizer for the voting hotkey.
- — diversified base point from the VAN (full point; applied for VAN integrity hash).
- — diversified transmission key point from the VAN (full point; applied for VAN integrity hash).
- — total voting weight in ballots.
- , — old and new bitmasks.
- — VAN commitment randomness (shared between old and new VAN).
- — Merkle proof for the old VAN in the VCT.
- — old VAN commitment.
- — plaintext share values.
- — El Gamal encryption randomness per share.
- — per-share blind factors.
Conditions
VAN Ownership and Spending
Condition 1: Merkle tree membership. The circuit MUST enforce that the old VAN exists in the VCT: MUST be a valid Merkle path of depth from to the anchor , using Poseidon for internal node hashing.
Condition 2: Old VAN integrity. The circuit MUST enforce that the old VAN commitment matches the claimed fields (using the two-layer construction defined in [Vote Authority Note (VAN)]). The values in the Poseidon input are x-coordinates, i.e., of the corresponding full points:
Condition 3: Diversified address integrity. The circuit MUST enforce that the VAN's address belongs to the voting key:
where 15.
Condition 4: Spend authority. The circuit MUST enforce that the randomized voting public key is a valid rerandomization:
Condition 5: VAN nullifier. The circuit MUST enforce that the public is correctly derived:
where is the field-element encoding of
"vote authority spend".
See [Why VAN Nullifier Domain Separation].
New VAN Construction
Condition 6: Proposal authority decrement. The circuit MUST enforce that bit is cleared in the authority bitmask:
- MUST be decomposed into 16 boolean wires that recompose to the original value.
- The bit at position MUST be 1 (the voter has authority for this proposal).
- MUST be the recomposition with bit cleared; all other bits MUST be unchanged.
Condition 7: New VAN integrity. The circuit MUST enforce that the new VAN is correctly constructed (using the two-layer construction defined in [Vote Authority Note (VAN)]):
The new VAN MUST reuse the old VAN's diversified address and commitment randomness; only changes.
Vote Commitment Construction
Condition 8: Shares sum correctness. The circuit MUST enforce:
Condition 9: Shares range check. The circuit MUST enforce that each share is bounded:
This bound is critical for two reasons: (1) it ensures the base-field share sum and the scalar-field El Gamal encoding agree (no modular reduction in either field), and (2) it keeps the aggregate discrete log small enough for efficient recovery at tally time (see 7).
Condition 10: Shares hash integrity. The circuit MUST enforce that the blinded share commitments and their aggregate hash are correctly computed:
Condition 11: El Gamal encryption integrity. The circuit MUST enforce that each ciphertext is a valid encryption of its share under :
The circuit MUST constrain equality on both the - and -coordinates of the computed and witnessed ciphertext points. The -coordinates are needed because they appear in the share commitment (condition 10); the ECC gadget already produces full curve points, so no additional decomposition cost is incurred. See [Why Share Commitments Bind Full Curve Points].
Condition 12: Vote commitment integrity. The circuit MUST enforce that the public vote commitment matches the private vote details:
Out-of-Circuit Verification
A verifier that receives a Vote Proof together with a vote spend authorization signature MUST perform the following checks:
- Verify against the public inputs.
- Compute the vote sighash from the message fields (see [Vote Sighash]).
- Verify as a valid signature on the vote sighash, under .
- Verify that does not appear in the VAN nullifier set. If it does, reject as double-voting.
- Verify that refers to a VCT state within the current voting round and that matches the VCT root at that height.
- Verify that is valid for the current round and within the voting window.
- Verify that matches an active round.
- Verify that matches the published election authority public key for this round.
- Insert and into the VCT.
- Add to the VAN nullifier set.
Note: is a private witness in the Vote Proof and MUST NOT be validated out-of-circuit at this stage.
It is validated when the share is revealed: the Vote Reveal Proof makes a public input, and the vote chain checks its validity at that point (see [Vote Reveal Proof] out-of-circuit step 6).
Vote Sighash
The vote sighash is computed by the vote chain from the vote message fields. Unlike the delegation sighash, it is not client-provided; the governance hotkey is software-controlled and signs the same chain-computable digest.
The vote sighash is using an unkeyed BLAKE2b-256 hash (no personalization parameter). is the concatenation of a domain prefix followed by the message fields, each zero-padded to 32 bytes:
| Component | Encoding | Size |
|---|---|---|
"SVOTE_CAST_VOTE_SIGHASH_V0" | raw ASCII bytes | 26 |
| field element, zero-padded to 32 | 32 | |
| compressed point, zero-padded to 32 | 32 | |
| field element, zero-padded to 32 | 32 | |
| field element, zero-padded to 32 | 32 | |
| field element, zero-padded to 32 | 32 | |
| 4-byte LE integer, zero-padded to 32 | 32 | |
| 8-byte LE integer, zero-padded to 32 | 32 |
Total payload: 250 bytes. Pallas field elements and compressed points use their canonical little-endian encoding 13, right-padded with zero bytes to fill 32 bytes when shorter. Integer fields are encoded as unsigned little-endian and right-padded with zero bytes to 32 bytes.
The voter's client MUST compute the same digest and sign it with the governance hotkey's spend-authorizing key (rerandomized by ) before submitting the vote message.
Vote Message
A vote transaction submitted to the vote chain MUST contain:
| Field | Type | Description |
|---|---|---|
| Proof | The Vote Proof | |
| Signature | SpendAuthSig under | |
| Pallas scalar | Old VAN nullifier | |
| Pallas point | Randomized voting public key | |
| Pallas scalar | New VAN commitment | |
| Pallas scalar | Vote commitment | |
| Pallas scalar | VCT root | |
| integer | VCT anchor height | |
| Proposal identifier | ||
| Pallas scalar | Round identifier | |
| Pallas point | EA public key |
Share Reveal Phase
Vote Reveal Proof
The Vote Reveal Proof opens a single encrypted share from a Vote Commitment, revealing the El Gamal ciphertext for homomorphic accumulation, without revealing the plaintext amount or which Vote Commitment the share came from. This proof is constructed by the submission server, not the voter. The Vote Reveal Proof circuit MUST enforce all conditions specified below.
Public Inputs
Given a primary input:
- — prevents double-counting.
- — the - and -coordinates of the El Gamal ciphertext points for this share.
- — which proposal.
- — the voter's choice.
- — root of the Vote Commitment Tree.
Auxiliary Inputs
The prover (submission server) knows:
- — the vote commitment being opened (hidden from the verifier).
- — Merkle proof for the VC in the VCT.
- — which share is being revealed.
- — all blinded share commitments (to recompute ).
- — the blind factor for the revealed share (at position ).
Conditions
Vote Commitment Membership
Condition 1: Merkle tree membership. The circuit MUST enforce that the VC exists in the VCT: MUST be a valid Merkle path from to , without revealing which leaf. The VC value is a private witness.
Condition 2: Vote commitment integrity. The circuit MUST enforce that the VC is correctly constructed from its components:
This binds the public and to the private VC, ensuring that the revealed share is attributed to the correct proposal and decision.
Share Opening
Condition 3: Shares hash integrity. The circuit MUST enforce that is recomputed from the witness share commitments:
The recomputed MUST equal the one inside the VC (via condition 2). The share commitments are blinded (see [Why Blinded Share Commitments]), so they do not reveal the ciphertexts or blind factors of other shares to the prover.
Condition 4: Share membership. The circuit MUST enforce that the commitment derived from the public ciphertext coordinates and the witness blind factor matches the share commitment at position :
The circuit MUST encode as a one-hot selector vector over positions. The mux MUST extract the corresponding via a dot product and constrain equality with the commitment derived from the public ciphertext coordinates and the witness blind factor. Only the blind factor for the revealed share is needed; the remaining share commitments used in condition 3 are opaque witnesses that do not expose their underlying ciphertexts or blind factors.
Nullifier
Condition 5: Share nullifier. The circuit MUST enforce that the public is correctly derived:
where is the field-element encoding of
"share spend" and is the blind factor for the
revealed share. The VC and blind are private, making the nullifier
unlinkable to a specific VC without knowledge of these witnesses.
Out-of-Circuit Verification
A verifier that receives a Vote Reveal Proof MUST perform the following checks:
- Verify against the public inputs.
- Verify that does not appear in the share nullifier set. If it does, reject as double-counting.
- Verify that matches a published VCT root.
- Verify that is valid for the current round.
- Verify that matches an active round.
- Verify that is a valid option for the proposal identified by in this round.
- Add to the share nullifier set.
- Accumulate into the aggregate ciphertext for :
where denotes component-wise Pallas point addition.
Share Reveal Message
A share reveal transaction submitted to the vote chain MUST contain:
| Field | Type | Description |
|---|---|---|
| Proof | The Vote Reveal Proof | |
| Pallas scalar | Share nullifier | |
| El Gamal ciphertext (two Pallas points) | ||
| Proposal identifier | ||
| Pallas scalar | Vote decision | |
| Pallas scalar | VCT root | |
| Pallas scalar | Round identifier |
Note: the Vote Reveal Proof has no spend authorization signature because it is constructed by the submission server, not the voter.
Share Submission Payload
The voter sends each share to a submission server as an off-chain payload. For share , the payload MUST contain:
| Field | Description |
|---|---|
| The vote commitment | |
| VCT position | Position of in the VCT |
| Hash of all blinded share commitments | |
| Proposal identifier | |
| Vote decision | |
| Which share to reveal (0-indexed) | |
| El Gamal ciphertext for this share | |
| Blind factor for this share | |
| All blinded share commitments | |
| Unix timestamp (seconds) for when the server should submit the share reveal transaction. 0 means immediate (last-moment mode). See 4 |
The server receives only the ciphertext and blind factor for the single share it is responsible for revealing. The remaining shares are sent to other servers, each of which receives only its own share's raw data. To recompute in condition 3 of the Vote Reveal Proof, the server uses the blinded share commitments, which do not expose the ciphertexts or blind factors of the other shares (see [Why Per-Server Share Isolation]).
Voters MUST distribute shares across multiple independent servers to further limit any single server's view of their voting activity. Server selection, temporal mixing, and communication protocols are specified in 4.
Tally
After the voting window closes, the vote chain MUST decrypt the per- aggregate ciphertexts using the threshold decryption procedure specified in 7.
Vote Chain
The vote chain MUST maintain the following state per voting round:
- The Vote Commitment Tree as defined in [Vote Commitment Tree].
- Three disjoint nullifier sets as defined in [Nullifier Sets].
- A per- encrypted share accumulator: the running component-wise sum of revealed El Gamal ciphertexts.
For each transaction type, the vote chain MUST verify the corresponding proof and perform the out-of-circuit checks specified in [Delegation Proof], [Vote Proof], or [Vote Reveal Proof] respectively.
The vote chain's consensus mechanism, block structure, transaction encoding, and API are out of scope for this ZIP.
Poseidon Instantiation
All Poseidon hashes in this protocol use the same instantiation as the nullifier non-membership tree defined in 3: over (the Pallas base field), with S-box , width , rate , targeting 128-bit security, using the standard parameter generation procedure from 16.
Hashes with inputs use mode (absorbing field elements with length padding), absorbing two elements per permutation.
The following table lists every Poseidon call site in this protocol:
| Hash | Inputs () | Mode | Permutations |
|---|---|---|---|
| Voting round identifier | 8 | 4 | |
| VAN core | 6 | 3 | |
| VAN blinding | 2 | 1 | |
| VAN nullifier | 4 | 2 | |
| Vote commitment | 5 | 3 | |
| Blinded share commitment | 5 | 3 | |
| Shares hash | 16 | 8 | |
| Share nullifier | 4 | 2 | |
| VCT internal node | 2 | 1 | |
| Governance nullifier | 4 | 2 | |
| Rho binding | 7 | 4 |
Domain Separator Tags
Several constructions in this protocol use a domain separator tag encoded as a Pallas scalar. Each tag is defined by a fixed ASCII string. To convert a tag string to a field element, interpret its byte representation as an unsigned little-endian integer:
where are the ASCII byte values of the string and is its length. All tag strings in this protocol are shorter than 32 bytes, so the resulting integer is less than and fits in without reduction.
Rationale
Why a Separate Vote Chain
Voting transactions (delegation proofs, vote proofs, share reveals) are not standard Zcash shielded transactions. They require a new commitment tree (the VCT), new nullifier sets, and an encrypted share accumulator with homomorphic aggregation. Implementing these as a sidechain avoids modifying the Zcash consensus layer and keeps the governance mechanism independent of mainchain upgrade cycles.
Why Poseidon for the VCT
The Orchard note commitment tree uses Sinsemilla 17 for Merkle hashing. The VCT uses Poseidon instead because all three ZKPs in this protocol require VCT Merkle membership proofs, and Poseidon operates natively on field elements, making it significantly more efficient inside Halo 2 18 arithmetic circuits than Sinsemilla (which is optimized for bitstring inputs). Since the VCT is new infrastructure with no backwards- compatibility constraint, the more circuit-efficient primitive is appropriate. This is the same rationale as for the nullifier non-membership tree in 3.
Why Ballot Scaling
Expressing vote values in ballots () rather than raw zatoshi reduces bit-width throughout the protocol: El Gamal scalar multiplications are faster, range checks are tighter, and the discrete-log recovery at tally time 7 has a smaller search space. The 0.125 ZEC minimum also prevents dust delegations from bloating vote chain state.
Why a 24-bit Remainder Range
The remainder in the ballot scaling constraint is range-checked to 24 bits (), which is wider than the divisor (). A prover can set , effectively shorting themselves one ballot. This is harmless: does not appear in any governance nullifier, so the only effect is the prover voting with slightly less weight than they could. The wider check avoids a custom non-power-of-two range check in circuit.
Why Delegation to a Hotkey
The protocol requires voters to produce Halo 2 zero-knowledge proofs (delegation proof, vote proof) and construct vote commitments, operations that demand general-purpose computation on private key material. Hardware wallets that custody Orchard spending keys cannot perform these operations: they support signature generation but not arbitrary-circuit ZKP construction.
Delegation to a governance hotkey resolves this by separating spend authorization from vote execution. The hardware wallet signs a single during delegation, authorizing the transfer of voting power to a software-controlled hotkey. All subsequent voting operations (VAN consumption, vote commitment construction, share submission) use the hotkey's key material and run on a general-purpose device.
Without this separation, hardware wallet users would need to either export spending keys to a software environment, negating the security benefit of hardware custody, or forgo participation in governance entirely. Delegation preserves the hardware wallet's role as the sole custodian of spending keys while enabling full participation in the voting protocol.
Why Deterministic Hotkey Derivation
The voting flow spans multiple app sessions: delegation (ZKP #1), one or more votes (ZKP #2), and vote signing. Each step requires the hotkey's spend-authorizing key. The per-vote secrets (, , ) are also derived deterministically from the hotkey seed via a domain-separated PRF (see [Per-Vote Secret Derivation]), so crash recovery extends to all secrets needed for vote construction.
Deriving every secret from a single seed means the wallet stores only a BIP 39 mnemonic in its keychain. If the app is terminated between delegation and voting, or between proposals, all key material is reconstructed from the mnemonic. Randomly sampled keys would require securely persisting each component (, , , and all per-vote and values) independently, and any storage failure would be unrecoverable.
Why VAN Nullifier Domain Separation
The VAN nullifier uses (the governance hotkey's
nullifier deriving key) as the Poseidon key, while the governance
nullifier uses (the holder's nullifier deriving key).
When the hotkey is a separate key from the holder's, these are distinct
field elements, and cross-circuit collision resistance follows from the
key difference alone. When the hotkey reuses the holder's key hierarchy
(e.g., in an all-software flow without hardware wallet separation),
and collision resistance relies on
the domain tags being distinct: "vote authority spend" and
"governance authorization" differ in both byte length and content,
producing distinct field elements. The domain tags provide
defense-in-depth in both cases.
Why 5 Notes per Delegation
The Delegation Proof fixes the note slot count at 5 (with padding for holders who have fewer notes). This choice balances wallet coverage against proof cost: empirical analysis of the Orchard shielded pool shows that over 90% of wallets hold 5 or fewer notes, so most holders can delegate their full balance with a single user-facing signature.
Each additional note slot adds a full set of per-note constraints to the circuit, increasing proving time. A higher slot count would cover marginally more wallets at a disproportionate cost in prover resources. Holders with more than 5 notes can perform multiple delegations, each covering up to 5 notes.
Why a Dummy Signed Note
The Delegation Proof includes a dummy signed note (value 0) whose rho is deterministically bound to the delegation context. This mechanism exists to obtain a from hardware wallets that support only the standard Orchard PCZT signing flow, without requiring firmware changes specific to governance.
The dummy signed note construction, PCZT signing flow, and design rationale (1-zatoshi display value, rho binding for non-replayability, ZIP 244 sighash reuse, and the trade-offs of a future custom signing protocol) are specified in 3.
Why Shares Per Vote
Splitting a vote into shares serves two purposes. First, it provides temporal unlinkability: shares are submitted independently at client-chosen times spread across the voting window, preventing an observer from attributing all shares to a single voter by timing correlation. Second, it limits the election authority's view: even if the EA decrypts individual ciphertexts, it sees only individual shares, not a voter's complete ballot allocation.
When a voter casts near the end of the voting window, the protocol falls back to single-share mode: the full ballot count is placed in one share and submitted immediately. This sacrifices both benefits above but ensures the vote is counted — each share requires the server to construct a computationally expensive Vote Reveal Proof, and with insufficient time remaining the server may not complete all proofs before the deadline. See 4 for the last-moment buffer definition and timing details.
Why Blinded Share Commitments
Each share commitment includes a random blind factor: . Without blinding, an observer could compute for each on-chain ciphertext and compare against the values committed in VCs, linking revealed shares back to specific vote commitments. The blind factor makes this reverse computation infeasible.
Why Share Commitments Bind Full Curve Points
On the Pallas curve, every -coordinate has two valid -values: and . If the share commitment bound only the -coordinates of the ciphertext points, a malicious block proposer could negate and by flipping the sign bits in their compressed encodings (2 bit flips) without changing the -coordinates. The negated ciphertext encrypts instead of under the same El Gamal key, so the tally would accumulate instead of , corrupting the election result. The Vote Reveal Proof would still verify because the share commitment — binding only -coordinates — would be unchanged.
Including both - and -coordinates in the share commitment hash binds each commitment to the exact curve point. Flipping a sign bit changes the -coordinate, producing a different , which cascades through Merkle root, invalidating the proof.
Full -coordinates are used rather than 1-bit sign values because the -cells are already available from the ECC gadget output in the Vote Proof circuit; extracting parity bits in-circuit would require a 255-bit field decomposition gadget, adding substantial constraint cost for no security benefit.
Why Per-Server Share Isolation
Each submission server receives only the ciphertext and blind factor for the single share it reveals, plus the blinded share commitments for all shares. It does not receive the raw ciphertexts or blind factors of shares assigned to other servers. This limits the data any single server can observe: even a compromised server learns only one share's plaintext-encrypted ciphertext and blind, not the full set. The blinded share commitments are sufficient for the server to recompute in the proof (condition 3), while the blinding prevents the server from correlating other shares' ciphertexts with their commitments.
Why Server-Delegated Share Reveal
The Vote Reveal Proof is constructed by the submission server rather than the voter's client for two reasons: mobile devices are unreliable for background ZKP computation, and server-side construction enables temporal mixing of shares from many voters. The trust requirement on the server is minimal: it learns encrypted shares and vote decisions but cannot decrypt amounts or link shares to identities.
Why Reusing VAN Address and Randomness
When a vote consumes a VAN and produces a new one, the new VAN reuses the old VAN's diversified address (, ) and commitment randomness (). Only changes. This is safe because VAN commitments are blinded Poseidon hashes - the shared fields are never externally observable (both old and new commitments appear as opaque field elements in the VCT), so address rotation would provide no additional unlinkability.
Why Proposal Identifiers Start at 1
The bitmask is 16 bits wide, but values start at 1, yielding 15 usable proposal slots rather than 16. Bit 0 is reserved.
This follows from how lookup arguments work in Halo 2. A lookup table that validates must include a default row that satisfies the lookup when the selector is inactive. The circuit uses the identity row for this purpose: when the selector , the lookup input evaluates to , which must be present in the table for the proof to verify. Because every inactive row matches this entry, cannot be used as a valid proposal identifier: a prover could trivially satisfy the lookup with even when no authority check is intended. An additional non-zero gate () provides defense-in-depth by rejecting on active rows.
Why Threshold Secret Sharing
See 7 for the rationale behind threshold secret sharing.
Why a Send-Based VAN Model
The Vote Proof consumes the old VAN and produces a new one with the voted proposal's authority bit cleared. This is a UTXO-style "send": each vote appends two leaves to the VCT (new VAN + VC) and requires the voter to hold a current Merkle path.
An alternative design was considered in which the VAN is eliminated entirely and delegation is performed by out-of-band sharing of a private key (hash-committed to on-chain). Under this model the Vote Proof would not consume or re-create a VAN, removing the proposal authority decrement step and the corresponding VCT growth. The primary appeal is reduced client sync overhead: voters would not need to track VAN re-insertions or maintain up-to-date Merkle paths for their own VANs.
The send-based model is retained because it preserves the ability to add partial delegation in a future extension, splitting across multiple delegates, each receiving a fraction of the holder's voting weight. The VAN's explicit ballot count and proposal authority bitmask are the data model that would enable this; a future VAN-to-VAN delegation proof could consume one VAN and produce two with subdivided ballot counts. Partial delegation is not specified in this ZIP but the VAN model keeps the design space open. Under the keysharing alternative, the delegate holds the full key and therefore the full voting weight; there is no in-protocol mechanism to subdivide it, and adding one later would require a fundamentally different data model.
The sync savings of the alternative are also smaller than they first appear: even without VAN re-creation, clients still need to update VCT Merkle paths for their Vote Commitments (which the submission server requires for the Vote Reveal Proof). The VAN model adds incremental path-update overhead but does not introduce a new category of sync obligation. If a future design change eliminated the need for clients to track VCT paths entirely (for example by moving Merkle path retrieval fully to the submission server), the tradeoff would shift in favor of removing the VAN. See [Open issues].
Why Classical El Gamal Rather Than Post-Quantum Encryption
See 7 for the rationale behind using classical El Gamal rather than post-quantum encryption. The key consequence for this protocol is that vote-amount privacy has a finite horizon tied to quantum computing timelines, while voter identity is unaffected (alternate nullifier unlinkability relies on Poseidon preimage resistance, not on El Gamal). Vote splitting across shares provides additional mitigation: a quantum adversary would recover individual shares rather than complete ballot allocations unless it also breaks the Poseidon-based blinded share commitments.
Why VCT Depth 24
The Orchard note commitment tree uses depth 32, supporting (~4.3 billion) leaves. Governance voting produces far fewer leaves: each voter generates one VAN per delegation and two leaves (a new VAN plus a VC) per vote. Even 10,000 voters each voting on 50 proposals produce roughly 1 million leaves, well within the (~16.7 million) capacity of a depth-24 tree. Because each voting round maintains its own VCT, this budget applies per round rather than accumulating across rounds.
The reduced depth saves constraint rows in every circuit that performs a Merkle membership proof (Vote Proof and Vote Reveal Proof), since each level adds a Poseidon hash region. Moving from 32 to 24 levels removes 8 hash regions per proof, reducing prover cost and verification time without any practical capacity risk.
Why Domain Tags in the VCT
Both VANs and VCs are leaves in the same Merkle tree. The domain tags (, ) as the first Poseidon input make it structurally impossible for a valid VAN preimage to produce the same hash as a valid VC preimage, regardless of the remaining inputs.
Deployment
This ZIP does not specify a consensus change to the Zcash mainchain. Deployment considerations are specific to the vote chain and will be addressed in the operational voting process ZIP.
construction.
Reference implementation
- 19 — Halo 2 circuits for the Delegation Proof, Vote Proof, and Vote Reveal Proof.
- 20 — Cosmos SDK vote chain implementing the VCT, nullifier sets, encrypted share accumulator, ceremony, tally, and submission server.
- 21 — PIR server and client for privately retrieving nullifier non-membership proofs.
- 22 — Client-side Rust library for proof generation, vote construction, tree synchronization, and governance PCZT construction.
Open issues
- A future custom signing protocol purpose-built for proof-of-balance could simplify the Delegation Proof circuit by removing the dummy signed note scaffolding and enable further improvements described in 3.
- Partial delegation (a VAN-to-VAN delegation proof that consumes one VAN and produces two with subdivided ) is enabled by the send-based VAN model but not specified in this ZIP. Specifying the circuit and transaction type would allow a holder to distribute voting weight across multiple delegates. See [Why a Send-Based VAN Model].
- A simplified non-send VAN model, replacing VAN consumption and re-creation with out-of-band key delegation, would remove the proposal authority decrement step from the Vote Proof and reduce per-vote VCT growth. This is currently not adopted because it forecloses partial delegation and clients still need VCT Merkle path updates regardless. If future design changes remove the client's need to track VCT paths (e.g., full server-side path retrieval), this tradeoff should be revisited. See [Why a Send-Based VAN Model].
- Open issues related to the EA key ceremony are tracked in 7.
- Open issues related to the submission server (share decomposition strategy, client confirmation via PIR, balance amendment, decision encryption) are tracked in 4.
- Open issues related to the balance proof are tracked in 3.
References
Footnotes
-
Information on BCP 14 — "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words" ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1] or later ↩
-
Orchard Proof-of-Balance ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16
-
Private Information Retrieval for Nullifier Exclusion Proofs ↩ ↩2
-
zcash/zips issue #693: Standardize a protocol for creating shielded transactions offline (PCZT) ↩ ↩2
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.7.1: Spend Authorization Signature (Orchard) ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 3.8: Note Commitment Trees ↩
-
M. Palatinus, P. Rusnak, A. Voisine, and S. Bowe, "BIP 39: Mnemonic code for generating deterministic keys", 2013 ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 4.2.3: Orchard Key Components ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.9.6: Pallas and Vesta ↩ ↩2
-
ZIP 244: Transaction Identifier and Signature Validation for v5 Transactions ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.9.4: CommitIvk ↩
-
Poseidon: A New Hash Function for Zero-Knowledge Proof Systems ↩
-
Zcash Protocol Specification, Version 2025.6.3 [NU6.1]. Section 5.4.1.9: Sinsemilla Hash Function ↩
-
S. Bowe, J. Grigg, and D. Hopwood, "Recursive Proof Composition without a Trusted Setup", 2019 ↩
-
valargroup/voting-circuits: Halo 2 ZKP circuits for shielded voting ↩
-
valargroup/vote-sdk: Cosmos SDK vote chain for shielded voting ↩
-
valargroup/vote-nullifier-pir: PIR system for nullifier non-membership proofs ↩
-
valargroup/librustvoting: Client-side voting library for proof generation and vote construction ↩