Skip to main content

What are Zero-Knowledge Proofs?

A zero-knowledge proof (ZKP) is a cryptographic method that allows one party (the prover) to prove to another party (the verifier) that a statement is true, without revealing any information beyond the validity of the statement itself.
Example: Cave AnalogyImagine a circular cave with one entrance and a magic door in the middle. Alice wants to prove to Bob that she knows the password to the door without revealing the password.
  1. Alice enters the cave and goes to either the left or right path (Bob can’t see which)
  2. Bob then shouts either “left” or “right”
  3. Alice emerges from the requested side by using the password to open the door if needed
  4. After many repetitions, Bob is convinced Alice knows the password without ever learning it
Privacy Cash works similarly: you prove you own a commitment without revealing which one.

Why Zero-Knowledge Proofs?

Privacy Cash uses ZK proofs to enable private transactions while maintaining security guarantees:

Privacy

Prove ownership of a commitment without revealing which commitment, maintaining transaction unlinkability.

Security

Ensure all transaction rules are followed (amount conservation, valid signatures, no double-spending) without trust.

Efficiency

Groth16 proofs are small (~256 bytes) and fast to verify (~1ms on Solana), enabling scalable privacy.

No Trusted Party

After trusted setup, no ongoing trust is required. The cryptography is trustless and verifiable.

Groth16 ZK-SNARKs

Privacy Cash uses Groth16, a popular ZK-SNARK (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) construction.

Properties

The proof reveals nothing about the witness (private inputs) beyond the statement being true.What’s hidden:
  • Private keys
  • Blinding factors
  • Input commitment amounts
  • Merkle path (which leaf in tree)
What’s revealed:
  • Proof is valid for some commitment in the tree
  • Public inputs (nullifiers, output commitments, amounts)
  • Merkle root (tree state)

Circuit Architecture

The Privacy Cash circuit is implemented in Circom and compiles to a rank-1 constraint system (R1CS).

Transaction Circuit

Located at circuits/transaction.circom:22, the main circuit has: Inputs:
Constraints:
  1. Keypair validation: Public key = hash(private key)
  2. Commitment computation: commitment = hash(amount, pubkey, blinding, mint)
  3. Signature generation: signature = hash(privkey, commitment, merkle_path)
  4. Nullifier computation: nullifier = hash(commitment, path_indices, signature)
  5. Merkle proof verification: Prove commitment is in tree with given root
  6. Amount conservation: sum(inputs) + publicAmount = sum(outputs)
  7. No duplicate nullifiers: inputNullifier[0] ≠ inputNullifier[1]
The circuit uses Poseidon hash throughout, which is ZK-friendly (requires fewer constraints than SHA-256).

Circuit Parameters

Constraint Count

The circuit compiles to approximately:
  • Total constraints: ~2,000
  • Merkle proof: ~1,500 constraints (26 levels × ~60 per level)
  • Poseidon hashes: ~400 constraints
  • Other logic: ~100 constraints
Smaller constraint counts mean faster proving and verification. Privacy Cash’s circuit is optimized for efficiency.

Proof Generation

Users generate proofs off-chain using the Privacy Cash SDK or CLI.

Generation Process

1

Collect Inputs

Gather all public and private inputs for the circuit:
2

Generate Witness

Execute the circuit to compute all intermediate signals:
This ensures all constraints are satisfied before proving.
3

Create Proof

Use the proving key to generate a Groth16 proof:
Output:
  • proof.json: The proof (π_a, π_b, π_c)
  • public.json: Public inputs
Time: ~2-5 seconds on modern laptops
4

Submit Transaction

Package the proof and submit to Solana:
The on-chain program verifies the proof before executing the transfer.

SDK Integration

The Privacy Cash SDK handles proof generation automatically:
Example: Generate Withdraw Proof

Proof Verification

The Solana program verifies proofs on-chain using a hardcoded verifying key.

Verification Process

1

Deserialize Proof

Extract proof elements and public inputs:
2

Initialize Verifier

Create a Groth16 verifier with the verifying key:
3

Pairing Check

Verify the proof using bilinear pairings:Groth16 verification equation:
Where:
  • e() is a pairing operation on elliptic curves (BN254)
  • α, β, γ, δ are from the verifying key
  • IC is computed from public inputs and verifying key
4

Check Result

Verification returns true/false:
If verification fails, the transaction is rejected before any state changes.

Verifying Key

The verifying key is hardcoded in the program at utils.rs:16:
The verifying key must match the circuit and proving key. Any mismatch will cause all proofs to fail verification.

Trusted Setup

Groth16 requires a trusted setup ceremony to generate proving and verifying keys.

What is Trusted Setup?

A trusted setup is a multi-party computation (MPC) where participants generate random values called “toxic waste.”Process:
  1. Multiple participants contribute randomness
  2. Each contribution is combined cryptographically
  3. Proving key and verifying key are generated
  4. Participants destroy their random values (toxic waste)
Security:
  • As long as one participant is honest and destroys their toxic waste, the setup is secure
  • Multiple participants increase confidence (no single point of failure)
  • Toxic waste could be used to forge proofs, so destruction is critical
Privacy Cash setup:
  • Conducted by the Privacy Cash team
  • Multiple contributors from different organizations
  • Setup artifacts are published for transparency

Powers of Tau

Privacy Cash uses a universal trusted setup (Powers of Tau) plus a circuit-specific phase:
1

Phase 1: Powers of Tau

Universal ceremony for all circuits of a certain size:
This phase is circuit-independent and can be reused.
2

Phase 2: Circuit-Specific

Generate proving and verifying keys for the specific circuit:
3

Deploy Verifying Key

Hardcode the verifying key in the Solana program:
The key is now immutably stored on-chain.
Upcoming: PLONK MigrationFuture versions may migrate to PLONK or other proof systems that don’t require trusted setup, providing trustless cryptography end-to-end.

Performance Characteristics

Proving Time

Proof generation happens client-side. For mobile applications, consider server-side proving with user consent.

Verification Time

Resource Usage

Proving:
  • Memory: ~500 MB (witness generation)
  • CPU: 1 core at 100% for 2-3 seconds
  • Disk: ~50 MB (proving key)
Verification:
  • Memory: ~10 MB
  • CPU: Minimal (< 1ms)
  • Disk: ~1 KB (verifying key)

Security Analysis

Property: A valid proof can only be generated if the statement is true.Groth16 soundness:
  • Based on hardness of discrete log on BN254 elliptic curve
  • Security level: ~128 bits
  • Probability of forging proof: < 2^-128 (negligible)
Audits:
  • Circuit reviewed by Accretion, HashCloak, Zigtur, Kriko
  • No soundness vulnerabilities found
Property: The proof reveals nothing about private inputs.Groth16 zero-knowledge:
  • Proof is computationally indistinguishable from random
  • Simulator can generate fake proofs without witness (for analysis only)
  • No information leakage even with multiple proofs from same witness
Best practices:
  • Never reuse blinding factors
  • Use cryptographically secure randomness
  • Don’t expose intermediate computation values
Property: If the statement is true and prover is honest, verification always succeeds.Guarantees:
  • Properly formed proofs always verify
  • No false rejections
  • Deterministic verification
Testing:
  • Extensive unit tests in anchor/tests/
  • Mainnet track record: 100% verification rate for honest proofs
Risk: If all setup participants collude and keep toxic waste, they could forge proofs.Mitigation:
  • Multiple independent contributors
  • Public ceremony with transparency
  • Published transcripts
  • Future migration to transparent setup (PLONK, STARKs)
Current status:
  • No evidence of setup compromise
  • All contributors reported destroying toxic waste

Implementation Reference

Transaction Circuit

Main circuit implementing JoinSplit logiccircuits/transaction.circom

Merkle Proof Circuit

Merkle tree membership proofcircuits/merkleProof.circom

Keypair Circuit

Keypair and signature generationcircuits/keypair.circom

Proof Verification

On-chain Groth16 verificationutils.rs:214

Next Steps

Merkle Trees

Learn how commitments are organized in Merkle trees

Commitments & Nullifiers

Understand the core cryptographic primitives

Build with SDK

Start integrating Privacy Cash into your application

Circuit Audit Reports

Review security audit findings