How Monero privacy works: sender, receiver and amount
A Bitcoin transaction names the output it spends, the address it pays and the amount. A Monero transaction hides all three, each with its own mechanism: ring signatures for the output being spent, one-time addresses for the recipient, and RingCT for the amount. None of them can be switched off. Since the network upgrade of 16 September 2017, every transaction has had to use RingCT, with a ring of at least five members, according to the upgrade table in the Monero README.
Who sent it: ring signatures
Every input in a Monero transaction points not at one earlier output but at a ring of them: the one really being spent and decoys taken from the chain. The signature proves that the signer holds the key to one member of the ring without saying which. Since the upgrade of 13 August 2022 the ring has had a fixed size of 16 members: the real output and 15 decoys.
The ring has grown with the upgrades: at least 5 members from September 2017, at least 7 from April 2018, exactly 11 from October 2018 and 16 since August 2022. The signature scheme changed as well. MLSAG gave way to CLSAG in October 2020, which the v0.17.0.0 release notes say improves verification speed and reduces transaction size.
What stops the same output being spent twice is the key image: a value derived from an output's private key, of which each output has exactly one, published with the input that spends it. A node rejects any transaction whose key image it has already seen. The key image proves that some output was spent; it does not say which member of the ring it was.
A ring is only as good as its decoys. Picked uniformly from the whole chain, they would make the real output — usually a recent one — stand out. Monero's wallet instead draws an age for each decoy from a gamma distribution, shape 19.28 and scale 1/1.61, over the logarithm of the age in seconds (wallet2.cpp). The code takes that distribution from the study of how old spent outputs really are by Malte Möser and ten co-authors (arXiv 1704.04299). It then finds roughly where on the chain an output of that age sits, and takes a random output from that block.
The same study measured the weakness it set out to fix. On the chain as it then stood, it found that about 62% of transaction inputs with one or more decoys could be deduced by elimination, and that guessing the newest member of a ring was right about 80% of the time. Both findings describe the chain of 2017, before mandatory RingCT, the larger rings of 2017 and 2018 and the age-matched decoys that wallets use now.
Who received it: one-time addresses
A Monero address is never written on the chain. For each payment the sender picks a random transaction key, r, and from it and the recipient's public view and spend keys works out a fresh one-time public key for the output. Only the holder of the private view key can recognise the output as theirs, and only the holder of the private spend key can spend it. Two payments to the same address land on two unrelated keys, and subaddresses let one wallet hand a different address to every payer without anyone else being able to tell that they belong together.
Finding those outputs means testing every new output on the chain. Since August 2022 each output carries a one-byte view tag, the first byte of a hash of the shared secret, so a wallet can discard all but about 1 in 256 of the outputs that are not its own before doing the expensive part. In its author's tests that cut scanning time by 30 to 40% (pull request #8061).
How much: RingCT
Amounts are hidden by Ring Confidential Transactions, allowed from 5 January 2017 and required from 16 September 2017. Each amount is replaced by a Pedersen commitment, which hides the value but still lets anyone check that the inputs equal the outputs plus the fee. A range proof shows that each committed amount is not negative; without one, a negative output could create coins nobody would see.
Range proofs are where Monero transactions lost most of their weight. Bulletproofs, enabled in October 2018, cut transaction size by at least 80%, according to Moneropedia's entry on them, and Bulletproofs+ replaced them in August 2022.
What a transaction still shows
Hidden is not the same as invisible. Every Monero transaction still shows when it entered a block, how many inputs and outputs it has, the fee it paid and the members of each ring. Since November 2019 every transaction has needed at least two outputs, so a lone output no longer stands out. A new output can only be spent after 10 blocks — CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE in the code — and the decoy picker skips outputs younger than that, since no real spend could use them.
The protocol also says nothing about the network. The node that first broadcasts a transaction can be seen by its peers, which is what Dandelion++ and a node over Tor or I2P are for. And the keys that unlock all of this can be shared on purpose: a view key shows every incoming payment to whoever holds it.
Mining plays no part in any of this. The proof of work, RandomX, decides who adds the next block, not what a transaction hides; random.directory covers it.
What comes next
The ring is the part of the design most often questioned, because its anonymity set is 16. The FCMP++ guide covers the upgrade that replaces it with a proof over every output on the chain, and Monero vs Zcash compares Monero's approach with the shielded pools of Zcash.
How Monero privacy works: sender, receiver and amount: questions
Is every Monero transaction private?
Every transaction uses ring signatures, one-time addresses and hidden amounts, and none of the three can be turned off. What a transaction does not hide is its timing, its number of inputs and outputs, and its fee; the IP address that broadcast it is a question for the network layer, not the protocol.
How many decoys does a Monero transaction use?
Each input sits in a ring of 16: the real output and 15 decoys. The ring size has been fixed at 16 since the network upgrade of 13 August 2022; before that it was 11 from October 2018.
Can someone see my Monero balance from my address?
No. An address is never written on the chain: each payment goes to a one-time key that only the wallet's private view key can link back to it. Anyone given that view key can see incoming payments, so it should be shared only on purpose.
Source: The Monero source code and its network-upgrade table (monero-project/monero on GitHub), with Zero to Monero 2.0 checked against the source
More Monero privacy guides
FCMP++: full-chain membership proofs
FCMP++ replaces Monero's 16-member rings with a proof over every output on the chain. How the proof works, what Carrot adds, and what Monero privacy gains.
Monero vs Zcash: two privacy models compared
Monero privacy is on for every transaction, in 16-member rings; Zcash hides only shielded transactions, but among every note in the pool. How the two compare.
Dandelion++: where a transaction began
Rings hide the sender on the chain, not the node that first broadcast the payment. How Dandelion++ adds Monero privacy on the network: stem, fluff and embargo.