Skip to content
Docs

Solana shred anatomy reference

Identify every logical region needed to parse, authenticate, recover, and reassemble a shred.

Before you start

  • A captured UDP datagram no larger than 1,228 bytes
  • A pinned Agave release

Read a shred as four regions

A raw datagram contains one shred. A shred has a common header, a type-specific header, erasure-coded material, and variant-dependent Merkle material. Data shreds carry serialized ledger bytes. Coding shreds carry Reed-Solomon parity over data-shred material. Both participate in one FEC set.

The feed delivers an average 1,216-byte packet and no datagram larger than 1,228 bytes. Do not infer the useful data capacity from either number. Merkle proof size and retransmitter-signature presence change the space available to a data shred. Ask the parser for data() or capacity for the parsed variant.

Common header

The current Agave wire implementation serializes an 83-byte common header. These byte ranges are verified against its public wire readers and serialization tests:

RangeWidthFieldMeaning
0..6464signatureEd25519 leader signature
64..651variantType, proof-size bits, and variant flags
65..738slotLittle-endian slot number
73..774indexLittle-endian index within its shred type
77..792versionLittle-endian shred version
79..834fec_set_indexLittle-endian first data index of the erasure set

Treat the signature as opaque until the variant parser defines the signed message. For current Merkle shreds, the leader signs the reconstructed Merkle root, not an arbitrary fixed slice selected by application code.

The data and coding index spaces are distinct. The identity of a shred is (slot, index, shred_type). Omitting type makes a data shred and coding shred with the same numeric index collide.

Data header

The current data header follows the common header and ends at byte 88:

RangeWidthFieldMeaning
83..852parent_offsetSlot minus parent slot, validated against the slot
85..861flagsReference tick plus completion bits
86..882sizeEnd of the declared header plus ledger data

The low six flag bits hold a saturated reference-tick value. 0x40 is DATA_COMPLETE_SHRED. 0xC0 represents LAST_SHRED_IN_SLOT and also implies data complete. Use the bitflag implementation from the pinned release rather than accepting arbitrary bits.

The size field is not the UDP datagram length. It identifies how much of the data shred contains headers plus ledger data. Padding and Merkle material can follow. A typed parser validates the declared size against the parsed variant and returns only the ledger data slice.

Coding header

The current coding header ends at byte 89:

RangeWidthFieldMeaning
83..852num_data_shredsData shard count k
85..872num_coding_shredsParity shard count m
87..892positionCoding position in 0..m

A coding shred's erasure-shard position is k + position. Its numeric shred index is not that local array position. Coding shreds in one set must agree on k, m, the first coding index derived as index - position, version, slot, FEC-set index, proof form, root, and leader signature.

The usual current target is 32 data and 32 coding shreds per FEC block. The last block can be shorter. Read k and m from a sanitized coding header rather than allocating every set as a permanent 32 by 32 assumption.

Merkle regions

The variant byte's low four bits encode proof entry count. High bits select data or coding and whether a retransmitter signature is present. Layout after the erasure-coded region depends on those values. Current Agave computes capacity and offsets from the parsed variant, hashes a leaf with the protocol prefix, walks the proof using the erasure-shard index, and obtains the root signed by the leader.

Do not copy a proof offset from a packet capture. The exact proof start depends on whether the shred is data or coding, proof entry count, payload size, and retransmitter-signature variant. The stable mechanism is: parse variant, sanitize the typed shred, ask it for its Merkle root or proof-related accessors, then verify using the scheduled leader key.

Chained Merkle data commits an FEC set to the preceding set's root. Retransmitter-signed variants carry an additional signature at the end while leaving the leader signature intact. The service forwards raw shreds. It does not replace the leader signature with a service signature.

Parse with checked slices

This small parser is suitable for header inspection. It is not a full validator:

rust

fn le_u32(bytes: &[u8], range: std::ops::Range<usize>) -> Option<u32> {
    Some(u32::from_le_bytes(bytes.get(range)?.try_into().ok()?))
}
fn le_u64(bytes: &[u8], range: std::ops::Range<usize>) -> Option<u64> {
    Some(u64::from_le_bytes(bytes.get(range)?.try_into().ok()?))
}

fn common(bytes: &[u8]) -> Option<(u8, u64, u32, u32)> {
    let variant = *bytes.get(64)?;
    let slot = le_u64(bytes, 65..73)?;
    let index = le_u32(bytes, 73..77)?;
    let fec = le_u32(bytes, 79..83)?;
    Some((variant, slot, index, fec))
}

fn main() {
    if common(&[0u8; 82]).is_some() { std::process::exit(1); }
}

Use these ranges only in tests, capture tools, and the minimal receiver tied to the documented format. Production code should construct solana_ledger::shred::Shred from serialized bytes and call sanitize. Keep raw bytes because recovery and duplicate-conflict evidence may need the canonical packet.

Record the parser revision beside every stored fixture. A byte map without its release identity becomes misleading after a format transition.

Parameters

NameTypeDefaultNotes
common_header_sizebytes83Current Agave common header, pin to the tested release.
data_header_sizebytes88Current total common plus data header size.
coding_header_sizebytes89Current total common plus coding header size.
max_datagram_sizebytes1228Maximum UDP datagram delivered by this product.

When it goes wrong

Invalid payload size: N

Cause. The packet is too short or does not match the capacity required by its variant.

Fix. Reject it and retain a sampled packet plus the pinned Agave revision for diagnosis.

Invalid shred variant

Cause. The variant byte is unknown to the parser or a legacy form is disabled in that release.

Fix. Use the Agave revision matching the network; do not add a guessed variant mask.

Invalid data size: size=N, payload=M

Cause. The data header's declared size is outside the legal region for that variant.

Fix. Reject the shred before exposing data bytes.

Questions

Is a shred always 1,228 bytes?
No. The product permits datagrams up to 1,228 bytes and observes a 1,216-byte mean. Parsed shred size and useful data capacity depend on the variant and Merkle material. Allocate for the maximum, use the received datagram length, and let the matching parser validate it.
Can the data size field be used as the UDP length?
No. The data size marks the end of common header, data header, and ledger data inside a data shred. Padding, chained-root data, Merkle proof entries, or a retransmitter signature can occupy other regions. The receive length and declared data size serve different purposes.
Why is shred type part of the identity?
Data and coding shreds have separate index sequences. A data shred and a coding shred can share a slot and numeric index without being the same packet. Use slot, index, and shred type for deduplication, while FEC grouping uses slot and FEC-set index.