Technical › Who sent?

Decoy selection

Where the 15 foreign outputs in a ring come from — and why it's harder than it looks

In short

The wallet picks 15 decoys from all outputs on the blockchain so that their age is distributed like the age of real spends: many recent, few old. It uses a gamma distribution over the logarithm of age. If the decoy distribution differs from how people behave, the real input stands out statistically.

Why not just "random outputs"

Imagine decoys were picked uniformly from the whole blockchain. Then a typical decoy would be years old. But people usually spend money they got recently: days or weeks. In a ring of 16 with one fresh output and 15 old ones — guess which is real.

That is what happened in early Monero: a 2017 study (Miller, Möser et al., "An Empirical Analysis of Traceability in the Monero Blockchain") showed that the "newest input is the real one" heuristic was right most of the time for rings of that era.

The gamma distribution

Since 2018 the wallet picks a decoy's age like this:

x ~ Gamma(shape = 19.28, scale = 1/1.61)    age = eˣ seconds parameters fitted to the empirical distribution of spends in Bitcoin (Möser et al.)

The age is then converted to a block height (assuming 2-minute blocks) and a random output from that block is picked. Plus a few rules:

  • outputs younger than 10 blocks are not used (they can't be spent either — that's the lock rule);
  • selection accounts for how many outputs each block has (otherwise outputs from "empty" blocks would be picked too often);
  • duplicates and already chosen outputs are discarded.
Age of real spends (model) versus age of chosen decoys. If the curves match, the real input doesn't stand out by age.

What can go wrong

Implementation bugs

In 2021 a bug was found in wallet2's decoy selection: for some outputs the probability of choosing recent decoys was too low, so transactions spending very recent outputs stood out. It was fixed, but transactions made before the fix stay on the blockchain forever with that quirk. Third-party wallets with their own decoy selection have also repeatedly turned out to be "recognisable".

Behaviour doesn't match the model

The model was fitted on 2017 Bitcoin. Real Monero users behave differently, and the bigger the difference, the more information age leaks. Improvements were proposed (e.g. fitting the distribution to Monero's own data), but with FCMP++ the problem disappears at the root.

Flooding

If someone creates a huge number of their own outputs (such episodes were observed in 2021 and 2024), they start landing massively in other people's rings. To the attacker their own outputs are known not to be real, so they mentally cross them out, shrinking the effective ring size.

The key takeaway

Decoy selection is not cryptography but statistics. The cryptography of the ring signature guarantees the signature doesn't reveal the real input. But anything that can be learned from the choice of the ring members themselves it doesn't protect. In FCMP++ the "ring" is every output, and there is nothing to choose.