Zero-Knowledge Proofs and Tornado Cash: A Technical Deep Dive -by Jay Makwana

Jay MakwanaJay Makwana
8 min read

Imagine you’re at a party and someone claims they know the password to the WiFi. Instead of just telling everyone the password (which would defeat the purpose), they prove they know it by successfully connecting their phone. They’ve proven their knowledge without revealing the actual secret. This is essentially what zero-knowledge proofs do in cryptography — and it’s exactly how Tornado Cash revolutionized blockchain privacy.

The Magic Behind Zero-Knowledge Proofs

Zero-knowledge proofs sound like something out of a sci-fi movie, but they’re actually elegant mathematical constructs that solve a fundamental problem: How do you prove you know something without revealing what you know?

Think of it like this classic analogy: You want to prove you can distinguish between two identical-looking balls (one red, one blue) without revealing which is which. A verifier holds both balls behind their back, shows you one, then hides it again. They ask, “Did I switch the balls?” If you really can tell them apart, you’ll be right every time. If you’re just guessing, you’ll be wrong about half the time. After enough rounds, the verifier becomes convinced you can distinguish them, but they never learn which ball is which color.

Round 1: Show Ball A → Hide → Show Ball B → "You switched!" ✓
Round 2: Show Ball B → Hide → Show Ball A → "You switched!" ✓
Round 3: Show Ball A → Hide → Show Ball A → "No switch!" ✓
...and so on

For a proof system to be truly zero-knowledge, it needs three properties:

🎯 Completeness: If you’re telling the truth and both parties play fair, the verifier will believe you.

🛡️ Soundness: If you’re lying, you can’t fool an honest verifier (except with tiny probability).

🔒 Zero-Knowledge: If you’re telling the truth, the verifier learns nothing except that fact.

Meet zk-SNARKs: The Crypto World’s Swiss Army Knife

Tornado Cash didn’t use just any zero-knowledge proof — it used zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge). Let’s break down why these are special:

  • Succinct: The proofs are tiny and super fast to verify

  • Non-interactive: No back-and-forth needed — just one proof does it all

  • Arguments of Knowledge: You must actually know the secret, not just know it exists

Think of zk-SNARKs as the difference between a long, tedious court case with multiple witnesses and evidence (traditional proofs) versus a single, undeniable DNA test (zk-SNARKs). Both prove guilt, but one is way more efficient.

How Tornado Cash Worked: The Technical Magic

The Big Picture

Tornado Cash was like a digital mixing bowl. You’d throw your “marked” coins in one side, and later withdraw “clean” coins from the other side. But here’s the genius part — it used math to prove you deserved those coins without revealing which specific coins you originally deposited.

Alice deposits 1 ETH → [MIXING POOL] → Bob withdraws 1 ETH
Carol deposits 1 ETH → [MIXING POOL] → Alice withdraws 1 ETH  
Dave deposits 1 ETH → [MIXING POOL] → Carol withdraws 1 ETH

The blockchain sees the deposits and withdrawals, but it can’t link them together. It’s like a magical vending machine that remembers you paid, but forgets your face.

The Merkle Tree: Your Digital Receipt System

When you deposited money into Tornado Cash, here’s what happened behind the scenes:

  1. You generated a secret recipe — two random ingredients called nullifier and secret

  2. You baked a commitment — commitment = hash(nullifier + secret)

  3. The system filed your receipt — added your commitment to a giant tree of all deposits

ROOT
                   /    \
              BRANCH1    BRANCH2
             /   \       /     \
        LEAF1  LEAF2  LEAF3  LEAF4
         ↑      ↑      ↑      ↑
      Alice   Bob   Carol   Dave
    (commits) (commits) (commits) (commits)

This Merkle tree is like a giant filing cabinet where your deposit receipt gets stored. The beautiful thing? Everyone can see the filing cabinet, but they can’t tell which folder is yours.

The Withdrawal Dance: Proving Without Revealing

When you wanted to withdraw, you had to perform a cryptographic dance with three moves:

Move 1: “I have a valid receipt” — Prove your commitment exists in the tree Move 2: “I know the secret recipe” — Show you know the nullifier and secret Move 3: “I haven’t used this before” — Prove this nullifier hasn’t been spent

Withdrawal Process:
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Generate      │    │   Create ZK     │    │   Submit to     │
│   ZK Proof      │ -> │   Proof that    │ -> │   Smart         │
│   (off-chain)   │    │   verifies all  │    │   Contract      │
│                 │    │   conditions    │    │   (on-chain)    │
└─────────────────┘    └─────────────────┘    └─────────────────┘

The Circuit: Where the Magic Happens

The zk-SNARK circuit was like a bouncer at an exclusive club. It checked multiple conditions simultaneously:

// Simplified circuit logic (not actual code, but the idea)
function tornadoCircuit(publicInputs, privateInputs) {
    // Public stuff everyone can see
    const { merkleRoot, nullifierHash, recipient } = publicInputs;

    // Secret stuff only you know
    const { nullifier, secret, merklePath } = privateInputs;

    // Prove you know the secret recipe
    const commitment = hash(nullifier, secret);

    // Prove your receipt is in the filing cabinet
    const computedRoot = verifyMerklePath(commitment, merklePath);
    assert(computedRoot === merkleRoot);

    // Prove you haven't used this receipt before
    const computedNullifierHash = hash(nullifier);
    assert(computedNullifierHash === nullifierHash);

    return "Valid proof! You can withdraw.";
}

The Cryptographic Toolbox

Poseidon: The Hash Function That Plays Nice

Traditional hash functions like SHA-256 are like trying to fit a square peg in a round hole when it comes to zero-knowledge circuits. Tornado Cash used Poseidon, a hash function designed specifically for zk-SNARKs.

SHA-256 in ZK circuit: 20,000+ constraints 😵
Poseidon in ZK circuit: ~150 constraints 😎

It’s like the difference between using a sledgehammer and a precision screwdriver — both get the job done, but one is way more elegant.

Groth16: The Proof System

Groth16 was the specific zk-SNARK construction Tornado Cash used. Think of it as a highly optimized proof factory:

Input: Your private information + public parameters Output: A tiny proof (just 3 numbers!) that convinces everyone

Proof Generation:
Private Inputs (Big) → [Groth16 Factory] → Proof (Tiny)
     ~1MB                                    ~200 bytes

The Smart Contract: Where It All Comes Together

The Tornado Cash smart contract was surprisingly simple for something so powerful:

contract TornadoCash {
    // Keep track of used nullifiers (prevent double-spending)
    mapping(bytes32 => bool) public nullifierHashes;

    // Store the commitment tree root history
    bytes32[ROOT_HISTORY_SIZE] public roots;

    function deposit(bytes32 _commitment) external payable {
        // Add your commitment to the tree
        // Update the tree root
        // Accept your ETH
    }

    function withdraw(
        bytes calldata _proof,
        bytes32 _root,
        bytes32 _nullifierHash,
        address payable _recipient,
        uint256 _fee
    ) external {
        // Verify the ZK proof
        // Check nullifier hasn't been used
        // Send ETH to recipient
        // Mark nullifier as used
    }
}

The User Experience: What It Actually Felt Like

Using Tornado Cash was like going to a high-tech bank:

  1. Deposit Phase: “Here’s my 1 ETH and my secret commitment”

  2. Waiting Period: “I’ll come back later from a different address”

  3. Proof Generation: “Let me create my mathematical proof…” (takes 2–3 minutes)

  4. Withdrawal: “Here’s my proof, give me my 1 ETH”

User Journey:
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Deposit   │    │    Wait     │    │  Generate   │    │  Withdraw   │
│   1 ETH     │ -> │  (optional  │ -> │  ZK Proof   │ -> │   1 ETH     │
│  Address A  │    │ time delay) │    │ (2-3 mins)  │    │  Address B  │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

Security Considerations: The Good, The Bad, The Ugly

The Good: Mathematical Guarantees

The cryptography was solid. If you generated your secrets properly and the setup was honest, your privacy was mathematically guaranteed.

The Bad: Trusted Setup

Groth16 required a “trusted setup ceremony” — like needing a group of people to create a master key and then destroy it. If they cheated, the whole system could be compromised.

Trusted Setup Process:
Person 1: Creates part of key → Destroys their part
Person 2: Adds to key → Destroys their part  
Person 3: Adds to key → Destroys their part
...
Final Key: Only usable if ALL participants were honest

The Ugly: Metadata Leakage

While your transaction amounts and addresses were private, other information could leak:

  • Timing: When you deposit vs. withdraw

  • Gas prices: How much you paid for transactions

  • Patterns: If you always withdraw exactly 24 hours after depositing

Performance and Practicality

The Numbers That Mattered

  • Proof Generation: 2–3 minutes on average laptop

  • Proof Size: ~200 bytes (tiny!)

  • Verification: ~270,000 gas (~$10–50 depending on gas prices)

  • Anonymity Set: Stronger with more users in the pool

Real-World Constraints

Generating proofs was computationally intensive. Your laptop would basically turn into a mini heater for a few minutes while crunching through the math. But once generated, verification was lightning fast.

Beyond Tornado Cash: The Legacy Lives On

Even though Tornado Cash faced regulatory challenges, its technical innovations influenced the entire blockchain privacy ecosystem:

Universal zk-SNARKs (PLONK)

New systems that don’t need trusted setup for every circuit — like having a universal key that works for any lock.

Recursive Proofs

Proofs that can prove other proofs, creating incredibly efficient blockchain systems.

Programmable Privacy

Extending the concept beyond simple mixing to private smart contracts and applications.

The Human Impact: Why This Matters

Privacy isn’t just about hiding illegal activity. It’s about:

  • Financial Privacy: Not wanting everyone to see your salary

  • Safety: Protecting activists in oppressive regimes

  • Business: Keeping competitive information private

  • Personal: Maintaining basic human dignity in digital transactions

Tornado Cash showed that strong privacy is technically possible on public blockchains. That’s a powerful concept that extends far beyond cryptocurrency.

Wrapping Up: The Future of Private Blockchains

Tornado Cash was like the first airplane — it proved the concept was possible, even if the implementation wasn’t perfect. The technical innovations it introduced continue to inspire new privacy-preserving protocols.

The core insight remains powerful: you can prove you have the right to do something without revealing everything about yourself. In a world where digital privacy is increasingly rare, that’s not just a technical achievement — it’s a fundamental building block for digital freedom.

Whether you’re a developer building the next privacy protocol or just someone curious about how this magic works, understanding these concepts helps us build a more private, secure digital future.


Want to dive deeper? The original Tornado Cash circuits are open source, and studying them is one of the best ways to understand practical zk-SNARK applications. Just remember — with great cryptographic power comes great responsibility.

1
Subscribe to my newsletter

Read articles from Jay Makwana directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jay Makwana
Jay Makwana