A visual reference of Monero's peer-to-peer layer: the Levin protocol handshake, the tx_pool (mempool), Dandelion++ transaction propagation in its stem & fluff phases, and the fluffy block flow that pushes mined blocks across the network.
Every monerod instance maintains a white peerlist (peers it has successfully connected to) and a gray peerlist (advertised but unverified peers). Connections to other nodes are negotiated over TCP on port 18080 using the custom Levin binary protocol. Once a handshake succeeds, peers exchange continuous TIMED_SYNC messages to keep their chain heights and peerlists fresh.
TIMED_SYNC but never verified. Promoted to white on successful connect.When you send XMR, your wallet builds a signed transaction and pushes it to its monerod via the RPC send_raw_transaction. The daemon validates it, places it in the local tx_pool, then propagates it using Dandelion++ — a two-phase protocol designed to break the link between a tx and its source IP.
Wallet builds a CLSAG-signed RingCT tx, sends it to a trusted monerod via RPC send_raw_transaction.
Daemon checks signatures, key-images (no double-spend), fee, weight, RingCT range proofs. On pass → enters local tx_pool.
Node picks one random outbound peer (its current "stem successor") and forwards via NOTIFY_NEW_TRANSACTIONS with dandelionpp_fluff=false.
After N random hops, a node rolls into fluff mode (~10% per hop). It re-emits the tx to every peer with Poisson-delayed timing — dandelionpp_fluff=true.
Each monerod keeps its own independent mempool, persisted to LMDB. There is no global mempool in Monero — pools converge by gossip but always differ at the edges. The pool is what miners draw from when assembling candidate blocks, and what nodes consult when they receive a fluffy block referencing tx hashes.
Tx is rejected if: bad signature, key-image already spent, tx weight over limit, fee below dynamic min, or it conflicts with a pool tx (double-spend).
Sorted by fee-per-byte. When the pool exceeds --max-txpool-weight (default 600 MB), lowest-fee txs are evicted. Txs expire after 24h if not mined.
Periodically a node sends NOTIFY_GET_TXPOOL_COMPLIMENT (id 2010) listing the hashes it holds. The peer replies with any txs the requester is missing.
A miner selects high-fee txs from its tx_pool, builds a candidate block (coinbase + tx list + previous hash + nonce), and hashes it with RandomX until it finds a hash below the network difficulty target (~2-minute block time). The winning block is then propagated via fluffy blocks — only the header + tx hashes are sent, since peers usually already hold the txs in their mempool.
Miner queries get_block_template, packs txs ordered by fee until median_block_weight is hit, then runs RandomX on the header until H < target.
Block is gossiped as a fluffy block (header + tx hashes). Peers reconstruct it from their mempool. Missing txs are pulled in a single round-trip. ~80% of nodes see new blocks within ~1s.
Peer verifies PoW, tx signatures, key-images, coinbase reward. On accept → block appended, included txs removed from tx_pool, block re-relayed to its own outbound peers.
All P2P messages travel over the Levin binary framing (each prefixed with a bucket_head2 header). Admin messages (1xxx) are request/response; protocol messages (2xxx) are notifications.
| ID | Name | Purpose |
|---|---|---|
| 1001 | HANDSHAKE | Initial connection: exchange node data, top block, sync data, seed peerlist. |
| 1002 | TIMED_SYNC | Heartbeat every ~60s: current height, top hash, cumulative difficulty, fresh peerlist. |
| 1003 | PING | Liveness check used during outbound peer discovery. |
| 2001 | NOTIFY_NEW_BLOCK | Legacy full-block notification. Deprecated in favor of fluffy blocks. |
| 2002 | NOTIFY_NEW_TRANSACTIONS | Tx propagation. Carries the dandelionpp_fluff bool (true = fluff, false = stem) + padding bytes. |
| 2003 | NOTIFY_REQUEST_GET_OBJECTS | Ask peer for specific blocks by hash (used during sync). |
| 2004 | NOTIFY_RESPONSE_GET_OBJECTS | Reply: requested blocks + any missed ids + current height. |
| 2006 | NOTIFY_REQUEST_CHAIN | Send a sparse list of known block ids (reverse chrono) to find the fork point. |
| 2007 | NOTIFY_RESPONSE_CHAIN_ENTRY | Reply: start height, peer's total height, cumulative difficulty, list of block ids & weights. |
| 2008 | NOTIFY_NEW_FLUFFY_BLOCK | Compact block: header + tx hashes (+ any txs peer probably lacks). |
| 2009 | NOTIFY_REQUEST_FLUFFY_MISSING_TX | Request the specific txs a peer couldn't reconstruct from its mempool. |
| 2010 | NOTIFY_GET_TXPOOL_COMPLIMENT | "Here are the tx hashes I already have" — peer replies with what's missing. |