What is Zero-Knowledge?
A zero-knowledge proof 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.Key Properties
Completeness: If the statement is true, an honest prover can convince an honest verifier. Soundness: A dishonest prover cannot convince a verifier of a false statement (except with negligible probability). Zero-Knowledge: The verifier learns nothing beyond the fact that the statement is true.Example Use Case
Prove you know a password without revealing it:Leo uses zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge) - proofs that are small, fast to verify, and don’t require interaction between prover and verifier.
From Leo to Zero-Knowledge Proofs
Compilation Pipeline
Why Flattening is Essential
Zero-knowledge circuits cannot represent dynamic control flow. The flattening pass transforms all control flow into arithmetic operations:- All loops must be unrollable at compile time
- No recursion (would create unbounded circuits)
- No dynamic memory allocation
R1CS: Rank-1 Constraint System
Leo programs compile to R1CS, a standard representation for arithmetic circuits.R1CS Structure
Each constraint has the form:wᵢare wire values (variables in the circuit)aᵢ, bᵢ, cᵢare coefficients- The constraint enforces:
A · B = C
Example: Addition
Example: Multiplication
Example: Complex Expression
Each R1CS constraint can only represent one multiplication (plus arbitrary additions). This is why SSA and flattening are crucial: they decompose programs into sequences of simple operations that map cleanly to R1CS.
Aleo’s Architecture
Leo compiles to Aleo Instructions, which are executed by the Aleo Virtual Machine (AVM).Aleo Instructions
Aleo uses a register-based instruction set:Public vs Private Data
Leo distinguishes between public and private data: Private (.private): Hidden from the verifier, included in the proof
Public (.public): Visible to everyone, used as public inputs to the proof
Constant (.constant): Known at compile time, optimized away
Records: Private State
Records enable private state management:- Private: Only the owner knows the record exists
- Consumed: Input records are spent (deleted)
- Created: Output records are generated
- Encrypted: Stored encrypted on-chain
Records implement the UTXO model from Bitcoin, but with privacy. Each record can only be spent once, and spending it creates new records.
Mappings: Public State
Mappings provide public on-chain storage:finalize blocks, which execute after the proof is verified.
Zero-Knowledge Optimizations in Leo
1. Constant Folding
The compiler evaluates constants at compile time to reduce circuit size:2. Dead Code Elimination
Removes unused computations that would add unnecessary constraints:3. Common Subexpression Elimination
Reuses computed values to reduce duplicate constraints:4. Function Inlining
Inlines small functions to eliminate call overhead:Each optimization pass reduces the number of constraints in the final circuit, directly improving proof generation time and memory usage.
Proof Generation and Verification
The Proving Process
- Compile: Leo source → Aleo instructions
- Execute: Run the program with inputs to generate a witness (all wire values)
- Setup: Generate proving and verifying keys (one-time per program)
- Prove: Create a proof that the execution is correct
- Verify: Check the proof (fast, ~milliseconds)
Trusted Setup
Aleo uses a universal trusted setup, meaning:- One setup ceremony serves all programs
- No per-program setup required
- Uses the Marlin proof system
Proof Size and Verification Time
With zk-SNARKs:- Proof size: ~1-2 KB (constant, independent of program size)
- Verification time: ~10 milliseconds (constant)
- Proving time: Proportional to circuit size (can be seconds to minutes)
Cryptographic Primitives
Leo provides cryptographic operations optimized for zero-knowledge circuits:Hash Functions
Commitments
Signatures
Circuit Size Analysis
Estimating Circuit Size
The circuit size (number of constraints) depends on:- Arithmetic operations: Each multiplication = 1 constraint
- Hash operations: Poseidon2 = ~300 constraints, SHA-256 = ~25,000 constraints
- Ternary operations: ~3 constraints
- Array accesses: Depends on array size (dynamic access requires one constraint per element)
- Loop unrolling: Circuit size multiplied by iteration count
Example Analysis
Optimization Guidelines
- Minimize multiplications: Use addition when possible
- Hoist loop-invariant computations: Move calculations outside loops
- Use ternary instead of if-else: Already done by flattening pass
- Inline small functions: Reduces call overhead
- Eliminate dead code: Remove unused computations
The Leo compiler automatically applies many optimizations. Focus on algorithmic improvements and choosing the right cryptographic primitives.
Async Functions and Finalize
Aleo introduces a two-phase execution model:Phase 1: Transition (Off-chain, Private)
Phase 2: Finalize (On-chain, Public)
Common Pitfalls
1. Dynamic Array Indexing
2. Unbounded Loops
3. Expensive Hash Functions
4. Unnecessary Branching
Further Reading
- Compiler Architecture - How the compiler is structured
- Compiler Passes - Detailed pass documentation
- Optimization - Performance tuning guide
- Aleo Documentation - Aleo VM and instruction set