Dandelion++: how Monero hides where a transaction began

The Dandelion++ paper

Everything in how Monero privacy works happens on the chain. Before a transaction gets there it has to cross the peer-to-peer network, and in a plain broadcast the node that created it is the first to tell its neighbours. The Dandelion++ paper's abstract states the problem: broadcast lets third-party observers link transactions to the IP addresses that sent them. A well-connected observer that hears a transaction first from one address has a good guess at where it came from.

Stem and fluff

Dandelion++, by Giulia Fanti, Shaileshh Bojja Venkatakrishnan, Surya Bakshi, Bradley Denby, Shruti Bhargava, Andrew Miller and Pramod Viswanath, submitted to arXiv on 28 May 2018, splits propagation in two. In the stem phase the transaction passes from node to node along a single line; at each hop the node decides at random whether to keep passing it on or to switch to the fluff phase, an ordinary broadcast. An observer who hears the fluff learns where the stem ended, not where it began. The paper claims near-optimal information-theoretic guarantees against large-scale deanonymisation, including by adversaries who disobey the protocol.

Monero added Dandelion++ in v0.16.0.0, Nitrogen Nebula, released on 28 May 2020 (pull request #6314). Pull request #7025, merged on 1 December 2020, set the fluff probability and the embargo it uses today.

The parameters

They are in cryptonote_config.h, with the reasoning in levin_notify.cpp and tx_pool.cpp.

Fluff probability: 20%. At every hop there is one chance in five that the stem ends, so a stem is on average about five hops long. The five is our arithmetic from the 20%, not a figure the code states.

Two stems per epoch. Each node picks 2 outgoing peers to stem through and keeps them for an epoch of ten minutes plus a random amount of up to 30 seconds, so that the stem path does not change with every transaction and give itself away by comparison.

Fluff delays. A node that fluffs does not send at once. It flushes transactions after a Poisson-distributed delay that averages 5 seconds towards inbound peers and half that towards outbound ones, as Bitcoin Core does.

The embargo: 39 seconds on average. A node that passes a transaction along the stem starts a timer. If the transaction has not come back to it as a fluff when the timer runs out, it fluffs the transaction itself, which stops a malicious node from swallowing stems. The average comes from the paper's formula, worked out in tx_pool.cpp with k = 5, ε = 0.10 and a hop time of 175 ms.

Your own transactions always stem. A transaction the node created itself is never fluffed by it directly.

Over Tor and I2P

A node that relays its transactions through Tor or I2P, as in running a node over Tor or I2P, works differently. By default it sends them inside noise: on two channels it sends a fixed-size 3 KiB packet every 10 to 15 seconds, whether or not it has anything to carry, and a transaction rides inside them in up to 20 fragments. A peer that receives a transaction over Tor or I2P forwards it to the public network after a randomised delay, which the v0.17.0.0 release notes announced and the code sets at 22 seconds on average, so that, in the code's words, two or more incoming connections could have sent it.

With noise turned off, the node sends with the fluff flag set and the receiving hidden service fluffs at once. A comment in levin_notify.cpp says what that trade means: the anonymity network is then replacing the sybil protection of Dandelion++.

What it does not do

Dandelion++ is a defence against spy nodes: peers that log what they hear and when. It does nothing against an observer of your own connection. Your internet provider still sees a node sending a transaction to its stem peer, which is what Tor and I2P are for. Nor does it hide anything from a remote node that a wallet sends through, since that node receives the transaction from the wallet directly; Moneropedia's entry on remote nodes says their operators can link transactions to IP addresses.

Dandelion++: how Monero hides where a transaction began: questions

Does Monero use Dandelion++?

Yes, since v0.16.0.0, released on 28 May 2020. A node stems each transaction along a line of peers, with a 20% chance at every hop of switching to an ordinary broadcast, and fluffs a stemmed transaction itself if it has not seen it broadcast within an embargo of 39 seconds on average.

Is Dandelion++ enough to hide my IP address?

It hides which node started a transaction from the other nodes on the network, but not from someone watching your own connection, such as your internet provider, and not from a remote node your wallet sends through. For those, run your own node and relay transactions over Tor or I2P.

Source: The Dandelion++ paper (arXiv 1805.11060) and Monero's cryptonote_config.h and levin_notify.cpp checked against the source

More Monero privacy guides