Proof System
Groth16 zk-SNARKs, circuit architecture, trusted setup, state transitions, and post-quantum outlook
Zera uses Groth16 zk-SNARKs -- the most succinct non-interactive zero-knowledge proof system in production -- to validate transactions while revealing nothing about amounts, senders, recipients, or note positions. Proofs are generated client-side via snarkjs and verified on-chain via Solana's alt_bn128 precompile.
1. Groth16 Protocol Overview
Given an arithmetic circuit C encoding a relation R(x, w) -- for example, "commitment C is in the Merkle tree with root R, and nullifier N is correctly derived" -- the Groth16 prover and verifier interact as follows:
(pk, vk) <- Setup(C) // trusted setup, once per circuitπ <- Prove(pk, x, w) // client-side, via snarkjsb <- Verify(vk, x, π) // on-chain, via alt_bn128 precompile
Why Groth16
Groth16 was chosen for Zera because it offers the smallest proof size and fastest verification time of any deployed SNARK system:
- Proof size: ~200 bytes (2 G1 points + 1 G2 point on BN254) -- smallest of any SNARK
- Verification: Single pairing equation check, constant time regardless of circuit complexity
- On-chain cost: ~200k compute units on Solana via the alt_bn128 precompile
- Trade-off: Requires a per-circuit trusted setup (powers of tau + phase 2), unlike universal/transparent systems like PLONK or STARKs
Security Properties
- Completeness: Honest prover with valid witness always produces an accepted proof
- Knowledge Soundness: Any prover that produces a valid proof must "know" the witness (under the KEA assumption on BN254)
- Zero-Knowledge: A simulator with the trapdoor can produce proofs indistinguishable from real ones -- no information about w leaks
Interactive Proof Flow
Explore how proofs flow through each of Zera's four circuit types. Click a circuit to see the step-by-step proof generation and verification pipeline:
Shield tokens by creating a Poseidon commitment in the Merkle tree
2. Four Circuit Types
Zera has four circuit types, each with its own (pk, vk) pair:
- Deposit: Proves the commitment is correctly formed from (amount, secret, blinding, asset, memo)
- Withdraw: Proves Merkle membership, correct nullifier derivation, and amount consistency
- Relayed Withdraw: Same as withdraw, plus relayer fee deduction and recipient designation
- Transfer: Proves spending of input notes and creation of output notes with value conservation
Circuit Architecture
Commitment Verification
Inside the circuit, the prover recomputes C = Poseidon(amount, secret, blinding, asset, memo[0..3]) and checks it matches the committed leaf. This costs ~250 R1CS constraints.
Merkle Path Verification
The circuit verifies a 24-level Merkle inclusion proof: starting from the commitment leaf, hashing up through 24 sibling nodes to reach the public root. ~6,000 R1CS constraints.
Nullifier Correctness
The circuit verifies N = Poseidon(secret, C) and exposes N as a public output. This ensures the nullifier is deterministically bound to the note and can be checked against the on-chain nullifier set.
3. Transaction Flow: Withdraw Example
A user withdrawing from the privacy pool proceeds through the following steps. Click each step to explore:
Retrieve Merkle Root
4. Trusted Setup
Zera currently uses a standard Groth16 trusted setup ceremony -- a one-time, multi-party computation that generates the proving and verification keys for each circuit (deposit, withdraw, relayed withdraw, transfer).
4.1 How It Works
Zera's four Groth16 circuits each require a structured reference string (SRS) generated by a trusted setup ceremony. The ceremony uses the "powers of tau" protocol followed by circuit-specific phase-2 contributions:
SRS = (pk, vk) ← Setup(C)where C is the arithmetic circuit (R1CS) for each operation
4.2 Security Guarantee
The standard Groth16 trust assumption: at least one participant in the ceremony must have honestly destroyed their toxic waste (the secret randomness used during their contribution). If this holds, the SRS is sound and no party can forge proofs.
4.3 On-Chain Verification
Groth16 proofs are verified on Solana using the alt_bn128 precompile, which natively supports BN254 pairing operations. The verification key (vk) is stored on-chain; each proof verification requires a single pairing check:
e(A, B) = e(α, β) · e(L, γ) · e(C, δ)where e is the BN254 optimal Ate pairing
5. Groth16 Security Properties
| Property | Guarantee | Assumption |
|---|---|---|
| Soundness | No polynomial-time adversary can produce a valid proof for a false statement (e.g., spending a note not in the tree, or reusing a nullifier) except with negligible probability. | Knowledge-of-exponent (KEA) on BN254 |
| Zero-Knowledge | Proofs reveal nothing about the witness (amount, secret, blinding, Merkle path, leaf index). A simulator can produce indistinguishable transcripts. | Simulation via trapdoor from SRS |
| Succinctness | Proof size is constant (~200 bytes: 2 G1 points + 1 G2 point). Verification is a single pairing equation check, independent of circuit size. | BN254 pairing efficiency |
| Trusted Setup | SRS is generated via multi-party ceremony. Security requires at least one honest participant to have destroyed their secret randomness ("toxic waste"). | 1-of-N honesty |
6. State Transitions & Double-Spend Prevention
Zera's global state evolves through leaf insertions into a 24-level incremental Merkle tree. Each state transition appends a new commitment and recomputes the root -- a deterministic, append-only transition verified by Groth16 proofs.
S_(i+1) = MerkleInsert(S_i, C_new)Root_(i+1) = Poseidon(... path from new leaf to root ...)
Nullifier-Based Double-Spend Prevention
Zera prevents double-spends through deterministic nullifiers. When a note is spent:
- The spender computes N = Poseidon(secret, commitment) and reveals N on-chain
- The on-chain program checks N against a nullifier set -- if N already exists, the transaction is rejected
- The Groth16 proof demonstrates that N was correctly derived from a valid commitment in the Merkle tree
- No party can spend the same note twice without producing the same nullifier
Concurrent Transaction Tolerance
7. Post-Quantum Considerations
Current Vulnerability Surface
BN254 pairings used by Groth16 are vulnerable to Shor's algorithm on a sufficiently large quantum computer. However, the Poseidon hash-based commitment layer is not directly broken by quantum attacks (unlike Pedersen commitments which rely on discrete log).
Migration Path
Hash-based commitments (already deployed) are quantum-resistant. The proof system would need replacement -- e.g., lattice-based SNARKs, STARKs, or WOTS (Winternitz One-Time Signatures) for hash-based authentication. The Poseidon commitment layer is architecturally positioned for a post-quantum proof system swap.
Research: Homomorphic Evolution
The theoretical framework of structure-preserving homomorphisms remains a valuable research direction for protocol upgrades that migrate commitment schemes while preserving validity of existing proofs, cross-chain bridges mapping state between different algebraic domains, and future epoch-based systems that evolve cryptographic parameters over time. This is not deployed in the current system, which uses fixed Poseidon parameters and a static Groth16 SRS.
8. Deployed System Parameters
| Parameter | Value | Rationale |
|---|---|---|
| Merkle tree depth | 24 levels | Capacity: 2^24 = 16,777,216 notes |
| Hash function | Poseidon X5 over BN254 scalar field | SNARK-friendly; minimal R1CS constraints |
| Root history buffer | 100 entries | Concurrent transaction tolerance |
| Proof system | Groth16 (snarkjs / groth16-solana) | ~200 byte proofs, constant-time verification |
| Verification | Solana alt_bn128 precompile | Native BN254 pairing at ~200k CU |
9. Performance & Implementation
- Proof generation: ~2-5 seconds client-side via snarkjs (WASM), dominated by the multi-scalar multiplication in Groth16 proving
- Proof size: ~200 bytes (constant) -- 2 G1 points + 1 G2 point on BN254
- On-chain verification: ~200k compute units on Solana via alt_bn128 precompile
- Trusted setup: One-time per circuit; Zera has four circuits (deposit, withdraw, relayed withdraw, transfer)
- Rust on-chain: light-poseidon 0.4, ark-bn254 0.5.0, ark-ff 0.5.0, groth16-solana 0.2.0
- TypeScript client: circomlibjs for Poseidon, snarkjs for Groth16 proving, @noble/hashes for Keccak-256
- Verification: Solana's alt_bn128 precompile for native BN254 pairing operations
- Circuit types: deposit, withdraw, relayed withdraw, transfer
10. Research Direction: Perpetual Genesis Ceremony
The following describes a theoretical research direction -- not the deployed system. It explores how a one-shot ceremony could be replaced with continuous participation, building on ideas from updatable SRS schemes.
Global Epoch Counter
Time would be divided into fixed-length epochs T1, T2, ... Every wallet would know the current epoch index i from the ledger header.
Base Randomness per Epoch
A public randomness beacon (e.g., the threshold-BLS drand network) would publish an unpredictable scalar: beta_i = Hash(drand sig_i) mod p.
User Contributions
Anyone could inject additional entropy during the contribution window. Contributors pick a private scalar r, broadcast a hash commitment H = Poseidon(r, nonce) with a proof of knowledge, and reveal after the window closes. All revealed scalars are added modulo p.
Epoch Seed & Updatable SRS
The epoch seed S_(i+1) = Poseidon(S_i, beta_i, rho_i) could drive an updatable structured reference string, allowing the proving/verification keys to evolve without re-running a full ceremony. This is an active area of research in the SNARKs community.
