Privacy Layer
zkSNARK-based privacy using Groth16 proofs and Poseidon hash
Privacy Layer
Paraloom's privacy layer is inspired by Zcash Sapling, using zero-knowledge proofs to hide transaction details while maintaining verifiability.
Key Concepts
Commitments
Cryptographic commitments to transaction values:
commitment = poseidon_hash(value || randomness)Nullifiers
Unique identifiers preventing double-spending:
nullifier = poseidon_hash(commitment || secret)Merkle Tree
Accumulator for all commitments:
root = merkle_tree.root() // Updated with each depositCryptographic Primitives
Poseidon Hash
Production-grade zkSNARK-friendly hash function.
Paraloom uses Zcash Sapling compatible parameters for maximum security and compatibility.
Parameters:
| Parameter | Value |
|---|---|
| Full Rounds | 8 |
| Partial Rounds | 57 |
| Alpha | 5 |
| Rate | 2 |
| Capacity | 1 |
| Security Level | 128 bits |
Properties:
- Collision-resistant
- Preimage-resistant
- zkSNARK-friendly (~500 constraints per hash)
- Deterministic (same input → same output)
- Avalanche effect (1-bit change → 50%+ output change)
API:
use paraloom::privacy::poseidon::{poseidon_hash, poseidon_hash_pair};
// Hash single value
let hash = poseidon_hash(&[1, 2, 3, 4]); // 32 bytes
// Hash two 32-byte values
let commitment = poseidon_hash_pair(&value, &randomness);
let nullifier = poseidon_hash_pair(&commitment, &secret);Groth16 zkSNARK
Zero-knowledge proof system on BLS12-381 elliptic curve.
| Property | Value |
|---|---|
| Curve | BLS12-381 |
| Security | 128-bit |
| Proof Size | 192 bytes |
| Setup | Trusted (requires ceremony) |
| Verification | ~10ms |
| Generation | ~2-3 seconds |
Circuit Constraints:
| Component | Constraints |
|---|---|
| Total | 1,306 |
| Per Hash | ~500 |
| Per Comparison | ~1 |
| Optimization | 40% reduction |
Transaction Types
1. Deposit (Public → Shielded)
Purpose: Move SOL into privacy pool
Process:
- User sends SOL to bridge program
- Bridge emits deposit event
- Privacy pool creates commitment
- Commitment added to merkle tree
- User receives shielded note
Privacy:
- Amount visible on-chain
- Recipient hidden
- Future spending unlinkable
2. Transfer (Shielded → Shielded)
Purpose: Private transfer between shielded addresses
Process:
- Prove knowledge of input notes
- Compute input nullifiers
- Generate output commitments
- Create zkSNARK proof
- Validators verify proof
- Update merkle tree
- Mark nullifiers as spent
Privacy:
- Amounts hidden
- Sender hidden
- Recipient hidden
- Only nullifiers revealed
3. Withdrawal (Shielded → Public)
Purpose: Move funds from privacy pool to public SOL
Process:
- Generate withdrawal proof
- Submit to consensus validators
- Validators verify proof (7/10 consensus)
- Submit transaction to Solana
- On-chain verification
- Transfer SOL to recipient
- Mark nullifier as spent
zkSNARK Circuits
Withdrawal Circuit
Public Inputs (visible on-chain):
pub struct WithdrawPublicInputs {
nullifier: [u8; 32], // Prevents double-spend
amount: u64, // Withdrawal amount
recipient: [u8; 32], // SOL address
merkle_root: [u8; 32], // Current tree root
}Private Inputs (hidden):
struct WithdrawPrivateInputs {
value: u64, // Original deposit value
randomness: [u8; 32], // Commitment randomness
secret: [u8; 32], // Nullifier secret
merkle_path: Vec<[u8; 32]>, // Merkle proof
}Circuit Logic:
fn withdraw_circuit(public: Public, private: Private) -> bool {
// 1. Recompute commitment (500 constraints)
let commitment = poseidon_hash(private.value || private.randomness);
// 2. Verify merkle path (500 constraints per level)
let root = verify_merkle_path(commitment, private.merkle_path);
assert_eq!(root, public.merkle_root);
// 3. Recompute nullifier (500 constraints)
let nullifier = poseidon_hash(commitment || private.secret);
assert_eq!(nullifier, public.nullifier);
// 4. Check amount (1 constraint)
assert!(private.value >= public.amount);
true // Circuit satisfied
}Proof Generation
Trusted Setup
Production requires multi-party computation (MPC) ceremony! The current setup is for testing only.
cargo run --bin setup-withdrawal-ceremonyOutput:
keys/
├── withdraw_proving.key # 1.5 MB (secret, for provers)
└── withdraw_verifying.key # 632 bytes (public, for verifiers)Generate Proof
1. Compute Inputs:
cargo run --bin compute-poseidon-inputs2. Generate Proof:
source /tmp/poseidon_exports.sh
cargo run --bin generate-withdrawal-proofSecurity Model
Privacy Guarantees
Confidentiality:
- Transaction amounts hidden (except at endpoints)
- Sender identity hidden
- Recipient identity hidden
- Transaction graph obscured
Integrity:
- Proof soundness guarantees valid transitions
- Nullifiers prevent double-spending
- Merkle tree ensures correct state
Comparison to Zcash
| Feature | Zcash Sapling | Paraloom |
|---|---|---|
| Curve | BLS12-381 | BLS12-381 |
| Proof System | Groth16 | Groth16 |
| Hash Function | Poseidon (8/57) | Poseidon (8/57) |
| Proof Size | 192 bytes | 192 bytes |
| Shielded Pool | On-chain | Hybrid |
| Consensus | Nakamoto | Byzantine (7/10) |