RaptorCast: high-performance block propagation
Higher throughput means larger blocks, and larger blocks take longer to propagate across the network. If block propagation time grows linearly with block size, it eventually dominates the block time budget, negating the throughput gains.
RaptorCast is Monad's block propagation protocol, designed to disseminate large blocks efficiently using fountain codes and multi-hop forwarding. RaptorCast is a key part of what makes Monad a scalable L1 with frequent (400 ms) and large (~4000 transaction) blocks while also preserving minimal bandwidth requirements and full geographic decentralization.
The problem with naive broadcast
In a naive broadcast protocol, the leader sends the full block to every validator. With V validators and a block of size B, the leader's outbound bandwidth requirement is V * B. For a large validator set and large blocks, this becomes the bottleneck: the leader's upload link is saturated, and validators at the end of the queue receive the block late.
An alternative is gossip (used by Ethereum and many other blockchains): each node forwards the block to a few peers, who forward it to their peers, and so on. This distributes the bandwidth load but introduces latency (each hop adds delay) and redundancy (nodes may receive the same data from multiple peers).
RaptorCast improves on both approaches by using Raptor codes -- a class of fountain codes that allow a receiver to reconstruct the original data from any sufficient subset of coded symbols, regardless of which specific symbols it received.
Raptor codes
Monad uses a custom implementation of R10 Raptor codes (RFC 5053) in the monad-raptor crate. R10 is a rateless erasure coding scheme: the encoder takes a message of K source symbols and can produce an unlimited number of encoded symbols. A decoder can reconstruct the original message from any K' symbols, where K' is only slightly larger than K. It doesn't matter which K' symbols the decoder receives -- any sufficient subset works.
This is what makes Raptor codes ideal for multi-hop broadcast: each node is intended to receive all symbols as a second-hop recipient, but can reconstruct the full message even if a large fraction of the symbols are blackholed by first-hop nodes acting maliciously.
Encoding
The message is divided into K fixed-size source symbols (K can range from 1 to 8,192). Two layers of pre-coding add redundancy before the main encoding step: LDPC (Low-Density Parity-Check) symbols and half symbols. Together with the K source symbols, these form L = K + S + H intermediate symbols.
Each encoded symbol is then produced by the LT (Luby Transform) stage. For a given encoding symbol ID (ESI), a pseudorandom function determines a degree and a subset of intermediate symbols; the encoded symbol is their XOR. Different ESIs produce different combinations, and the encoder can generate up to 65,521 unique encoded symbols from the same source data. Because each ESI produces a distinct symbol, the encoder can produce as many symbols as needed without re-encoding.
Encoding pipeline
The full encoding flow from application message to UDP packets proceeds in five stages, implemented in the assembler:
1. Calculate symbol count
The number of source symbols is derived from the message length and symbol size:
base_symbols = ceil(message_len / symbol_len)
This is then scaled by the redundancy factor (2.5x for primary RaptorCast, 2.0x for secondary) to determine how many encoded symbols to generate.
2. Chunk assignment
Encoded symbols are assigned to validators via stake-weighted distribution. Each validator i receives:
base_chunks_i = floor(num_symbols * stake_i / total_stake)
If the fractional part of num_symbols * stake_i / total_stake is nonzero, the validator also receives a rounding chunk. This ensures no validator is under-assigned due to integer division.
3. Encode, sign, and assemble
The Raptor encoder generates each encoded symbol via the LDPC + half-symbol + LT pipeline. Chunks are then grouped into Merkle batches (as discussed below), and the root is signed once with ECDSA. Finally, headers, Merkle proofs, chunk headers, and payloads are packed into UDP packets sized for the Ethernet MTU (1,500 bytes).
Amortizing signatures with Merkle trees
A large block may produce hundreds or thousands of erasure-coded packets. Each packet must be authenticated so that receivers can verify it came from the claimed sender. The naive approach -- signing every packet individually with ECDSA -- is too expensive. ECDSA signing costs roughly 200us per operation, and at thousands of packets per block the sender would spend hundreds of milliseconds just signing, creating a CPU bottleneck on the critical path.
RaptorCast solves this by batching packets under a Merkle tree. The sender groups packets into batches (32 packets per batch at the default depth of 6), hashes each packet (using a 20-byte hash) to form the leaves of a binary Merkle tree, and signs the Merkle root once with ECDSA. Each packet then carries the Merkle root signature plus the sibling hashes (the Merkle proof) needed to verify that the packet is a leaf of the signed tree.
This achieves two things:
Signing cost is amortized. One ECDSA signature covers 32 packets instead of one. The sender's signing workload drops by 32x.
Each packet is independently verifiable. A receiver (or a forwarding validator on a later hop) can verify any individual packet without needing the other packets in the batch. It hashes the packet, walks the Merkle proof up to the root, and checks the ECDSA signature on that root. This is critical for multi-hop forwarding, where different validators receive and re-broadcast different subsets of packets.
Packet structure
Each RaptorCast packet is sized for the Ethernet MTU of 1,500 bytes (1,472 bytes after IPv4 and UDP headers). The wire format packs five sections sequentially:
Signature (65 bytes): An ECDSA signature over the concatenation of the header (excluding the signature field itself) and the Merkle root. This single signature authenticates both the header metadata and the packet's membership in its Merkle batch.
Header (43 bytes):
Offset Size Field
------ ---- -----
0 2 Version (u16, little-endian)
2 1 Flags: broadcast (1 bit), secondary broadcast (1 bit),
unused (2 bits), Merkle tree depth (4 bits)
3 8 Epoch number (u64, little-endian)
11 8 Unix timestamp in milliseconds (u64, little-endian)
19 20 Message hash (first 20 bytes of SHA hash)
39 4 Message length (u32, little-endian)
Merkle proof (variable, 100 bytes at default depth): (depth - 1) sibling hashes at 20 bytes each. At the default depth of 6, this is 5 * 20 = 100 bytes.
Chunk header (24 bytes):
Offset Size Field
------ ---- -----
0 20 First-hop recipient hash
20 1 Merkle leaf index within batch
21 1 Reserved
22 2 Chunk ID / encoding symbol ID (u16, little-endian)
Payload (remaining bytes): The Raptor-encoded symbol data. At a 1,500-byte MTU, the data payload is 1,472 - 65 - 43 - 100 - 24 = 1,240 bytes. Total per-packet overhead is 232 bytes.
The minimum chunk payload is 960 bytes (to mitigate small-packet DoS attacks). Multi-chunk messages that would produce payloads below this threshold are rejected.
Two-hop distribution
RaptorCast distributes symbols in two phases rather than relying on the leader to send all symbols to all validators.
Phase 1: leader to validators
The leader Raptor-encodes the message and sends different subsets of encoded symbols to different validators. Assignment is deterministic and stake-weighted: given the message hash as a seed, each validator's share is proportional to its stake. A validator with 2% stake in a network of 100 validators receives roughly 2% of the encoded symbols directly from the leader.
Phase 2: validators to validators
Each validator that receives symbols from the leader re-broadcasts them to all other validators. Rebroadcast packets are assigned high priority in the UDP egress queue, ensuring they're sent before new outbound messages. This keeps the forwarding pipeline full and minimizes reconstruction latency.

Why two hops suffice
In phase 1, the leader sends every encoded symbol to exactly one validator. In phase 2, each validator re-broadcasts every symbol it received to all other validators. The result is that every validator receives every encoded symbol -- unless the symbol was routed through a Byzantine validator that refused to forward it.
Consider a concrete example: a 200 KB block with 1,240-byte symbols produces roughly 160 source symbols. At 2.5x redundancy, the leader generates about 400 encoded symbols total. A validator with 2% stake receives approximately 8 symbols directly from the leader in phase 1 and re-broadcasts all 8 to the rest of the network. After phase 2, every honest validator has received all symbols forwarded by honest validators. Even if 1/3 of stake weight is Byzantine and drops everything, the remaining 2/3 of the 400 symbols (roughly 267) still far exceeds the approximately 160 needed to decode.
Two hops are sufficient because the first hop fans out symbols across validators and the second hop ensures every validator gets every symbol. The network's aggregate bandwidth is used efficiently: instead of the leader's single link being the bottleneck, many links contribute to propagation simultaneously.
Decoding
A receiver collects encoded symbols as they arrive, each tagged with its encoding symbol ID.
Two-stage recovery
Once approximately K symbols have accumulated, the decoder solves a system of linear equations over GF(2) -- XOR arithmetic -- to recover the intermediate symbols, and from those, the original source symbols.
Decoding proceeds in two stages. First, a peeling decoder resolves degree-1 symbols: each directly reveals one intermediate symbol, which is XOR'd out of all other symbols that reference it, potentially reducing them to degree 1 and triggering a chain reaction. Second, Gaussian elimination solves any residual symbols that peeling couldn't resolve. The peeling decoder handles most of the work in linear time, which is why Raptor decoding is O(K) in practice.
Validation
After reconstruction, the decoder computes the hash of the recovered message and compares it to the message hash advertised in the packet headers. If the hashes don't match, the message is rejected.
Decoding cache
Validators may be simultaneously receiving symbols for many different messages from many different senders. The decoding cache manages these concurrent partial decodes without allowing any single sender to exhaust memory.
Three-tier LRU cache
Partially-decoded messages are stored in a tiered cache, with each tier configured for its traffic pattern:
| Tier | Slots | Per-author limit | Min slots/author |
|---|---|---|---|
| Broadcast | 1,000 | 20 MB | 5 |
| Validator | 600 | 1 MB | 3 |
| P2P | 500 | 1 MB | 1 |
The broadcast tier has the most capacity because broadcast messages (block proposals) are the largest and most frequent. Validator and P2P tiers handle smaller targeted messages.
Recently-decoded cache
A separate LRU cache of 10,000 entries tracks recently-decoded message hashes. When a symbol arrives for an already-decoded message, it's dropped immediately without allocating decoder state. This provides fast duplicate detection.
Four-stage eviction
When the cache is under memory pressure, eviction proceeds in four stages:
Over-quota author: Evict the oldest entry from the author who most exceeds their per-author size limit.
All over-quota: Evict from any author exceeding their limit.
Expired: Evict entries older than 10 seconds.
Random: Evict a random entry as a last resort.
Redundancy and fault tolerance
Redundancy factors
RaptorCast transmits more encoded symbols than strictly necessary for reconstruction:
Primary (validator-to-validator): 2.5x redundancy
Secondary (validators to full nodes): 2.0x redundancy
Fault tolerance
With 2.5x redundancy, reconstruction succeeds even with roughly 60% packet loss. This defends against up to 1/3 of the stake weight being Byzantine, plus additional loss from network issues.
Primary vs. secondary RaptorCast
Monad runs two RaptorCast instances:
Primary RaptorCast (validator-to-validator)
The primary instance handles consensus-critical messages: block proposals, votes, and other consensus protocol messages. It operates over authenticated UDP between validators with 2.5x redundancy. Every validator participates in encoding, forwarding, and decoding. Chunk assignment is stake-weighted.
Secondary RaptorCast (validators to full nodes)
The secondary instance distributes blocks to full nodes (non-validator nodes that maintain state but don't participate in consensus). It uses a group-based model with 2.0x redundancy.
Group structure: Validators organize full nodes into groups of up to 50 nodes. Each full node can join up to 3 groups simultaneously. Groups are formed dynamically via a three-step handshake:
PrepareGroup: A validator sends group invitations to candidate full nodes, starting up to 1,200 rounds (roughly 10 minutes) in advance.
PrepareGroupResponse: Full nodes that accept respond within a timing window (heartbeat interval of 10 seconds).
ConfirmGroup: The validator confirms the group membership.
Groups persist for a configurable round span (default 120 rounds, roughly 60 seconds) before being reformed. The invite lookahead of 1,200 rounds ensures groups are ready well before they're needed.
This group structure avoids broadcasting to every full node individually while ensuring reliable delivery to nodes that need the data.
Rate limiting and DoS protection
RaptorCast includes several layers of protection against abuse:
Signature verification rate limiting: A token-bucket rate limiter caps signature verifications at 10,000 per second. Validator-originated packets bypass this limit entirely, ensuring consensus messages are never throttled.
Signature caching: Recently verified signatures are cached (10,000 entries, keyed by header + Merkle root) to avoid re-verifying retransmitted or forwarded packets.
Per-author memory limits: The decoding cache enforces per-author limits tiered by message type -- 20 MB for broadcast messages, 1 MB for validator-targeted and P2P messages. The four-stage eviction strategy described above prevents a single sender from consuming excessive memory.
Minimum chunk payload: Multi-chunk messages must have at least 960 bytes of payload per chunk. This prevents amplification attacks where an attacker sends many small packets that each require signature verification and decoder state allocation.
Recipient hash validation: Each packet carries a 20-byte first-hop recipient hash in its chunk header. Nodes drop packets not addressed to them, preventing unwanted processing of misdirected traffic.
Maximum message size: Messages are capped at 3 MB before decompression, bounding the resources any single message can consume.
Disconnect and IP banning: Severe protocol violations (e.g., invalid signatures, malformed packets) immediately disconnect the offending socket. The dataplane also supports 5-minute IP-wide bans that drop all connections from the banned address and reject new ones until the ban expires.
Further reading
Please refer to this excellent blog post from Category Labs summarizing the RaptorCast system.
Originally published on X on February 10, 2026.