The Anonymity That Actually Holds: What Zcash and Monero Prove, and How Each Proof Breaks
A mechanism-level teardown of on-chain anonymity: how Zcash's zk-SNARK proof and Monero's ring-signature ambiguity work, and the exact break inside each.
Permalink1. Two Coins, Two Hidden Break Conditions
For roughly the first two years of Zcash's life, a flaw buried in its zero-knowledge proof meant that someone holding the right secret could have minted counterfeit shielded coins out of nothing, and no node, auditor, or observer could ever have caught them at it [1]. In those same years, a team of academics sat down with the Monero blockchain -- a ledger marketed as untraceable -- and correctly guessed the real coin behind roughly four out of five "hidden" spends [2]. Both coins were sold as anonymous. Each shipped with a break condition sealed inside its own proof.
That pairing is no coincidence; it is the whole argument of this article. Zcash and Monero are the two most serious answers to one hard question: how do you move value on a public ledger so sender, recipient, and amount stay hidden while everyone can still verify it? They answered in opposite ways. Zcash writes a mathematical proof that a valid, authorized, balanced spend is hidden somewhere in a set. Monero makes the real spend one of many plausible candidates and proves nothing about which is real. They are not merely different mechanisms but different break conditions, chosen at design time.
Anonymity on a public ledger is not asserted; it is proved, and a proof is only as strong as the one assumption whose failure is its break condition. Zcash proves a spend is valid and hidden, so its break is a flaw in the proof system (soundness) or in the crowd the proof ranges over (the shielded set). Monero writes a statistical proof that the spend is one of N, so its break is anything that makes N collapse -- a guessable real spend. Name the proof, and you have named the break.
The distinction that organizes everything below is hidden versus provably hidden. A magician's coin is hidden: you cannot see it, but nothing stops you reasoning about where it went. A provably hidden coin comes with a guarantee -- a statement of exactly what an adversary cannot learn, under exactly which assumption. The value of the guarantee is the value of that assumption. When the assumption is "this proof system is sound," the break is a soundness bug. When it is "the real spend is indistinguishable from its decoys," the break is a statistical tell.
So this is not a chronology or a feature comparison. It is one claim, argued twice: the anonymity that holds is the anonymity you can prove, and each proof hands you its own break condition for free. The path runs from two 2013 designs that forked at the root, through each coin's construction and documented failure, to the moment the two failures turn out to be the same shape -- and then to a 2026 break no proof, however perfect, can touch. First, though, we need one precise definition of what on-chain anonymity has to prove.
2. What On-Chain Anonymity Actually Has to Prove
Start with what everyone half-knows: a Bitcoin address is not a name, but it is not anonymous either. Because the ledger is public and permanent, every payment an address ever makes or receives is visible forever, and addresses can be linked to each other and, often, to a person. That is pseudonymity, not anonymity.
The gap between the two -- the taxonomy of pseudonymity, unlinkability, and untraceability -- is the subject of the companion post "Pseudonymous Was Never Anonymous," which this article assumes rather than repeats, along with how Bitcoin's base layer gets deanonymized in practice. Here we pick up where it leaves off: given that a transparent pseudonymous ledger is not enough, what would a genuinely private payment have to accomplish?
The answer is more demanding than "hide who I am," because a payment system also has to stay honest. On a public ledger, anyone can verify the rules: no coins are created from nothing, no coin is spent twice, and only the owner can spend. A privacy design cannot quietly drop those checks. It has to hide the transaction and keep proving it valid at the same time.
Both coins inherit Bitcoin's basic accounting shape. Money exists as discrete outputs; a transaction consumes some existing outputs as inputs and creates new outputs, and the network must confirm that the inputs were real, unspent, and authorized, and that value in equals value out. Transparency makes those checks trivial, because everything is in the clear. Privacy makes them hard, because now the checks have to pass over data nobody is allowed to see. That tension -- verify what you cannot look at -- is the technical heart of the field.
The anonymity set is the collection of possibilities an adversary cannot distinguish among after doing their best analysis. If your spend is genuinely indistinguishable from any of candidates, your anonymity set has size and the adversary's best guess is correct with probability . The nominal anonymity set is the size the design advertises; the effective anonymity set is what survives an adversary's heuristics. Almost every result in this article is a gap between those two numbers.
There are only two known ways to make those four requirements hold at once without a trusted referee, and they define the two halves of this article. The first is to attach a cryptographic proof: hide the spend behind commitments, then publish a zero-knowledge argument that a valid spend exists in a committed set, revealing nothing else. The second is to manufacture ambiguity: present the real spend alongside decoys so that, to any observer, it is merely one of many, and design the rest of the transaction so validity still checks out. Zcash took the first road, Monero the second.
The pseudonymity-versus-anonymity taxonomy, and the specifics of Bitcoin base-layer deanonymization, belong to the companion post and are deliberately out of scope here. This piece is about the two constructions that sit above that boundary, and about the precise adversary each one faces -- which is also why the recurring question below is never just "is it private?" but "private against whom, within which set?" That framing comes from the corpus's threat-model post, "Secure Against Whom."One more thing follows from the definition of an anonymity set: a privacy claim is meaningless until you name the adversary. "Anonymous" against a casual block-explorer user is a different property than "anonymous" against a well-funded chain-analysis firm with heuristics, timing data, and network telemetry. Keep that question -- anonymous against whom? -- in view; it is the question that will still be standing at the end, after every proof has been named and every break condition counted. Two designs answered all of this in 2013, months apart, and they disagreed from the first line about what "hidden" should mean.
3. Two Bloodlines That Forked in 2013
Both design philosophies were born in the same year, months apart, as two answers to the same frustration. By 2013 the state of the art in on-chain privacy was mixing: hand your coins to a tumbler that shuffles them with other people's, or coordinate a joint transaction (CoinJoin) so the graph shows only "one of paid one of ." Mixing works operationally but proves nothing.
A tumbler can steal your coins or keep logs that unwind every link; and CoinJoin, as the Zerocash authors noted, "can thus only mix small volumes of coins amongst users who are currently online, is prone to denial-of-service attacks, and requires effort to find mixing partners" [3]. Both 2013 lineages set out to remove the trusted third party and the online coordination -- and split immediately on how.
A ring signature lets someone sign a message on behalf of a set (a "ring") of public keys, so that any verifier is convinced that some member of the ring signed, but cannot tell which one. Unlike a group signature, there is no manager and no way to later revoke the anonymity. The ring is assembled ad hoc by the signer, who is the only real member; the rest are decoys pulled from the ledger.
The ambiguity lineage came first in spirit. The CryptoNote v2.0 whitepaper, published in 2013 under the pseudonym Nicolas van Saberhagen, defined untraceability through ring signatures (a spend is signed by one of possible keys) and unlinkability through one-time addresses (each payment lands at a freshly derived key) [4]. In April 2014 a community launched Monero as a durable, no-premine deployment of that design [5]. Nothing here is proved about which output was really spent; the security comes entirely from the spend being lost in a crowd the spender assembles.
CryptoNote's own history is murky: the v2.0 whitepaper is dated 17 October 2013, but a v1.0 carries a December 2012 date and researchers have questioned whether the timestamps were backdated. "Nicolas van Saberhagen" is a pseudonym whose identity has never been established. This article treats "2013 (CryptoNote v2.0)" as the design of record and does not attribute the work to any named person [4].The proof lineage started the same year with the opposite instinct. Zerocoin (Miers, Garman, Green, and Rubin, 2013 IEEE Symposium on Security and Privacy) asked whether anonymity could be proved rather than merely obscured: mint a coin by publishing a commitment that joins a public accumulator of minted coins, then spend it by proving in zero knowledge that you know the opening of some commitment in it, plus a serial number to stop double-spends [6]. The idea was correct and the proofs enormous -- more than 45 kB each and around 450 ms to verify, far too heavy to be a currency at scale [3].
That cost set up the successor. Zerocash (Ben-Sasson, Chiesa, Garman, Green, Miers, Tromer, and Virza, 2014 IEEE Symposium) replaced the heavy accumulator proof with a succinct one -- a zk-SNARK over note commitments and nullifiers that hides sender, recipient, and amount while proving the transaction balanced and authorized [3]. That construction became Zcash.
A zk-SNARK is a Zero-Knowledge Succinct Non-interactive ARgument of Knowledge. "Zero-knowledge" means the proof reveals nothing beyond the truth of the statement; "succinct" means the proof is small and fast to check regardless of how complex the statement is; "non-interactive" means it is a single message with no back-and-forth; and "argument of knowledge" means a computationally bounded prover cannot produce a convincing proof unless the statement is true and they actually know a witness for it.
A second footnote to Zerocoin's story matters more than the proof size, because it foreshadows the whole article. Zerocoin carried a break with nothing to do with the soundness of its zero-knowledge proof. Ruffing, Thyagarajan, Ronge, and Schroeder showed in 2018, in a paper aptly titled "Burning Zerocoins," that a network attacker could permanently destroy honest users' coins -- a "cryptographic denial-of-spending attack" that, in the authors' words, "does not contradict the formal security analyses" of the original scheme, because it exploited a property nobody had modeled [7].
Hold onto that shape: a construction can be proved, in the exact sense its authors intended, and still break at the edge of what the proof was ever about. "Proved" and "unbreakable" are different words, and the distance between them is where most of this article lives.
There is one more seam worth flagging now, because it detonates in Section 5. Zerocash's succinct proof rode on the pairing-based zk-SNARK template introduced by Pinocchio (Parno, Howell, Gentry, and Raykova, 2013), and the concrete construction Zcash shipped -- known as BCTV14, after Ben-Sasson, Chiesa, Tromer, and Virza -- inherited its security argument from that lineage rather than carrying a dedicated proof of its own [8] [9]. Hold that thought too.
Diagram source
flowchart TD
Root["2013: a public ledger is transparent and pseudonyms are linkable"]
Root --> Proof["Proof school: hide the spend, prove it is valid anyway"]
Root --> Amb["Ambiguity school: make the real spend one of N candidates"]
Proof --> P1["Zerocoin 2013 (accumulator + zero-knowledge membership)"]
P1 --> P2["Zcash Sprout 2016 (BCTV14 zk-SNARK)"]
P2 --> P3["Zcash Sapling 2018 (Groth16)"]
P3 --> P4["Zcash Orchard 2022 (Halo 2, no trusted setup)"]
Amb --> A1["CryptoNote 2013, Monero 2014 (ring signatures + stealth addresses)"]
A1 --> A2["RingCT 2017 (amounts hidden)"]
A2 --> A3["Mandatory RingCT 2017; ring 11 + Bulletproofs 2018"]
A3 --> A4["CLSAG 2020; ring 16 + Bulletproofs+ 2022"]
P4 --> Law["AMLR Article 79 legal ban, effective 10 July 2027"]
A4 --> Law Here is the thesis in historical form: the split in proof strategy is the split in break condition, present at birth. Choose to hide by proof, and you inherit two ways to fail -- an unsound proof, or a set too small to hide in. Choose to hide by ambiguity, and you inherit exactly one -- a real spend that becomes guessable. Everything that follows is those two inheritances playing out. The proof school promised something that sounds impossible, so we start there: how do you prove a spend is valid without revealing which spend it is?
4. The Zcash Construction: Proving a Spend You Cannot See
Zcash's shielded pool is best understood as a machine for making one specific statement believable without evidence you can inspect. To spend, you must convince every node that a real, unspent coin you own is being consumed and that value balances -- while showing them none of: which coin, whose coin, or how much. It pulls this off with three moving parts and a proof that ties them together.
The first part is the note. A shielded coin is a note -- a bundle of (value, owner key, randomness) -- that never appears on chain in the clear. What appears is its commitment.
A note commitment is a cryptographic commitment to the contents of a note (its value, the owner's key, and a random nonce). Like a sealed envelope, it binds the sender to those exact contents (they cannot change them later) while hiding them from everyone. Every new note's commitment is appended to a global, append-only Merkle tree, so "this note exists" becomes "this commitment is a leaf in the tree."
The second part solves double-spending without identity. In a transparent chain you stop double-spends by pointing at the specific coin being consumed; here you are not allowed to point at anything. The answer is the nullifier.
A nullifier is a deterministic tag derived from a note's secret data such that each note maps to exactly one nullifier, but the nullifier reveals nothing about which note produced it. Spending publishes the nullifier; the network records it in a set and rejects any future transaction that reuses it. This voids the note -- provably, and exactly once -- without ever disclosing which leaf of the commitment tree was spent.
The third part is the zk-SNARK that ties it together. A shielded spend carries a succinct zero-knowledge proof of the following statement: I know an opening of a note whose commitment is a leaf in the tree; its nullifier is correctly derived; I am authorized to spend it; and the input value equals the output value [3] [10]. A verifier who checks that proof learns only one bit -- the statement is true -- and nothing about the note, the owner, or the amount.
Diagram source
flowchart TD
Note["A note commitment sits in the append-only tree (value + owner key + randomness)"]
Auth["Spender demonstrates spend authorization"]
Null["Nullifier is published, voiding the note without naming it"]
SNARK{"zk-SNARK verifies: membership + authorization + nullifier + balance"}
Out["Fresh output note commitments are appended"]
Note --> SNARK
Auth --> SNARK
SNARK --> Null
SNARK --> Out That is the model. Everything else in Zcash's history is a change of proving system underneath the same model -- and each change was forced by a break condition, which is exactly the thesis. The pool has run three proving systems.
The first, Sprout, shipped at the 2016 mainnet launch on the BCTV14 zk-SNARK, encoding a spend proof in 288 bytes [11] [10]. The protocol specification encodes the Sprout proof in 288 bytes and the Sapling Groth16 proof in 192 bytes; the older literature sometimes quotes 296 bytes for Sprout because of a difference in point encoding [10]. Its parameters came from a multi-party "trusted setup" ceremony -- which introduces the one idea most people get wrong.
A pairing-based zk-SNARK needs public parameters called a structured reference string (SRS), generated from secret randomness. That secret is the "toxic waste": anyone who retains it can forge proofs of false statements. A trusted-setup ceremony has many participants jointly generate the SRS so that the waste is safe as long as at least one participant honestly destroys their share. Note carefully what the waste can and cannot do.
The second system, Sapling (October 2018), kept the model but swapped in Jens Groth's 2016 pairing-based zk-SNARK, "Groth16," whose proof is just three group elements -- 192 bytes -- and, unlike BCTV14, carries a rigorous, independently reviewed security analysis [12] [10] [13]. Purpose-built curve and circuit engineering cut the prover's cost by roughly 80% in time and 98% in memory versus Sprout, which is what first made shielding feasible on a phone [14]. Sapling still needs a per-circuit trusted setup; it fixed the soundness of the proof, not the existence of the ceremony.
The third system, Orchard, activated at Network Upgrade 5 on 31 May 2022 [15] and specified in ZIP 224 [16], removed the ceremony altogether. It is built on Halo 2, whose inner-product argument descends from the 2019 Halo proposal for recursion without a trusted setup [17] and, in the Electric Coin Company's words, "did not require a trusted setup and had relatively small proof sizes, at the cost of poor verification performance" [18]. Orchard runs over the Pallas and Vesta ("Pasta") curves [10] -- a transparent, no-setup instantiation, not a universal or updatable SNARK.
| Proving system (era) | Spend proof size | Trusted setup | Curve | Status |
|---|---|---|---|---|
| Sprout / BCTV14 (2016) | 288 bytes [10] | Yes, per-circuit | pairing-friendly (BN) | Superseded 2018 |
| Sapling / Groth16 (2018) | 192 bytes, three elements [10] | Yes, per-circuit | BLS12-381 + Jubjub | Active |
| Orchard / Halo 2 (2022) | Logarithmic in circuit size, no fixed constant [18] | No | Pallas / Vesta ("Pasta") | Active default |
Read the table against the thesis and the shape is clear: this is a literal mathematical proof, so the guarantee is exactly as strong as the proof's soundness. Sapling and Orchard improved the guarantee by improving the proof -- better analysis, then no ceremony. But that framing carries an unavoidable implication. If your anonymity is a proof, then a false proof is a broken anonymity. This proof shipped in 2016. In 2018, someone discovered it was unsound. What does it even mean for a proof to be false?
5. When the Proof Itself Is Unsound: The BCTV14 Break
On 1 March 2018, Ariel Gabizon, a cryptographer at the Electric Coin Company, was studying the BCTV14 setup and noticed something that should not have been there. The parameter-generation procedure, as described in the paper, "mistakenly produces extra elements that violate the soundness of the proving system" [1]. An honest prover ignores those elements and never notices. But their presence, in the postmortem's exact words, "allows a cheating prover to circumvent a consistency check, and thereby transform the proof of one statement into a valid-looking proof of a different statement" [1].
"The parameter setup algorithm ... mistakenly produces extra elements that violate the soundness of the proving system." -- Electric Coin Company postmortem [1]
A proof system is sound if no prover can produce a convincing proof of a false statement, except with negligible probability. For succinct arguments like zk-SNARKs the guarantee is computational soundness: no prover bounded to feasible computation can forge a proof, assuming some hardness problem holds. Soundness is the promise that "verified" means "true." Break it, and a verified proof means nothing.
What were those extra elements, concretely? This is the part summaries skip, and the mechanism is the whole lesson. Gabizon's own write-up (IACR ePrint 2019/119) states it precisely: the flaw "stems from including redundant elements in the CRS," the structured reference string -- elements the Pinocchio/PGHR13 template had deliberately excluded and that are, in his words, "vital not to expose" [20]. Publishing them let a cheating prover do something specific and devastating: "creating a proof of knowledge for any public input given a valid proof for some public input" [20].
In other words, hand the attacker one honestly generated proof, and the redundant CRS elements let them retarget it to a statement of their choosing. The BCTV14 paper's own errata later acknowledged it "fixes an error in Appendix B, found by Ariel Gabizon" [9]. With the excluded elements kept out, the construction was secure in an idealized generic-group model -- but the deployed parameters kept them in [20].
Read what that retargeting buys an attacker. The shielded pool's whole security rests on the proof meaning "this spend is valid and balanced." If a cheating prover can attach that same certificate to a false statement, they can prove a spend is balanced when it is not -- creating shielded value from nothing. This is not a privacy bug; it is a counterfeiting bug, and it would have been invisible.
The proof was real mathematics, and it was false. The human story around that sentence is small and tense. Gabizon confirmed the flaw with his colleague Sean Bowe, and the two briefed chief executive Zooko Wilcox and engineer Nathan Wilcox. For a time just four people knew that the currency they were building could, in principle, be counterfeited without a trace [1].
The exploit required data that survived only in the original Sprout multi-party ceremony transcript -- the extra elements are present there even though the shipped parameter files omit them -- so the company quietly removed the transcript, maintaining a cover story that it had been lost to accidental deletion [1].
The fix was not a patch, because you do not patch an unsound proof. You replace it. Sapling's move to the independently analyzed Groth16 circuit, activated 28 October 2018, migrated shielded value off the flawed system entirely, and only then -- on 5 February 2019, eleven months after discovery -- did the company disclose publicly [1].
The postmortem records how far the flaw reached: the same bug had propagated into Fuchsbauer's subversion-zero-knowledge adaptation and the ADSNARK scheme, and the Sprout parameters themselves came from a multi-party protocol (BGG17) that carried the extra elements forward [21] [22] [23]. Parno's 2015 note had already flagged a separate soundness problem in the same construction [19]. The mathematics was reviewed, published, deployed -- and still wrong for years.
Diagram source
flowchart TD
Setup["BCTV14 parameter setup emits redundant CRS elements (unused by honest provers)"]
Setup --> Honest["Honest prover ignores the extra elements"]
Setup --> Cheat["Cheating prover keeps the extra elements"]
Honest --> HCheck{"Consistency check"}
HCheck --> Sound["Only true statements pass: valid spends"]
Cheat --> Bypass{"Consistency check is circumvented"}
Bypass --> Forge["Some-input to any-input: a false statement passes, undetectable in-pool counterfeiting"] That is one way a proof-based system breaks: the proof is wrong. There is a second, quieter way that survives even a perfect proof. A zero-knowledge proof of membership only hides you inside the set it ranges over -- and for Zcash, that set is whatever value has actually been shielded.
Kappos, Yousaf, Maller, and Meiklejohn measured the deployed pool: only 14.96% of transactions ever interacted with the shielded pool at all, roughly 3.9 million ZEC flowed in and back out in a near-perfect reflection dominated by founders and miners, and a handful of simple usage heuristics cut the effective anonymity set by 69.1% [24]. The proof was sound; the crowd it proved over was thin.
The Kappos measurement is blunt about who was in the pool: about 3,901,124 ZEC were deposited and 3,788,889 withdrawn in a "near-perfect reflection ... accounted for almost fully by the founders and miners," with roughly 76.7% of pool deposits coming from miners [24]. A shielded pool used mostly as a pass-through by two known cohorts is a small crowd to hide in.So the proof school's break condition has two faces, and we can write the first row of a table we will fill in completely later. What Zcash proves, and how, and the exact assumption whose failure is the break:
| System | What is proved | By what mechanism | Break condition | Anonymous against whom / within which set |
|---|---|---|---|---|
| Zcash Sapling | "This spend is valid, authorized, balanced, and hidden in the shielded set" | Groth16 zk-SNARK over BLS12-381, note commitments and nullifiers | An unsound proof (the BCTV14 precedent) or a trusted-setup leak -- and a thin real set | A chain observer, within the set that actually shielded (small in practice) [24] |
| Zcash Orchard | The same statement, with no ceremony to trust | Halo 2 inner-product argument (transparent) plus recursion | An unsound argument or discrete-log break; the trusted-setup class is removed, but the set is still thin | A chain observer, within the Orchard set (opt-in) [18] |
The proof school's failure was a discrete event with a date and four names. The other school never had a single break at all. Its anonymity eroded slowly, one measurement at a time -- and to see why, we need Monero's construction.
6. The Monero Construction: Hiding in a Crowd You Assemble Yourself
Monero makes the opposite bet. It proves nothing about which output was spent. Instead it makes the real spend one of many plausible candidates and arranges the rest of the transaction so validity still checks out. Four primitives work in lockstep to hide sender, recipient, and amount while proving the payment is honest.
Sender ambiguity comes from the ring signature. To spend an output, you assemble a ring of that one real output plus a set of decoys pulled from the chain, and produce a signature proving that some ring member authorized the spend, without revealing which [4]. A verifier sees a valid signature over a ring of, today, sixteen members, and has no cryptographic handle on which one is real.
That immediately raises a problem: if you never say which output you spent, how does the network stop you from spending it twice? The answer is a linkable ring signature and its key image.
A key image is a deterministic tag derived from the real signing key, published with every Monero spend. It is unique to the key that actually signed, but reveals nothing about which ring member that was. The network keeps a set of seen key images and rejects any transaction that reuses one. This is the exact analogue of Zcash's nullifier: it prevents double-spends by catching reuse, not by naming the coin.
Recipient unlinkability comes from stealth addresses.
A stealth, or one-time, address is a fresh public key that the sender derives for each payment from the recipient's long-term address, using a shared secret only the two of them can compute. Funds land at that one-time key, never at the published address, so two payments to the same person produce two unrelated on-chain destinations that no observer can link.
Amount hiding comes from RingCT (Ring Confidential Transactions), which bolts Pedersen-committed amounts and range proofs onto the ring signature [25] [26].
A Pedersen commitment to a value with random blinding factor is , where and are fixed group generators with no known relationship. It perfectly hides (any is consistent with any value, given the right ) and is additively homomorphic: . That homomorphism is what lets a verifier confirm inputs minus outputs equal zero without learning a single amount.
The homomorphism is worth seeing directly, because it is how a public network checks that hidden numbers balance. Commit two amounts with independent blinding factors, and the product (in a multiplicative group) or sum (in the additive elliptic-curve notation Monero actually uses) of the commitments equals the commitment to the summed values -- so "the amounts balance" becomes a single equality on commitments.
// Illustrative only: real Monero uses a large elliptic-curve group with additive
// notation C = vG + rH. Here we use modular exponentiation in a prime field, where
// the same homomorphism reads as a product of commitments. The point is the algebra.
const p = 2n ** 61n - 1n; // a Mersenne prime, big enough for the demo
const g = 5n, h = 7n; // two public generators with no known relationship
const modpow = (base, exp, m) => {
let r = 1n; base %= m;
while (exp > 0n) { if (exp & 1n) r = (r * base) % m; base = (base * base) % m; exp >>= 1n; }
return r;
};
// commit(v, r) = g^v * h^r mod p -- hides v behind the blinding factor r
const commit = (v, r) => (modpow(g, v, p) * modpow(h, r, p)) % p;
const v1 = 40n, r1 = 12345n; // amount 1 and its blinding factor
const v2 = 25n, r2 = 67890n; // amount 2 and its blinding factor
const C1 = commit(v1, r1);
const C2 = commit(v2, r2);
const Csum = commit(v1 + v2, r1 + r2);
console.log('C1 . C2 mod p =', (C1 * C2) % p);
console.log('commit(v1+v2, r1+r2) =', Csum);
console.log('do they match? ', ((C1 * C2) % p) === Csum);
// A verifier tests whether input commitments minus output commitments cancel,
// confirming value in = value out, while learning neither v1 nor v2. Press Run to execute.
Range proofs finish the job. Without them, you could "balance" a transaction by committing to a negative output, minting value while the equation still holds.
A range proof is a zero-knowledge proof that a committed value lies in a valid range (say, between zero and ) without revealing the value. It closes the negative-amount loophole in a confidential-transaction scheme. Monero's first range proofs (Borromean) were large; Bulletproofs replaced them with logarithmic-size proofs that need no trusted setup, and Bulletproofs+ shrank them further.
Monero's Borromean range proofs were large and dominated transaction size. In October 2018 they were replaced by Bulletproofs -- logarithmic-size range proofs with no trusted setup -- which cut the average Monero transaction size by at least 80% [27] [28] [29]. A shorter successor, Bulletproofs+, shipped in August 2022 [30].
Diagram source
flowchart TD
Ring["Assemble a ring: 1 real output + 15 decoys from the chain"]
Sig["Linkable ring signature proves one member signed, not which"]
KI["Key image published, unique to the real key, catches double-spends"]
Stealth["Payment lands at a fresh one-time stealth address"]
Amt["Amount hidden by a Pedersen commitment + Bulletproof+ range proof"]
Ring --> Sig
Sig --> KI
Sig --> Stealth
Stealth --> Amt The deployment history is a ratchet, and every notch is dated in the project's source of record: RingCT went live as optional in January 2017, became mandatory in September 2017, the ring size was fixed at 11 in October 2018, and raised to 16 in August 2022 [5]. Alongside those, the signature scheme moved from MLSAG to the smaller, faster CLSAG in October 2020, with the same one-of-N guarantee [31].
That the signature could be swapped so freely -- MLSAG to CLSAG, with no change to the security story -- is itself a tell. A 2024 Cypher Stack review even found two gaps in CLSAG's formal security proof, with, in the project's words, no effect on "the Monero implementation of CLSAG, so users are not at risk" [31]. The break in Monero was never the signature scheme; it was always the decoys. You can optimize or even re-prove the signature all you like and the anonymity is unaffected, because the anonymity lives in the ring, not in the algebra of the signature [31].Now hold this construction next to Zcash's and the deep difference is exact. Zcash makes a cryptographic proof, so it can have a soundness flaw. Monero makes a statistical proof of ambiguity, so there is no soundness flaw to find -- there is no cryptographic assertion about which output was spent that could be false. The guarantee is instead exactly as strong as the crowd is indistinguishable. Which raises the only question that can break it: what if the decoys do not look like real spends?
7. When the Crowd Collapses: Monero's Statistical Break
Nobody forged a Monero coin. The break was subtler and, for a privacy coin, arguably worse: researchers simply guessed which ring member was real, and were right most of the time. Because the hiding is statistical, the whole guarantee is only as good as the decoys, and early Monero decoys were terrible.
The first lever was the ring of size one. In early Monero many inputs used no decoys, and a ring with one member is not a ring -- the real spend is named outright. Kumar, Fischer, Tople, and Saxena showed in 2017 that these zero-mixin inputs are not just individually traceable; they cascade [32]. Once an output is proven spent, it can be eliminated as a decoy everywhere it later appears. Each elimination can shrink another ring to one candidate, which is itself then proven spent, poisoning the next ring, and so on. One collapse propagates through the graph.
Diagram source
flowchart TD
Z["Zero-mixin input, ring size 1: the real spend is named outright"]
Z --> P1["That output is now known to be spent"]
P1 --> R1["A later ring used it as a decoy, so eliminate it"]
R1 --> C1["That ring shrinks and its real input can be forced"]
C1 --> R2["The forced output poisons the next ring in turn"]
R2 --> C2["The crowd collapses ring by ring, a chain reaction"] Then the temporal attack. Because early decoys were sampled with no regard for how old real spends tend to be, the real input was almost always the newest member of its ring. The "guess the newest" heuristic was correct about 80% of the time over all transactions with at least one mixin, and still 81% even among transactions meeting the minimum ring size mandated after September 2017 [2].
"The real input is usually the 'newest' input ... [we can] guess the real input with 80% accuracy over all transactions with 1 or more mixins." -- Moser et al., PoPETs 2018 [2]
The simulation below shows the mechanism and, just as importantly, the fix. With decoys sampled uniformly from all history, a genuinely recent real spend stands out as the newest ring member almost every time. Sample decoys from the same recency distribution that real spends follow, and the real output stops standing out -- the attacker's success falls back toward the one-in-sixteen a random guess would give.
// Toy model of Monero decoy selection. Outputs are created over time; a higher index
// means a newer output. Real spends tend to be RECENT. We build a ring of the real
// output plus decoys, then apply the attacker's heuristic: guess the newest member.
const RING = 16; // ring size (Monero has used 16 since August 2022)
const TRIALS = 50000;
const POOL = 100000; // number of past outputs available as decoys
function gaussian() { // Box-Muller, standard normal
const u = Math.random(), v = Math.random();
return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
}
// Real spends skew recent: age is a small offset back from the newest output.
const realAge = () => Math.floor(Math.abs(gaussian()) * 1800);
function runSim(pickDecoy) {
let correct = 0;
for (let t = 0; t < TRIALS; t++) {
const realIndex = POOL - 1 - realAge(); // a recent real output
const ring = [realIndex];
while (ring.length < RING) ring.push(pickDecoy());
const guess = Math.max(...ring); // heuristic: newest is real
if (guess === realIndex) correct++;
}
return (100 * correct / TRIALS).toFixed(1);
}
// 1) Naive UNIFORM decoys (early Monero): spread across all history.
const uniform = () => Math.floor(Math.random() * POOL);
// 2) REALISTIC decoys: same recency skew as real spends.
const realistic = () => POOL - 1 - realAge();
console.log('Uniform decoys -> guess-newest correct:', runSim(uniform), '%');
console.log('Realistic decoys -> guess-newest correct:', runSim(realistic), '%');
console.log('Random baseline -> 1 in 16:', (100 / RING).toFixed(1), '%'); Press Run to execute.
That is why every Monero mitigation reads like a direct answer to the attack that named it, which is the pattern this failure-mode style prizes. Mandatory RingCT (September 2017) meant RingCT outputs could no longer be spent by zero-mixin inputs, structurally ending the cascade at its source. Zero-decoy spends were eliminated. The ring size was fixed at 11, then 16, so no transaction advertises a distinctive ring size and the crowd is uniform. And decoy selection moved to a gamma distribution designed to mimic the real spend-age curve, directly targeting the guess-newest tell [5] [2].
Why a gamma distribution, specifically?
Guess-newest works because real spends cluster at a particular age -- recent, but not the newest block, since coins need confirmations and people spend on human timescales. Uniform decoy selection ignores that shape, so the real input's age stands out. A gamma distribution is a two-parameter family tunable to the observed spend-age curve, so a decoy drawn from it looks, age-wise, like a plausible real spend. The mitigation is not "more decoys" but "decoys drawn from the right distribution" -- which is why sixteen realistic decoys beat sixty-four obvious ones, and why decoy realism, not ring size, is the live problem [2].
Note the honest epistemics, because they are the whole point. None of this made Monero unconditionally private. It is a ratchet, not a proof: each turn raises the cost of the statistical attack, but the guarantee stays statistical and the crowd stays bounded at sixteen. Decoy realism versus improving classifiers is a permanent arms race.
So the Monero row of our recurring table writes itself: what is proved is one-of-N ambiguity; the mechanism is ring signature plus stealth address plus RingCT; the break is that N collapses when a real spend becomes guessable; and it holds against a chain observer within a ring of sixteen, only as far as the decoys are realistic. Two coins, two breaks -- one a flaw in a proof, the other a collapse of a crowd. Are those different problems, or the same problem wearing two masks?
8. Head-to-Head: Cryptographic Anonymity Versus Statistical Anonymity
Put the two proofs on one table and the symmetry is startling. Here is the recurring device, now filled for both coins and both frontier systems: what each proves, by what mechanism, the single assumption whose failure is its break, and the adversary and set it holds against.
| System | What is proved | By what mechanism | Break condition | Anonymous against whom / within which set |
|---|---|---|---|---|
| Zcash Sapling | This spend is valid, authorized, balanced, and hidden in the shielded set | Groth16 zk-SNARK over BLS12-381, commitments and nullifiers | An unsound proof (the BCTV14 precedent) or a trusted-setup leak; and a thin real set | A chain observer, within the set that actually shielded, small in practice [24] |
| Zcash Orchard | The same statement, with no ceremony to trust | Halo 2 inner-product argument (transparent) plus recursion | An unsound argument or discrete-log break; the trusted-setup class is removed; the set is still thin [18] | A chain observer, within the Orchard set (opt-in) |
| Monero RingCT | The real spend is one of sixteen ring members; amounts balance and are in range | CLSAG linkable ring signature, stealth addresses, RingCT with Bulletproofs+ | The N collapses: guessable decoys, chain-reaction near 62% and guess-newest near 80% before mitigation; a bounded ring [2] | A chain observer, within a ring of sixteen, only as far as decoys are realistic |
| Monero FCMP++ | The real spend is one of every output on the chain | Curve-Trees membership proof, Generalized Bulletproof, Generalized Schnorr | An unsound Generalized Bulletproof or discrete-log break; soundness proofs still being formalized [33] | A chain observer, within the entire output set, roughly 100,000,000 [34] |
The engineering trade-offs behind those rows are what a protocol designer actually weighs.
| Dimension | Zcash Sapling | Zcash Orchard | Monero RingCT | Monero FCMP++ |
|---|---|---|---|---|
| Anonymity model | Cryptographic proof | Cryptographic proof, transparent | Statistical ambiguity | Cryptographic membership proof |
| Trusted setup | Yes, per-circuit | No | No | No |
| Proof / tx size | About 192-byte proof, about 2.8 KB tx [10] [35] | Logarithmic proof, larger than Groth16 [18] | At least 80% smaller than pre-Bulletproofs [28] | About 2.9 KB proof, about 4 KB tx [35] |
| Verify cost | Constant, cheapest | Heavier, near-constant when batched [18] | Grows with ring size times inputs | About 80 ms single tx, about 5 ms batched over 100 [35] |
| Anonymity set (nominal) | All shielded notes | All Orchard notes | 16 per input [34] | About 100,000,000 [34] |
| Anonymity set (effective) | Far below nominal, founder and miner dominated [24] | Same structural risk, opt-in | 16 or fewer, often collapsible [2] | Equal to nominal, the full chain |
| Post-quantum | No, pairings | No, discrete log | No, discrete log | Privacy forward-secret against a DL oracle, soundness not PQ [34] |
| Default privacy | Opt-in | Opt-in | Always-on | Always-on |
| Deployed? | Yes (2018) | Yes (2022, default) | Yes (current) | No (audited, in development) |
Now read the four rows against the thesis and the deep structure appears. The two Zcash rows and the FCMP++ row all say the same thing -- a proof is exactly as strong as its soundness and the set it ranges over. The Monero RingCT row is the ambiguity variant -- a crowd is exactly as strong as it is indistinguishable.
And a startling diagonal runs through the table. As Monero moves from RingCT down to FCMP++, it stops being statistical and becomes a proof: the anonymity set jumps from sixteen to the whole chain, but the break changes species, from "a guessable decoy" to "an unsound Generalized Bulletproof." Monero, chasing a bigger crowd, walks straight into the proof school's break condition. Meanwhile Zcash, moving from Sapling to Orchard, keeps the proof but discards the trusted-setup break entirely.
The two schools are converging on the same ideal from opposite corners. Monero is adding a proof to escape a collapsible crowd; Zcash is removing a trusted setup to shed a soundness liability. The shared destination is a transparent, sound, full-set membership proof -- and the moment Monero reaches it, it inherits exactly the break condition Zcash spent a decade learning to fear. Name the proof, and you have named the break.
Diagram source
flowchart TD
S["Zcash Sapling: a sound proof, but a trusted setup"]
O["Zcash Orchard: proof kept, trusted setup removed"]
R["Monero RingCT: statistical ambiguity over a ring of 16"]
F["Monero FCMP++: a membership proof over the whole chain"]
Ideal["Transparent, sound, full-set membership proof"]
S --> O
O --> Ideal
R --> F
F --> Ideal If both roads lead to the same ideal proof, a natural question follows: is there a ceiling neither road can pass? There is, and it is a theorem rather than an engineering wart.
9. The Ceiling and the Floor: What No Construction Can Escape
Suppose the proof is perfect and the trusted setup is gone. Are you finally, unconditionally anonymous? No -- and the reasons are theorems, not engineering warts. Three limits sit under every system in this article, and naming them turns the whole comparison from a horse race into a map.
The first is a ceiling. You cannot be more anonymous than the crowd your proof ranges over. If the set an argument covers has size , then an adversary with no other information already guesses the real spender with probability at least , and every usable heuristic only pushes that up. This -anonymity ceiling is why FCMP++ is a genuine theoretical advance rather than an optimization: raising the ring from sixteen to the whole chain does not tune a constant, it raises the ceiling itself, from to [34] [35].
It is also why the empirical papers matter so much. Moser's and Kappos's results are precisely measurements of the gap between the nominal ceiling and the effective set the adversary actually faces [2] [24]. The ceiling is set by design; the distance you fall below it is set by details.
The second is a floor. The succinct systems here are not "proofs" in the strongest sense at all; they are arguments.
A proof is sound even against an adversary with unlimited computing power: a false statement simply cannot be proved. An argument is only computationally sound: forging a proof of a false statement is possible in principle but requires breaking a hardness assumption, so no feasible attacker can do it. Every succinct system deployed by Zcash or Monero is an argument. Its soundness rests on a problem being hard -- pairings and knowledge-of-exponent for Groth16, discrete log for inner-product arguments, Bulletproofs, ring signatures, and Curve Trees.
That floor is load-bearing, and BCTV14 is the proof of it. The argument was accepted mathematics, and it was unsound, and the only thing standing between "unsound argument" and "silent counterfeiting" was the aggregate turnstile [1]. An argument is a promise conditional on an assumption, and a conditional promise can fail two ways: the assumption is false, or the reduction to it is buggy.
The third limit is the one place the field genuinely closed a practical gap -- and it is the single most mis-stated fact in popular writing about these systems, so state it exactly. You will often read that pairing-based SNARKs "provably require" a structured reference string while transparent systems use "no setup at all." Both halves are wrong, and the correct statement is older and more fundamental.
Non-interactive zero-knowledge was defined by Blum, Feldman, and Micali in 1988 relative to a shared common reference string -- setup was baked into the model from the start [36]. And Goldreich and Oren proved in 1994 that non-interactive zero-knowledge in the plain model, with no shared setup at all, exists only for trivial (BPP) languages -- so a non-interactive zero-knowledge argument for any non-trivial language must have some common setup: a common reference string or a random oracle [37]. Every system here therefore has a setup. The only question is whether you must trust it.
A pairing SNARK like Groth16 uses a structured reference string whose secret randomness is toxic waste, which is why it needs a ceremony. Transparent arguments -- Halo 2's inner-product argument, Bulletproofs, Curve Trees -- use a public, untrusted setup: nothing-up-my-sleeve generators and public hash functions that hide no secret and need no ceremony. The advance is not "no setup"; it is "setup nobody has to trust," and it now comes at transaction sizes within a small constant of trusted-setup SNARKs -- roughly 4 KB (with a 2.9 KB membership proof) against Sapling's 2.8 KB transaction [18] [35] [29]. The toxic-waste break class is not mitigated here; it is removed.
One more subtlety belongs here, because it is routinely conflated with the setup question. SNARK succinctness itself cannot be proved secure from standard falsifiable assumptions by black-box reductions -- Gentry and Wichs showed any such argument must instead rest on non-falsifiable "knowledge" assumptions or an idealized model like the random oracle [38]. That is a statement about the assumptions a short proof needs, not about whether its reference string is structured.
Put the three together and the honest reading is bracing: a succinct argument leans on a strong assumption to exist at all, on a hardness assumption to be sound, and on some public setup to be non-interactive. Removing the trusted part of the setup, as Halo 2 and Curve Trees do, is a real and hard-won advance -- but it removes exactly one of those three dependencies, not all of them.
Every one of these limits is the thesis restated at the level of theory: the ceiling is the set your proof ranges over, the floor is the soundness your argument rests on, and even the one problem the field solved was solved only in part -- the setup stayed, but the trust in it went away. So if the ceiling is the set and the floor is soundness, where do today's systems still leak?
10. Where the Proofs Still Leak
Every unsolved break condition in this field is just a proof not yet strong enough, or a leak the proof was never meant to cover. Five stand out.
Decoy realism (Monero). Choosing ring decoys so that no statistical test -- age, chain-reaction, timing -- beats is the entire security of the ambiguity model, and it is never provably perfect. Mandatory RingCT, zero-decoy elimination, larger rings, and gamma-distributed selection blunted the Moser attacks but did not end the arms race [2]. The clean resolution is to stop selecting decoys at all, which is exactly what a full-chain membership proof does [34].
Shielded-pool adoption (Zcash). Making the effective set approach the nominal pool is an incentive and user-experience problem, not a cryptographic one. A perfectly sound proof over a thin, founder-dominated pool yields little real anonymity [24]. Cheaper shielding and better wallets help, but as long as transparent addresses exist and shielding is opt-in, the pool's composition stays a live analysis target.
The network layer. No on-chain proof hides who broadcast a transaction. IP and timing metadata sit entirely outside both constructions, and they defeat privacy at the peer-to-peer layer for Zcash and Monero alike. Dandelion++ raises the cost of first-relayer deanonymization, but remote-node and light-wallet usage still leaks [41].
Dandelion++ is a network-layer defense, not a cryptographic one: it splits transaction propagation into a randomized "stem" phase before the public "fluff" broadcast, so the first node to see a transaction cannot easily be assumed to be its origin [41]. It is a genuine improvement, but it protects a surface -- who broadcast -- that ring signatures and zk-SNARKs never claimed to cover, which is exactly why it belongs on a separate layer of the map below.Formal verification of the deployed circuit. BCTV14 was a flaw in the construction as described, invisible to expert review for years, so auditing the abstract scheme is not enough -- you must audit the deployed circuit. The 2024 Cypher Stack review of CLSAG found two gaps in its security proof with no impact on the Monero implementation, a reassuring near-miss [31]. Less reassuring: FCMP++'s Generalized Bulletproof soundness proofs were still being written at proposal time, and that risk runs straight back to Gabizon's redundant-CRS-element discovery [33] [20]. The gap between "implemented" and "proven sound" is the BCTV14 gap, reopened for the successor.
Post-quantum private payments. No deployed private coin is post-quantum sound at competitive cost, and a store-now-decrypt-later adversary threatens privacy today. FCMP++'s forward secrecy protects privacy against a future discrete-log oracle but leaves soundness classical [34]. This is the same pressure that the corpus's "how X would break" post-quantum entries trace across ML-KEM, Falcon, and SLH-DSA, and it is the most pressing open problem here.
Diagram source
flowchart TD
OnChain["On-chain proof: is the argument sound?"]
Set["Anonymity set: is it large and real?"]
Net["Network layer: who broadcast the transaction?"]
Circuit["Circuit: is the deployed code actually sound?"]
PQ["Post-quantum: does a quantum solver break it?"]
Legal["Legal: are you allowed to trade it at all?"]
Q{"Anonymous against whom?"}
OnChain --> Q
Set --> Q
Net --> Q
Circuit --> Q
PQ --> Q
Legal --> Q The frontier tying several of these together is FCMP++, and it deserves an honest description because the hype around it is wrong in a specific way. It would replace the ring of sixteen with a zero-knowledge membership proof over every output on the chain, taking each input's anonymity set from sixteen to roughly 100,000,000, with no trusted setup, on a transparent Curve-Trees accumulator [34] [35] -- with a reference implementation taking shape in the monero-oxide Rust codebase [42]. In doing so it converts the ambiguity school into the proof school and inherits its break condition: the soundness of a Generalized Bulletproof [33].
One of the quiet pleasures of this history is a revival. The inner-product argument "famously used in Bulletproofs" -- the ambiguity school's 2018 tool for shrinking Monero's range proofs -- is the same primitive Zcash built Halo 2 on in 2022 to eliminate trusted setup. The efficiency trick of one school became the other school's route to a cleaner proof [18] [27].Even a perfect proof over the entire chain, though, runs into one break condition that no cryptography can touch -- and by 2026 it is the sharpest of them all.
11. The Anonymity You Are Allowed to Prove
After a decade of tightening the cryptography, the sharpest live break condition for both coins is not a soundness flaw or a collapsible ring. It is a statute. Through 2024, 2025, and 2026, major exchanges operating in the European Union -- Binance, Kraken, OKX, and others -- delisted or restricted Monero, Zcash, and similar assets [44]. The delistings are real and current. What matters for anyone reasoning clearly is attributing them to the right instruments, because the popular shorthand "MiCA banned privacy coins" is wrong on every word.
| Instrument | What it actually does | Key date | Effect on privacy coins |
|---|---|---|---|
| MiCA, Regulation (EU) 2023/1114 | A market-conduct, transparency, and licensing regime for crypto-asset service providers | CASP provisions apply from 30 December 2024 | Compliance pressure and cost; contains no privacy-coin ban [45] |
| AMLR Article 79, Regulation (EU) 2024/1624 | Prohibits obliged entities from keeping anonymous crypto-asset accounts and anonymity-enhancing coins | Applies from 10 July 2027 | The actual prohibition, binding service providers, not individuals [46] |
| FATF Recommendation 16 and EU TFR 2023/1113 | Requires originator and beneficiary information to travel with each transfer | In force in the EU | The global driver that makes traceless coins hard to service [47] [48] |
Zcash and Monero are the canonical constructions, but the same regime sweeps up their relatives, each a variant of the two schools here. Dash offers non-custodial CoinJoin mixing through masternodes -- ambiguity by shuffling, closer to the 2013 pre-history than to either coin [49]. Firo's Lelantus Spark is a no-trusted-setup membership-proof scheme, a descendant of the very Zerocoin that was later broken, rebuilt as a proof [50]. Beldex is a Monero-lineage CryptoNote/RingCT coin [51]; Pirate Chain uses always-shielded Zcash-Sapling zk-SNARKs [52]. Every one inherits a construction from this article and, from 10 July 2027, the same legal break [46].
The thesis reaches its final form here. A provable anonymity and a permitted anonymity are different properties, and the second is not something cryptography can supply. No cryptographic advance postpones the 10 July 2027 deadline [46]. There is a genuine irony in the one technical escape hatch: both systems already support selective disclosure -- Zcash and Monero view keys -- that let a holder voluntarily reveal a transaction to an auditor or regulator [53] [54].
But that is precisely the disclosure these constructions spent a decade proving you need not make. Using it to satisfy a compliance adversary is not a break in the proof; it is a decision to stop relying on the proof. A fittingly honest place to land before turning the whole argument into something you can use.
The delisting notices themselves are anchored here to the MiCA and AMLR primary texts rather than to individual exchange announcements, several of which are bot-gated or geo-restricted and could not be independently confirmed. The legal drivers are the load-bearing claim; any single exchange notice is illustrative [45] [46] [44].So how should an engineer actually reason about any of this on a Monday morning?
12. Reasoning About On-Chain Anonymity
One question collapses almost all of the marketing around privacy coins: anonymous against whom, within which set, under which proof? If a claim cannot answer those, it is not an engineering statement. The whole article reduces to a four-line checklist you can run against any construction, deployed or promised.
| Ask | What you are establishing | Zcash answer | Monero answer |
|---|---|---|---|
| What is proved? | A cryptographic soundness proof, or a statistical claim of ambiguity | A zk-SNARK: the spend is valid and hidden | Ambiguity: the spend is one of N |
| Over which set? | The nominal set size, and the effective size after heuristics | All shielded notes, but thin in practice [24] | A ring of 16, effective and often smaller [2] |
| Against which adversary? | Casual observer, chain-analysis firm, network watcher, or a compliance authority | Sound vs a chain analyst if you shield; opt-in | Always-on vs a chain analyst; realism-bounded |
| What is the break condition? | The single assumption whose failure collapses the guarantee | Unsound proof, or a set too small | A guessable real spend |
Run that checklist and the design choices sort themselves:
- Want the smallest proof and cheapest verification, and can accept a trusted-setup lineage plus opt-in privacy? Zcash Sapling / Groth16.
- Want strong hiding with no ceremony to trust, plus recursion for scale, still opt-in? Zcash Orchard / Halo 2 -- today's recommended pool [18].
- Want always-on, no-setup, uniform privacy now, and can accept a bounded ring of sixteen with residual decoy risk? Monero RingCT / CLSAG / Bulletproofs+ -- deployed.
- Want a full-chain, transparent membership proof, the honest endgame of the ambiguity school? Monero FCMP++ -- once it is deployed and its Generalized Bulletproof soundness is proven, which is not today [33] [35].
- Facing a compliance or legal adversary (an exchange or custodian)? None is "permitted"; the break is regulatory, not cryptographic -- AMLR Article 79 and the FATF Travel Rule, not a soundness bug [46].
How to sanity-check whether your own ZEC ever actually shielded
The single most common Zcash misconception is "I hold ZEC, so I am private." Privacy in Zcash is a property of transactions that entered the shielded pool, not of the asset. To check, trace each receiving address on a block explorer: an address beginning with t is transparent, and every amount and counterparty is public; a z (Sapling) or unified address that received a shielded output is the only value the zk-SNARK actually hides.
If your coins arrived at a transparent address and never made a shielded (z-to-z) hop, the proof never ranged over them -- you have the pool's cryptography and none of its anonymity. The same logic is why Kappos et al. could measure the pool from outside: the transparent-to-shielded boundary is itself public [24].
Frequently asked questions
Are Zcash transactions always private?
No. Zcash is transparent by default, and the shielded proof only ranges over value that has actually entered the shielded pool -- which most ZEC never does. Measurements found only about 15% of transactions ever touched the pool, and simple heuristics cut the effective anonymity set substantially [24]. Privacy in Zcash is real but opt-in, and it is only as large as the set you actually join.
Is Monero untraceable?
It is statistically ambiguous, not unconditionally untraceable. Early decoy selection made the real spend guessable -- roughly 62% of inputs with a mixin were deducible by chain-reaction, and the newest ring member was the real one about 80% of the time [2]. Mandatory RingCT, larger rings, and realistic decoy sampling mitigated these attacks, but the guarantee remains statistical and the ring is bounded at sixteen.
Did MiCA ban privacy coins?
No. MiCA (Regulation (EU) 2023/1114) is a market-conduct and licensing framework with no privacy-coin prohibition; it is pressure, not a ban [45]. The actual prohibition is AMLR Article 79 (Regulation (EU) 2024/1624), which bars service providers from keeping anonymity-enhancing coins and applies from 10 July 2027 [46].
Is a trusted setup a backdoor?
No. The trusted-setup secret ("toxic waste") governs the proof system's soundness, not its privacy. A leaked setup would let an attacker counterfeit value, which is serious, but it is not a key that deanonymizes anyone's transactions. Transparent systems like Halo 2 and Bulletproofs remove the trusted part of the setup entirely [18].
Was the Zcash counterfeiting bug ever exploited?
There is no evidence it was. In-pool counterfeiting would have been cryptographically undetectable, but the turnstile that meters net value crossing the pool boundary showed no aggregate anomaly, and Sapling superseded the flawed circuit before the vulnerability was disclosed on 5 February 2019 [1].
Does a bigger ring always help?
Only against the right adversary. A larger, uniform ring blunts size-fingerprinting and the guess-newest heuristic, but the guarantee stays statistical and bounded, and the realism of the decoys matters more than the raw number. Sixteen realistic decoys beat sixty-four obvious ones [2].
Is FCMP++ live, and does it make Monero fully anonymous?
Not as of 2026. The current release is 0.18.5.1 "Fluorine Fermi," with ring signatures still in consensus [43]. FCMP++ would raise each input's anonymity set to roughly 100,000,000, but it trades Monero's statistical assumption for the soundness of Generalized Bulletproofs, whose proofs were still being formalized at proposal time [33] [34].
The Monday-morning version of the whole argument: before you trust any on-chain anonymity, name the proof. If it is a cryptographic proof, its break is unsound math or a set too small to hide in. If it is statistical ambiguity, its break is a guessable real spend. Then name the adversary, and check whether the effective set -- not the advertised one -- still hides you against that adversary. Name the proof, and you have named the break.
Two coins, two constructions, two break conditions that turned out to be the same statistical shape underneath -- and, waiting past every proof, a legal wall no cryptography can climb. The lesson is not that one coin is more private than the other. It is that "private" was never a single property. It is a proof with an assumption attached, and the assumption is always nameable. That is the discipline worth keeping: not "is it anonymous?" but "what does it prove, and what breaks when that proof's one assumption fails?"
Name the proof, and you have named the break.
Study guide
Key terms
- Anonymity set
- The set of candidates an adversary cannot distinguish among; success probability is at best 1 over its effective size.
- zk-SNARK
- A succinct, non-interactive zero-knowledge argument that a statement is true, revealing nothing else.
- Note commitment
- A hiding, binding commitment to a shielded coin, appended to an append-only tree.
- Nullifier
- A deterministic per-note tag that voids a note to prevent double-spends without revealing which note.
- Trusted setup
- Public parameters generated from secret toxic waste; if the waste survives it enables counterfeiting, not deanonymization.
- Soundness
- The promise that a verified proof means a true statement; broken by BCTV14 in 2018.
- Ring signature
- A signature proving some member of a signer-chosen set authorized a message, without revealing which.
- Key image
- A unique tag of the real spending key that catches Monero double-spends without naming the output.
- Stealth address
- A one-time destination key derived per payment so payments to one person stay unlinkable.
- Pedersen commitment
- An additively homomorphic commitment C = vG + rH that hides an amount while letting a verifier check balance.
Comprehension questions
Why is a Zcash soundness break a counterfeiting risk rather than a privacy risk?
Soundness governs whether false statements can be proved; a broken proof lets an attacker mint unbalanced value, while hiding still comes from commitments and zero knowledge.
What single assumption is Monero's ambiguity resting on?
That the real spend is statistically indistinguishable from its decoys; guessable decoys collapse the set.
Why does FCMP++ inherit the proof school's break condition?
It replaces the ring with a full-chain membership proof, so its security now depends on the soundness of a Generalized Bulletproof rather than on decoy realism.
References
- (2019). Zcash Counterfeiting Vulnerability Successfully Remediated. https://electriccoin.co/blog/zcash-counterfeiting-vulnerability-successfully-remediated/ ↩
- (2018). An Empirical Analysis of Traceability in the Monero Blockchain. https://petsymposium.org/popets/2018/popets-2018-0025.php ↩
- (2014). Zerocash: Decentralized Anonymous Payments from Bitcoin. https://doi.org/10.1109/SP.2014.36 ↩
- (2013). CryptoNote v 2.0. https://bytecoin.org/old/whitepaper.pdf - Attributed to the pseudonym Nicolas van Saberhagen; identity unknown, dating disputed. ↩
- (2024). monero-project/monero (scheduled network upgrades, source of record). https://github.com/monero-project/monero ↩
- (2013). Zerocoin: Anonymous Distributed E-Cash from Bitcoin. https://doi.org/10.1109/SP.2013.34 ↩
- (2018). Burning Zerocoins for Fun and for Profit: A Cryptographic Denial-of-Spending Attack on the Zerocoin Protocol (IACR ePrint 2018/612). https://eprint.iacr.org/2018/612 ↩
- (2013). Pinocchio: Nearly Practical Verifiable Computation (IACR ePrint 2013/279). http://web.archive.org/web/20260503003449/https://eprint.iacr.org/2013/279 ↩
- (2014). Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture. https://www.usenix.org/conference/usenixsecurity14/technical-sessions/presentation/ben-sasson ↩
- (2024). Zcash Protocol Specification. https://zips.z.cash/protocol/protocol.pdf ↩
- (2016). Zcash begins. https://electriccoin.co/blog/zcash-begins/ ↩
- (2016). On the Size of Pairing-Based Non-Interactive Arguments. https://doi.org/10.1007/978-3-662-49896-5_11 ↩
- (2018). Cultivating Sapling: New Crypto Foundations. https://electriccoin.co/blog/cultivating-sapling-new-crypto-foundations/ ↩
- (2018). Cultivating Sapling: Faster zk-SNARKs. https://electriccoin.co/blog/cultivating-sapling-faster-zksnarks/ ↩
- (2022). Zcash v5.0.0 (NU5) release. https://github.com/zcash/zcash/releases/tag/v5.0.0 ↩
- (2022). ZIP 224: Orchard Shielded Protocol. https://zips.z.cash/zip-0224 ↩
- (2019). Halo: Recursive Proof Composition without a Trusted Setup (IACR ePrint 2019/1021). http://web.archive.org/web/20251117154113/https://eprint.iacr.org/2019/1021 ↩
- (2020). Explaining Halo 2. https://electriccoin.co/blog/explaining-halo-2/ ↩
- (2015). A Note on the Unsoundness of vnTinyRAM's SNARK (IACR ePrint 2015/437). http://web.archive.org/web/20260314195616/https://eprint.iacr.org/2015/437 ↩
- (2019). On the Security of the BCTV Pinocchio zk-SNARK Variant (IACR ePrint 2019/119). https://eprint.iacr.org/2019/119 ↩
- (2017). Subversion-Zero-Knowledge SNARKs (IACR ePrint 2017/587). http://web.archive.org/web/20260101154436/https://eprint.iacr.org/2017/587 ↩
- (2014). ADSNARK: Nearly Practical and Privacy-Preserving Proofs on Authenticated Data (IACR ePrint 2014/617). http://web.archive.org/web/20260414232220/https://eprint.iacr.org/2014/617 ↩
- (2017). A Multi-Party Protocol for Constructing the Public Parameters of the Pinocchio zk-SNARK (IACR ePrint 2017/602). http://web.archive.org/web/20260430085127/https://eprint.iacr.org/2017/602 ↩
- (2018). An Empirical Analysis of Anonymity in Zcash. https://www.usenix.org/conference/usenixsecurity18/presentation/kappos ↩
- (2016). Ring Confidential Transactions (Monero Research Lab MRL-0005). https://web.getmonero.org/resources/research-lab/pubs/MRL-0005.pdf ↩
- (2015). Confidential Transactions. https://elementsproject.org/features/confidential-transactions ↩
- (2018). Bulletproofs: Short Proofs for Confidential Transactions and More. https://doi.org/10.1109/SP.2018.00020 ↩
- (2018). Moneropedia: Bulletproofs. https://web.getmonero.org/resources/moneropedia/bulletproofs.html ↩
- (2018). Bulletproofs. https://crypto.stanford.edu/bulletproofs/ ↩
- (2020). Bulletproofs+: Shorter Proofs for a Privacy-Enhanced Distributed Ledger (IACR ePrint 2020/735). http://web.archive.org/web/20260430081143/https://eprint.iacr.org/2020/735 ↩
- (2024). CLSAG Security Proof Revisions. https://www.getmonero.org/2024/03/08/clsag-security-proof-revisions.html ↩
- (2017). A Traceability Analysis of Monero's Blockchain. https://doi.org/10.1007/978-3-319-66399-9_9 ↩
- (2024). FCMP++ Development (Monero CCS proposal). https://ccs.getmonero.org/proposals/fcmp++-development.html ↩
- (2024). Full-Chain Membership Proofs Development. https://www.getmonero.org/2024/04/27/fcmps.html ↩
- (2023). Curve Trees: Practical and Transparent Zero-Knowledge Accumulators. https://www.usenix.org/conference/usenixsecurity23/presentation/campanelli ↩
- (1988). Non-Interactive Zero-Knowledge and Its Applications. https://doi.org/10.1145/62212.62222 ↩
- (1994). Definitions and Properties of Zero-Knowledge Proof Systems. https://doi.org/10.1007/BF00195207 ↩
- (2011). Separating Succinct Non-Interactive Arguments from All Falsifiable Assumptions (IACR ePrint 2010/610). https://eprint.iacr.org/2010/610 ↩
- (2004). Direct Anonymous Attestation. https://doi.org/10.1145/1030083.1030103 ↩
- (2024). NIST Releases First 3 Finalized Post-Quantum Encryption Standards. https://www.nist.gov/news-events/news/2024/08/nist-releases-first-3-finalized-post-quantum-encryption-standards ↩
- (2018). Dandelion++: Lightweight Cryptocurrency Networking with Formal Anonymity Guarantees. https://arxiv.org/abs/1805.11060 ↩
- (2024). monero-oxide (Rust reimplementation hosting the fcmp++ branch). https://github.com/monero-oxide/monero-oxide ↩
- (2026). Monero Release Blog (0.18.5.1 Fluorine Fermi). https://www.getmonero.org/blog/ ↩
- (2024). Kraken to Delist Monero (XMR) in the European Economic Area Due to Regulatory Pressure. https://cryptobriefing.com/kraken-monero-delisting-eea/ - Dated trade-press account of the 2024 EEA privacy-coin delistings, naming Binance, Kraken, and OKX and the MiCA/AML drivers; corroboration only, with the load-bearing legal claims anchored to the MiCA/AMLR primaries. ↩
- (2023). Regulation (EU) 2023/1114 (Markets in Crypto-Assets, MiCA). https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32023R1114 ↩
- (2024). Regulation (EU) 2024/1624 (Anti-Money-Laundering Regulation), Article 79. https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32024R1624 ↩
- (2023). Regulation (EU) 2023/1113 (Transfer of Funds Regulation, the EU Travel Rule). https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32023R1113 ↩
- (2024). Virtual Assets (Recommendation 16 / Travel Rule). http://web.archive.org/web/20260709030805/https://www.fatf-gafi.org/en/topics/virtual-assets.html ↩
- (2024). Dash Features: CoinJoin. https://docs.dash.org/en/stable/docs/core/guide/dash-features-coinjoin.html ↩
- (2021). Lelantus Spark: Secure and Flexible Private Transactions (IACR ePrint 2021/1173). https://eprint.iacr.org/2021/1173 ↩
- (2024). Beldex (Monero-lineage CryptoNote/RingCT coin). https://github.com/Beldex-Coin/beldex ↩
- (2024). Pirate Chain (always-shielded zk-SNARKs, Komodo/Zcash Sapling). https://github.com/PirateNetwork/pirate ↩
- (2018). Viewing Keys: Selective Disclosure for Zcash. https://electriccoin.co/blog/viewing-keys-selective-disclosure/ ↩
- (2024). View Key (Moneropedia). https://web.getmonero.org/resources/moneropedia/viewkey.html ↩