⏱️ Lectura: 13 min
You can prove to a bank that you’re of legal age without telling it your exact birth date, or prove to a server that you know a password without ever sending it over the network. That’s the promise of zero-knowledge proofs (ZKP), a cryptographic protocol that turns trust into something mathematically verifiable, without the verifier learning anything beyond the fact that your claim is true.
📑 En este artículo
- TL;DR
- What is a zero-knowledge proof and why does it matter?
- The classic analogy: Ali Baba’s cave
- How it works under the hood: the Schnorr protocol
- From interactive to non-interactive: the Fiat-Shamir transform
- zk-SNARKs and zk-STARKs: the modern generation
- Getting started: your first circuit with circom and snarkjs
- Real-world use cases
- Comparison table: zk-SNARKs vs zk-STARKs vs Bulletproofs
- Common mistakes and best practices
- Going deeper: arithmetic circuits and elliptic curves
- Frequently Asked Questions
- References
The concept was born in 1985 with a paper by Shafi Goldwasser, Silvio Micali, and Charles Rackoff, documented today in the Wikipedia entry on zero-knowledge proofs, but it only moved from pure theory to production over the last decade: blockchains, digital identity systems, and verifiable computation schemes use it every day.
TL;DR
- You understand the three properties of every zero-knowledge proof: completeness, soundness, and zero-knowledge.
- You know how to simulate the Schnorr interactive protocol in Python, the historical basis of modern ZKPs.
- You can install circom and snarkjs and generate your first zk-SNARK proof from scratch, with the exact commands.
- You can tell when a zk-SNARK, a zk-STARK, or a Bulletproof fits better depending on the use case.
- You identify the trusted setup risk in Groth16 and why zk-STARKs avoid it.
- You recognize the most common mistakes when designing arithmetic circuits for zero-knowledge proofs.
- You know where they’re used in production today: Zcash, Ethereum rollups, and identity verification.
What is a zero-knowledge proof and why does it matter?
A zero-knowledge proof is a protocol between two parties: the prover and the verifier. The prover wants to convince the verifier that they know a secret piece of data, or that a claim is true, without revealing that data or any additional detail. It matters because it separates two things that normally go together: proving something and exposing it.
In traditional systems, proving you know a password means sending it (or a hash of it) to the server. With zero-knowledge proofs, you prove you know it without the server ever seeing it, not even encrypted. That shrinks the attack surface: if the server gets breached, there are no passwords to steal.
The three properties that define a ZKP
- Completeness: if the claim is true and both parties follow the protocol, the verifier is always convinced.
- Soundness: if the claim is false, no dishonest prover can convince the verifier, except with negligible probability.
- Zero-knowledge: the verifier learns nothing beyond ‘the claim is true’: not the secret, not how it was computed.
The classic analogy: Ali Baba’s cave
The simplest way to understand a zero-knowledge proof is Ali Baba’s cave, published in 1989 by Jean-Jacques Quisquater and other authors to explain the concept without math. Picture a circular cave with a single entrance and a magic door at the back that only opens with a secret word, splitting the tunnel into two paths, A and B.
Peggy (the prover) enters through either path while Victor (the verifier) waits outside without seeing which one she chose. Victor shouts which path he wants Peggy to use to exit. If Peggy knows the secret word, she crosses the door and always comes out through the requested path, regardless of which one she entered through. If she doesn’t know it, she only gets it right half the time. Repeating the experiment twenty times, the probability that Peggy is lying and still gets it right every time drops to less than one in a million, and Victor never heard the secret word.
How it works under the hood: the Schnorr protocol
The first practical zero-knowledge protocol backed by real math, not just an analogy, is the Schnorr identification protocol, based on the discrete logarithm problem: given y = g^x mod p, it’s computationally hard to recover x even if you know y, g, and p.
The protocol has three steps, mirroring Ali Baba’s cave: commitment, challenge, and response.
sequenceDiagram
participant P as Prover
participant V as Verifier
P->>V: commitment t = g^r mod p
V-->>P: random challenge c
P->>V: response s = r + c*x
Note over P,V: V verifies g^s = t * y^c without knowing x
You can simulate the full protocol with plain Python, no external libraries, using a small prime for teaching purposes only (in production, primes with hundreds of bits or elliptic curves are used):
import random
p = 23 # small prime, for the example only
g = 5 # generator
x = 6 # Peggy's secret (the prover)
y = pow(g, x, p) # y = g^x mod p, this is public
# 1. Peggy picks a random number and sends the commitment
r = random.randint(1, p - 2)
t = pow(g, r, p)
# 2. Victor (the verifier) sends a random challenge
c = random.randint(0, 1)
# 3. Peggy responds based on the challenge
s = (r + c * x) % (p - 1)
# 4. Victor verifies without knowing x
lhs = pow(g, s, p)
rhs = (t * pow(y, c, p)) % p
print("Valid:", lhs == rhs)
The script computes y = g^x mod p as a public value (the equivalent of the magic door), generates a commitment t, receives a binary challenge c, and produces a response s. The final check, g^s = t * y^c mod p, holds if and only if Peggy knows x, without that value ever appearing in any message sent. Run the script several times: you’ll see Valid: True printed every time the secret x is correct.
From interactive to non-interactive: the Fiat-Shamir transform
The Schnorr protocol needs Victor to be present and send a live challenge, impractical for a blockchain where thousands of nodes verify the same proof without coordinating. The Fiat-Shamir transform replaces the verifier’s random challenge with the hash of the commitment: c = H(t). The prover can’t predict the hash before generating t, so cheating is still impossible, but now the proof is a single message that anyone can verify later, with no back-and-forth.
That idea, replacing interaction with a hash function, underlies almost every zk-SNARK and zk-STARK used in production today.
zk-SNARKs and zk-STARKs: the modern generation
A zk-SNARK (succinct non-interactive argument of knowledge) translates an arbitrary claim, not just ‘I know x such that y = g^x’, into an arithmetic circuit: a network of additions and multiplications over a finite field. That circuit compiles into a format called R1CS (rank-1 constraint system), and a scheme like Groth16 runs on top of it, described in the original paper by Jens Groth, producing proofs of constant size regardless of how large the circuit is.
The cost of that compactness is the trusted setup: before you can prove or verify anything, a ceremony has to generate random parameters specific to each circuit. If those intermediate values (‘toxic waste’) aren’t destroyed, someone could forge false proofs that still pass verification.
zk-STARKs, championed by StarkWare, avoid the trusted setup entirely: they rely only on hash functions, resistant to quantum computers, instead of paired elliptic curves. In exchange, STARK proofs are heavier than a Groth16 proof and take longer to verify on-chain.
Bulletproofs are a third family: they also don’t need a trusted setup, they generate proofs of logarithmic size relative to the circuit, and they underpin the confidential transactions of Monero. Their downside is that verifying them is slower than a SNARK, since they compress the verifier’s work less.
flowchart TD
A["circom circuit"] --> B["Compile to R1CS"]
B --> C["Trusted setup"]
C --> D["Proving key (zkey)"]
D --> E["snarkjs generates the proof"]
E --> F["Verifier checks proof.json"]
flowchart LR
A["Zero-knowledge proofs"] --> B["Interactive"]
A --> C["Non-interactive (NIZK)"]
C --> D["zk-SNARKs"]
C --> E["zk-STARKs"]
C --> F["Bulletproofs"]
pragma circom 2.0.0;
include "circomlib/circuits/poseidon.circom";
template KnowsSecret() {
signal input secret;
signal output hash;
component poseidon = Poseidon(1);
poseidon.inputs[0] <== secret;
hash <== poseidon.out;
}
component main = KnowsSecret();
This circuit, written in circom, declares a private input signal called secret and computes its hash with Poseidon, a hash function designed to be cheap inside an arithmetic circuit. The hash signal stays public: anyone can verify the prover knows a secret that produces that hash, without the secret ever being revealed.
Getting started: your first circuit with circom and snarkjs
To go from theory to a real proof you need three tools: the circom compiler, the snarkjs library, and a ceremony file (ptau) for the trusted setup. The official circom documentation covers the full language syntax; snarkjs generates and verifies proofs from the compiled circuit. The steps are these:
npm install -g circom snarkjs
circom knows_secret.circom --r1cs --wasm --sym
snarkjs groth16 setup knows_secret.r1cs pot12_final.ptau knows_secret_0000.zkey
snarkjs zkey export verificationkey knows_secret_0000.zkey verification_key.json
snarkjs groth16 prove knows_secret_0000.zkey witness.wtns proof.json public.json
snarkjs groth16 verify verification_key.json public.json proof.json
The expected result of the last command is the line [INFO] snarkJS: OK! in the terminal: it confirms proof.json is valid for the public inputs in public.json, without snarkjs ever needing to know the value of secret during verification.
💡 Tip: always test your circuit with snarkjs locally, without publishing it on-chain, before generating the final verification key: any change to the circuit invalidates the previous key.
Real-world use cases
Zcash, launched in 2016, was the first cryptocurrency to use zk-SNARKs in production to hide a transaction’s amount and addresses while still letting the network verify that no one is creating money out of thin air.
Ethereum added the precompile needed to verify proofs based on the BN254 curve via EIP-197, which made verifying a zk-SNARK inside a smart contract cheaper and enabled today’s rollups (zkSync, StarkNet, Polygon zkEVM) to compress thousands of transactions into a single proof published on-chain.
- Digital identity: age or citizenship verification apps that prove an attribute of a document without exposing the whole document.
- Verifiable delegated computation: a client asks an untrusted server for a heavy computation and only needs to verify the proof, much cheaper than repeating the original calculation.
Comparison table: zk-SNARKs vs zk-STARKs vs Bulletproofs
| Option | When to use it | Advantage | Limitation |
|---|---|---|---|
| zk-SNARKs (Groth16) | Cheap on-chain verification, fixed and stable circuit | Constant-size proof and very fast verification | Requires a trusted setup per circuit |
| zk-STARKs | When you don’t want to depend on a setup ceremony or elliptic curves | No trusted setup, quantum-resistant | Heavier proofs and more expensive on-chain verification |
| Bulletproofs | Confidential transactions with amounts, without needing ultra-fast verification | No trusted setup, logarithmic-size proofs | Slower verification than a SNARK |
Common mistakes and best practices
- Reusing the trusted setup across different circuits: each circuit needs its own ceremony; mixing circuits with someone else’s key invalidates the security guarantees.
- Confusing zero-knowledge with full anonymity: the proof hides the secret, but metadata like IP address or submission time can still identify the prover.
- Underestimating proof generation time: creating a proof is far more CPU- and memory-intensive than verifying one; designing giant circuits without measuring proving time is the most common mistake in production.
- Not versioning the circuit hash alongside the verification key: if the circuit changes and the verification key goes stale, new proofs fail verification with no clear explanation.
⚠️ Watch out: in zk-SNARKs with Groth16, each circuit needs its own trusted setup. If the random parameters from that ceremony (the so-called ‘toxic waste’) aren’t destroyed, whoever holds onto them can forge valid proofs without knowing the real secret.
Going deeper: arithmetic circuits and elliptic curves
Under the hood, an arithmetic circuit for zk-SNARKs operates over a finite field: a set of numbers where addition and multiplication are computed modulo a large prime, so the result always stays within the same range. Each constraint in the circuit is expressed as an equation of the form a * b = c over that field, and the full set of constraints forms the R1CS mentioned earlier.
Schemes like Groth16 use bilinear pairings over elliptic curves to compress the verification of thousands of constraints into a handful of operations. The curve most used on Ethereum is BN254 (also called alt_bn128), the same one EIP-197 added as a native precompile.
💭 Key point: verifying a zk-SNARK proof is almost always computationally cheaper than repeating the original calculation: that’s why rollups pay to publish proofs on Ethereum instead of re-executing every transaction on-chain.
📖 Summary on Telegram: View summary
Your next step: install circom and snarkjs, compile this article’s KnowsSecret circuit, and run the five commands from the ‘Getting started’ section until you see snarkJS: OK! in your own terminal.
Frequently Asked Questions
Can a ZKP prove any kind of claim?
In theory, yes, as long as the claim can be expressed as an arithmetic circuit or a relation verifiable in polynomial time. In practice, translating a complex problem into that format is the hardest part of the design.
Is zero-knowledge the same as anonymity?
No. Zero-knowledge guarantees the verifier doesn’t learn the secret used in the proof, but it doesn’t automatically hide who generates the transaction or other metadata: that depends on the whole system, not just the proof.
Do I need special hardware to generate a proof?
It’s not required, but generating zk-SNARK proofs is CPU- and memory-intensive; large circuits benefit from more cores and RAM. Verifying the proof, on the other hand, is fast even in a browser.
What exactly is the trusted setup and why does it matter?
It’s a ceremony where the random parameters needed for protocols like Groth16 are generated. If someone keeps those values instead of destroying them, they can forge false proofs that still pass verification.
Do zk-STARKs replace zk-SNARKs?
They don’t replace them, they compete on different trade-offs: STARKs avoid the trusted setup and resist quantum computers, but they generate heavier proofs than a SNARK like Groth16.
Can I practice without using a real blockchain?
Yes. circom and snarkjs run entirely locally: you can compile circuits, generate and verify proofs without publishing anything on-chain or spending gas.
References
- Wikipedia: Zero-knowledge proof: history and formal definition of the concept, from the 1985 paper.
- Zcash: zk-SNARKs technology: how Zcash uses zk-SNARKs for private transactions in production.
- Official circom documentation: language syntax for writing arithmetic circuits.
- snarkjs repository on GitHub: tool for generating and verifying zk-SNARK proofs.
- EIP-197: Ethereum precompile for verifying pairings over the BN254 curve.
📱 Enjoy this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day.
0 Comments