Security

Zero-Knowledge Proofs Explained — How ZKPs Enable Private Transfers

Zero-knowledge proofs let you prove a statement is true without revealing why it's true. Here's how Groth16, Poseidon hashing, and Merkle trees work together to make private crypto transfers possible.

By Jorge Rodriguez · 8 min read · 2025-04-28

What is a zero-knowledge proof?

A zero-knowledge proof (ZKP) is a cryptographic method that lets one party (the prover) convince another party (the verifier) that a statement is true — without revealing any information beyond the truth of the statement itself.

In practice: you can prove you know a password without ever showing the password. You can prove you're over 18 without revealing your birth date. You can prove you made a deposit without revealing which deposit is yours.

Types of zero-knowledge proofs

Not all ZKPs are the same. The main categories relevant to blockchain are:

TypeTrusted SetupProof SizeVerification TimeUsed By
Groth16Yes (one-time)~200 bytes~1msMixoor, Zcash
PLONKYes (universal)~400 bytes~2mszkSync, Aztec
STARKsNo~50 KB~5msStarkNet, Cairo
Halo2No~5 KB~3msZcash Orchard, Scroll

Mixoor uses Groth16 because it produces the smallest proofs and the fastest on-chain verification — critical when every byte costs gas and every millisecond counts on Solana's runtime.

How Groth16 works in Mixoor

The Mixoor protocol combines three cryptographic primitives: Poseidon hashing, Merkle trees, and Groth16 proofs. Here's how they fit together:

Poseidon hash — the commitment

When you deposit, Mixoor generates two random values: a secret and a nullifier. It then computes commitment = Poseidon(secret, nullifier). Poseidon is a hash function designed specifically for zero-knowledge circuits — it's 8x more efficient inside a ZKP than SHA-256 or Keccak.

Merkle tree — the anonymity set

Your commitment is inserted as a leaf in a Merkle tree stored on-chain. The tree has a depth of 20, meaning it can hold up to 2^20 (1,048,576) deposits. When you withdraw, you prove your commitment exists somewhere in this tree — but not where. The larger the tree, the bigger your anonymity set.

Groth16 proof — the privacy guarantee

The ZK circuit takes as private inputs your secret and nullifier, and as public inputs the Merkle root and the nullifier hash. It proves: (1) you know a secret that hashes to a commitment in the tree, and (2) you haven't used this nullifier before. The proof is ~200 bytes and verifies in under 1ms on-chain.

The trusted setup

Groth16 requires a one-time trusted setup ceremony to generate the proving and verification keys. If the secret randomness from the ceremony is compromised, someone could forge proofs. Mixoor uses the Hermez ceremony — a public multi-party computation where over 100 independent participants contributed randomness.

As long as at least one participant was honest and destroyed their secret, the setup is secure. The ceremony transcript and all contributions are publicly verifiable.

Where the proof is generated

The Groth16 proof is generated entirely in your browser using snarkjs and circomlibjs. Your secret and nullifier never leave your device — they're used locally to compute the proof, then discarded. Only the proof and the nullifier hash are sent to the blockchain.

🖥️Client-side only

Proof generation runs in WebAssembly inside your browser. No server involved.

🔑Secret stays local

Your deposit secret and nullifier are never transmitted. They exist only in your browser's memory.

📦~200 byte proof

The final proof sent on-chain is only ~200 bytes. Small enough to fit in a single Solana transaction.

⏱️2–5 second generation

Proof computation takes 2–5 seconds in a modern browser. Verification on-chain takes under 1ms.

Frequently asked questions

Can a ZKP be faked?

No. The mathematical property of 'soundness' guarantees that a valid proof can only be generated by someone who actually knows the secret. Forging a proof would require breaking the discrete logarithm problem on BN254 — computationally infeasible with current technology.

Why not use STARKs instead of Groth16?

STARKs don't require a trusted setup, which is an advantage. But STARK proofs are ~250x larger than Groth16 proofs. On Solana, where transaction size is limited to 1,232 bytes, a STARK proof simply wouldn't fit. Groth16's compact proof size makes it the only viable option for on-chain verification on most chains.

What is BN254?

BN254 (also called alt_bn128) is an elliptic curve optimized for pairing-based cryptography. It's the curve used by Groth16 proofs and has native precompile support on Ethereum (EIP-196/197) and Solana. This means verification is fast and cheap on both chains.

Is the circuit code open source?

Yes. Mixoor's Circom circuits, the Solana program, and the EVM smart contracts are all open source and publicly auditable. The circuit defines exactly what the proof proves — you can verify it yourself.

See ZKPs in action

Try a private transfer on Mixoor and watch the proof generate in real time.

Launch Mixoor →