ParaloomPARALOOM

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.

Privacy Flow

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 deposit

Cryptographic Primitives

Poseidon Hash

Production-grade zkSNARK-friendly hash function.

Paraloom uses Zcash Sapling compatible parameters for maximum security and compatibility.

Parameters:

ParameterValue
Full Rounds8
Partial Rounds57
Alpha5
Rate2
Capacity1
Security Level128 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.

PropertyValue
CurveBLS12-381
Security128-bit
Proof Size192 bytes
SetupTrusted (requires ceremony)
Verification~10ms
Generation~2-3 seconds

Circuit Constraints:

ComponentConstraints
Total1,306
Per Hash~500
Per Comparison~1
Optimization40% reduction

Transaction Types

1. Deposit (Public → Shielded)

Purpose: Move SOL into privacy pool

Process:

  1. User sends SOL to bridge program
  2. Bridge emits deposit event
  3. Privacy pool creates commitment
  4. Commitment added to merkle tree
  5. 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:

  1. Prove knowledge of input notes
  2. Compute input nullifiers
  3. Generate output commitments
  4. Create zkSNARK proof
  5. Validators verify proof
  6. Update merkle tree
  7. 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:

  1. Generate withdrawal proof
  2. Submit to consensus validators
  3. Validators verify proof (7/10 consensus)
  4. Submit transaction to Solana
  5. On-chain verification
  6. Transfer SOL to recipient
  7. 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-ceremony

Output:

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-inputs

2. Generate Proof:

source /tmp/poseidon_exports.sh
cargo run --bin generate-withdrawal-proof

Security 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

FeatureZcash SaplingParaloom
CurveBLS12-381BLS12-381
Proof SystemGroth16Groth16
Hash FunctionPoseidon (8/57)Poseidon (8/57)
Proof Size192 bytes192 bytes
Shielded PoolOn-chainHybrid
ConsensusNakamotoByzantine (7/10)

On this page