Zero-knowledge proofs are the cryptographic primitive that makes private blockchains possible. They allow one party — the prover — to convince another party — the verifier — that a statement is true, without revealing any information beyond the fact that the statement is true. That sounds abstract, so let's start with the cave.
The Cave Analogy
Imagine a cave shaped like a ring. There is a single entrance, and somewhere in the middle of the ring is a door that can only be opened with a secret password. A and B stand at the entrance. A wants to prove to B that A knows the password, without actually saying the password aloud.
A walks into the cave and arbitrarily takes either the left or right path. B, who cannot see which path A took, shouts a random command: "Come out from the left!" or "Come out from the right!" If A knows the password, A can always comply — open the door if necessary and walk out the correct side. If A doesn't know the password and happened to take the wrong path, A is stuck. Repeat this experiment enough times: after 20 rounds, the probability that a password-ignorant A happened to guess correctly every time is 1 in 2²⁰ — about one in a million. B becomes convinced A knows the password. A never said it.
This is the essence of a ZK proof: repeated challenge-response interactions that statistically compel belief in a fact, without revealing the fact itself.
From Interactive to Non-Interactive
The cave analogy describes an interactive proof, which requires back-and-forth communication. Practical blockchain systems need non-interactive proofs — a single artifact that anyone can verify without the prover being present. The key insight enabling this is the Fiat-Shamir heuristic: the prover simulates the verifier's random challenges by hashing their own commitments. The hash function acts as a "random oracle," making the challenges unpredictable and binding them to the specific proof.
Non-interactive proofs can be published to a blockchain and verified by any node independently, making them ideal for on-chain applications.
Commitment Schemes: The Mathematical Foundation
Before generating a ZK proof, a prover must "commit" to their secret value. A cryptographic commitment scheme has two properties: it is binding (you cannot change the committed value later) and hiding (the commitment reveals nothing about the underlying value). The simplest example is a hash: commit(v) = H(v || randomness). Given the commitment, you cannot determine v; but given v and the randomness, anyone can verify the commitment is consistent.
In ZK circuits, commitments are used to represent private note values. The circuit proves properties about committed values — for example, that the sum of inputs equals the sum of outputs — without revealing the values themselves.
How zk-SNARKs Work
SNARK stands for Succinct Non-interactive ARgument of Knowledge. "Succinct" means the proof is small and fast to verify, regardless of the complexity of the underlying computation. A zk-SNARK proceeds roughly as follows:
- Arithmetization. The computation to be proven (e.g., "I know a valid spend key for this note commitment") is converted into an arithmetic circuit: a set of constraints over field elements.
- Polynomial encoding. The circuit's constraints are encoded as polynomial equations. Satisfying the circuit is equivalent to finding polynomials that satisfy these equations.
- Commitment to polynomials. The prover commits to these polynomials using a polynomial commitment scheme (KZG, IPA, etc.), producing short cryptographic commitments.
- Challenge and evaluation. The verifier (simulated via Fiat-Shamir) challenges the prover to evaluate the polynomials at a random point. The prover provides the evaluation and a proof that it is consistent with the commitment.
- Verification. The verifier checks the evaluation proofs against the commitments in constant time, regardless of circuit size.
The Trusted Setup Problem
Classical SNARKs (Groth16, the system used in early Zcash) require a "trusted setup" ceremony. During setup, a structured reference string (SRS) is generated from a random secret. If anyone ever learns that secret, they can forge proofs — creating valid-looking proofs for false statements. This creates a perpetual security assumption: the original ceremony participants must have destroyed their secret contributions.
This is not merely theoretical. If a trusted setup is compromised, counterfeit transactions could be created silently, with no way to detect them from the outside. The attack is sometimes called the "toxic waste" problem.
Why UltraHonk Eliminates It
UltraHonk, the proving backend in Aztec's Barretenberg library, uses a universal structured reference string (SRS) that has already been generated and publicly audited. Unlike Groth16, the SRS is not circuit-specific — the same parameters work for any circuit up to the maximum supported size. There is no per-circuit ceremony, no new toxic waste when circuits are upgraded, and no trusted third parties involved in protocol updates.
Sanect's shielded pool circuits are written in Noir (a Rust-like ZK circuit language maintained by Aztec) and compiled to UltraHonk proofs via Barretenberg. The universal SRS means that when we upgrade the circuit — adding features, fixing bugs, or optimizing constraints — we recompile and deploy a new verifier contract. The SRS does not change. No ceremony is re-run.
The practical implication is that UltraHonk proofs can be verified by any node without trusting that some ceremony participants behaved honestly years ago. Security derives from the hardness of the discrete logarithm problem over BN254, not from a historical human event.
What a ZK Proof in Sanect Proves
In Sanect's shielded pool, a spend proof demonstrates the following facts, simultaneously, without revealing the underlying values:
- The prover knows the spending key associated with a valid note commitment in the Merkle tree.
- The note has not been previously spent (the nullifier is fresh).
- The sum of input note values equals the sum of output note values plus any declared fee.
- The output note commitments are correctly formed from the declared output values and recipient keys.
The verifier — every validator in the network — checks the proof against the published Merkle root and nullifier set. It learns nothing about the sender's identity, receiver's identity, or the transfer amount. It only learns that the transaction is valid. This is what "privacy by default" means at the cryptographic layer: validity without disclosure.