// schema · monero protocol v18+

How monerod nodes talk,
share txs & forge the chain.

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.

01 · Network Layer

§1Peer discovery & the Levin protocol

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.

monerod node A B C D E F G H I GRAY · advertised WHITE · connected

Peerlists & scoring

White list — peers we've handshaken with. Default: up to 12 outgoing + many incoming.
Gray list — peers advertised via TIMED_SYNC but never verified. Promoted to white on successful connect.
Offense tracker — each peer holds a score. Invalid blocks, bad txs, double-spend attempts, height mismatches lower it. Past threshold → drop + temp ban.
Peerlist exchange — on request, a node returns 250 random peers from the top 300 of its white list (timestamps stripped since the 2019 update).

Handshake & sync sequence

NODE A initiator NODE B receiver HANDSHAKE · id 1001 network_id, peer_id, support_flags, top_block, sync_data HANDSHAKE response · + peerlist (≤250) node_data, payload_data, local_peerlist_new LOOP · every ~60s while connected TIMED_SYNC · id 1002 current_height, top_id, cumulative_difficulty, pruning_seed TIMED_SYNC response · + fresh peerlist If B's chain is taller → A triggers chain sync (§4)
02 · Transactions

§2From wallet to network · Dandelion++

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 monero-wallet-* RPC N0 origin tx → local tx_pool STEM PHASE · anonymity single random outbound peer · no diffusion · random hop count N1 N2 N3 stem fwd stem fwd ~10% → fluff (random per node, every ~10min) FLUFF PHASE · diffusion broadcast to all peers with Poisson-delayed timing F PPP PPP PPP Fail-safe: each stem node arms a timer. No fluff back in time → it fluffs itself.
01

Wallet → daemon

Wallet builds a CLSAG-signed RingCT tx, sends it to a trusted monerod via RPC send_raw_transaction.

02

Local validation

Daemon checks signatures, key-images (no double-spend), fee, weight, RingCT range proofs. On pass → enters local tx_pool.

03

Stem relay

Node picks one random outbound peer (its current "stem successor") and forwards via NOTIFY_NEW_TRANSACTIONS with dandelionpp_fluff=false.

04

Fluff broadcast

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.

03 · Mempool

§3tx_pool — the local mempool

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.

a

Admission checks

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).

b

Ordering & eviction

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.

c

Pool reconciliation

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.

04 · Consensus

§4Mining & fluffy block propagation

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 NODE found new block PEER NODE receives gossip NOTIFY_NEW_FLUFFY_BLOCK · id 2008 header + coinbase + [tx hashes] + (any txs peer likely lacks) peer checks own tx_pool all hashes resolved? missing → NOTIFY_REQUEST_FLUFFY_MISSING_TX · id 2009 block_hash + indices of missing txs NOTIFY_NEW_FLUFFY_BLOCK (again) · id 2008 this time with the requested tx blobs included verify PoW + tx merkle add to chain · remove txs from tx_pool · re-fluff to peers
α

Block construction

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.

β

Fluffy propagation

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.

γ

Validation & chain extension

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.

05 · Reference

§5Levin message IDs at a glance

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.

IDNamePurpose
1001HANDSHAKEInitial connection: exchange node data, top block, sync data, seed peerlist.
1002TIMED_SYNCHeartbeat every ~60s: current height, top hash, cumulative difficulty, fresh peerlist.
1003PINGLiveness check used during outbound peer discovery.
2001NOTIFY_NEW_BLOCKLegacy full-block notification. Deprecated in favor of fluffy blocks.
2002NOTIFY_NEW_TRANSACTIONSTx propagation. Carries the dandelionpp_fluff bool (true = fluff, false = stem) + padding bytes.
2003NOTIFY_REQUEST_GET_OBJECTSAsk peer for specific blocks by hash (used during sync).
2004NOTIFY_RESPONSE_GET_OBJECTSReply: requested blocks + any missed ids + current height.
2006NOTIFY_REQUEST_CHAINSend a sparse list of known block ids (reverse chrono) to find the fork point.
2007NOTIFY_RESPONSE_CHAIN_ENTRYReply: start height, peer's total height, cumulative difficulty, list of block ids & weights.
2008NOTIFY_NEW_FLUFFY_BLOCKCompact block: header + tx hashes (+ any txs peer probably lacks).
2009NOTIFY_REQUEST_FLUFFY_MISSING_TXRequest the specific txs a peer couldn't reconstruct from its mempool.
2010NOTIFY_GET_TXPOOL_COMPLIMENT"Here are the tx hashes I already have" — peer replies with what's missing.