zkSNARKs
Zero-Knowledge Succinct Non-Interactive Argument of Knowledge
Veil utilizes Groth16 zkSNARKs on the BN254 (alt_bn128) elliptic curve to prove the validity of private transactions without revealing sensitive data. The transfer circuit contains approximately 7,000 R1CS constraints and enables complete transaction privacy.
A zkSNARK (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) allows a prover to convince a verifier that a statement is true without revealing any information beyond the truth of the statement itself.
Properties:
- Zero-Knowledge: The proof reveals nothing about the private inputs (amounts, secrets, blinding factors)
- Succinct: The proof is small (256 bytes for Groth16) and verification is fast (<1 second)
- Non-Interactive: The prover generates the proof once; no back-and-forth communication needed
- Argument of Knowledge: The prover must actually know the secret inputs, not just that they exist
When you send a private transaction, you generate a proof that demonstrates:
"I know a secret, amounts, and blinding factors such that:
- The input commitment exists in the Merkle tree (membership proof via merkle_path)
- I derived the spending key correctly from my secret using Poseidon hash
- The nullifier is computed correctly from the spending key and leaf index
- The input commitment opens to the claimed amount using the correct blinding factor
- The output commitment is correctly formed with the same amount
- All cryptographic operations follow the protocol (no cheating)
...without revealing the secret, amounts, or which note I'm spending."
Curve: BN254 (alt_bn128) Constraint System: R1CS (Rank-1 Constraint System) Total Constraints: ~7,000 Proof Size: 256 bytes
proof_a: 64 bytes (G1 point)proof_b: 128 bytes (G2 point)proof_c: 64 bytes (G1 point)
Performance:
- Proof Generation: 5-10 seconds (client-side, one-time per transaction)
- Verification: <1 second on-chain (~200,000 compute units on Solana)
- Security Level: ~128 bits (based on BN254 discrete log hardness)
These values are visible on-chain and included in the proof verification:
merkle_root(32 bytes): Current root of the commitments Merkle treenullifier(32 bytes): Unique identifier preventing double-spendnew_commitment(32 bytes): Commitment to the output amount
Total Public Input Size: 96 bytes
These values are known only to the prover and never revealed:
secret(32 bytes): Master secret for deriving spending keyinput_amount: Amount in the input commitmentinput_blinding(32 bytes): Blinding factor of input commitmentoutput_blinding(32 bytes): Blinding factor for output commitmentmerkle_path(20 × 32 bytes): Sibling hashes proving Merkle membershipleaf_index: Position of the input commitment in the tree- Additional metadata: Asset IDs, domain separators, etc.
The circuit enforces the following operations:
1. Spending Key Derivation (~400 constraints)
spending_key = Poseidon(secret, "NYX_SPENDING_KEY")
- One Poseidon hash operation
- Ensures spending key is correctly derived from secret
2. Input Commitment Verification (~500 constraints)
input_commitment = input_amount * G + input_blinding * H
- Pedersen commitment computation
- Scalar multiplication on BN254 G1
- Verifies the input commitment matches the claimed amount and blinding
3. Merkle Tree Membership (~4,000 constraints)
root = VerifyMerklePath(input_commitment, merkle_path, leaf_index)
- 20 levels of Poseidon hashing (depth 20 tree)
- ~200 constraints per Poseidon hash × 20 levels
- Proves the input commitment exists in the tree at the claimed position
4. Nullifier Derivation (~400 constraints)
nullifier = Poseidon(spending_key, Hash(leaf_index || "NYX_NULLIFIER"))
- Two-step circuit-safe derivation
- Prevents double-spending by producing unique nullifier per note
- Ensures nullifier cannot be reused
5. Output Commitment (~500 constraints)
new_commitment = input_amount * G + output_blinding * H
- Creates commitment for recipient
- Ensures amount is preserved (same as input)
6. Amount Equality & Validation (~1,200 constraints)
- Range checks (amounts are non-negative and within bounds)
- Amount conservation (input_amount == output_amount for transfers)
- Additional validation logic
The zkSNARK circuit provides the following security guarantees:
- No Double-Spend: Each nullifier can only be derived once per leaf_index
- Amount Conservation: Output amount equals input amount (for transfers)
- Valid Commitment: The input commitment exists in the Merkle tree
- Correct Derivation: All cryptographic operations follow the protocol specification
- Knowledge of Secret: Only the true owner knowing the secret can generate valid proofs
Groth16 Proof (256 bytes):
- Used in production
- Provides full security guarantees
- Slower to generate (5-10s) but small and fast to verify
MVP Proof (96 bytes):
- Ed25519 signature-based (testing/development only)
- Fast generation (<1ms) for rapid iteration
- NOT secure for production use
- Auto-detected by proof length during verification
Client-Side (Proof Generation):
- Time: 5-10 seconds (depends on hardware)
- RAM: ~2-4 GB peak usage
- CPU: Heavy computation (multi-core helpful)
- One-time cost per transaction
On-Chain (Verification):
- Compute Units: ~200,000 CU (Solana)
- Time: <1 second
- Cost: ~0.0002 SOL per transaction (at current rates)
- Performed by validators
Groth16 requires a trusted setup to generate cryptographic parameters:
- Proving Key (PK): Used to generate proofs (client-side)
- Verification Key (VK): Used to verify proofs (on-chain)
Multi-Party Computation (MPC)
Veil will undergo a multi-party computation ceremony in Phase 4 where:
- Multiple independent participants (100+) each contribute randomness
- Participants combine their contributions sequentially
- Each participant must delete their secret contribution ("toxic waste")
- Security holds as long as at least one participant is honest and deletes their waste
Current Status:
- Veil currently uses a development trusted setup (Phase 3G complete)
- Production MPC ceremony planned for Phase 4
- Not suitable for mainnet deployment until ceremony is complete
Security Implications
If all participants collude (extremely unlikely with 100+ participants):
- They could generate fake proofs
- They could NOT steal funds or decrypt amounts
- The system would lose soundness but maintain zero-knowledge
After honest ceremony:
- No one can generate invalid proofs
- System is cryptographically secure
- Parameters can be used indefinitely
Groth16 is chosen for Veil because of:
- Smallest Proof Size: 256 bytes (constant, regardless of circuit complexity)
- Fast Verification: <1s on-chain, critical for blockchain deployment
- Mature & Audited: Well-studied since 2016, battle-tested in Zcash
- Good Prover Performance: 5-10s is acceptable for privacy-critical operations
- Solana Integration: Excellent support via
groth16-solanacrate
Trade-offs vs Other zkSNARKs:
- vs PLONK: Smaller proofs, faster verification, but requires trusted setup
- vs STARKs: Much smaller proofs (256 bytes vs ~100KB), faster verification, but setup required
- vs Bulletproofs: Smaller proofs, much faster verification, but slower proving
For a privacy protocol on Solana where on-chain verification cost is critical, Groth16's trade-offs are optimal.
Libraries Used:
- Proof Generation:
ark-groth16(arkworks-rs ecosystem) - Curve Operations:
ark-bn254(BN254/alt_bn128 curve) - On-Chain Verification:
groth16-solana(Solana-optimized verifier) - Circuit Constraints: Custom R1CS constraints for Pedersen, Poseidon, Merkle trees
Witness Generation: The prover computes all private inputs off-chain, then generates the proof. This process is deterministic given the same inputs, ensuring reproducibility for debugging.
See also:
- Commitments - How amounts are hidden
- Nullifiers - How double-spending is prevented
- Privacy Model - Overall privacy guarantees
On This Page
- zkSNARKs
- What is a zkSNARK?
- What Does the Circuit Prove?
- Groth16 Specifications
- Public Inputs (On-Chain)
- Private Inputs (Witness)
- Constraint Breakdown (~7,000 Total)
- 1. Spending Key Derivation (~400 constraints)
- 2. Input Commitment Verification (~500 constraints)
- 3. Merkle Tree Membership (~4,000 constraints)
- 4. Nullifier Derivation (~400 constraints)
- 5. Output Commitment (~500 constraints)
- 6. Amount Equality & Validation (~1,200 constraints)
- Circuit Guarantees
- Proof Format
- Performance Characteristics
- Trusted Setup Ceremony
- Multi-Party Computation (MPC)
- Security Implications
- Why Groth16?
- Implementation Details