Skip to main content

clone_solana_ledger/
shred.rs

1//! The `shred` module defines data structures and methods to pull MTU sized data frames from the
2//! network. There are two types of shreds: data and coding. Data shreds contain entry information
3//! while coding shreds provide redundancy to protect against dropped network packets (erasures).
4//!
5//! +---------------------------------------------------------------------------------------------+
6//! | Data Shred                                                                                  |
7//! +---------------------------------------------------------------------------------------------+
8//! | common       | data       | payload                                                         |
9//! | header       | header     |                                                                 |
10//! |+---+---+---  |+---+---+---|+----------------------------------------------------------+----+|
11//! || s | s | .   || p | f | s || data (ie ledger entries)                                 | r  ||
12//! || i | h | .   || a | l | i ||                                                          | e  ||
13//! || g | r | .   || r | a | z || See notes immediately after shred diagrams for an        | s  ||
14//! || n | e |     || e | g | e || explanation of the "restricted" section in this payload  | t  ||
15//! || a | d |     || n | s |   ||                                                          | r  ||
16//! || t |   |     || t |   |   ||                                                          | i  ||
17//! || u | t |     ||   |   |   ||                                                          | c  ||
18//! || r | y |     || o |   |   ||                                                          | t  ||
19//! || e | p |     || f |   |   ||                                                          | e  ||
20//! ||   | e |     || f |   |   ||                                                          | d  ||
21//! |+---+---+---  |+---+---+---+|----------------------------------------------------------+----+|
22//! +---------------------------------------------------------------------------------------------+
23//!
24//! +---------------------------------------------------------------------------------------------+
25//! | Coding Shred                                                                                |
26//! +---------------------------------------------------------------------------------------------+
27//! | common       | coding     | payload                                                         |
28//! | header       | header     |                                                                 |
29//! |+---+---+---  |+---+---+---+----------------------------------------------------------------+|
30//! || s | s | .   || n | n | p || data (encoded data shred data)                                ||
31//! || i | h | .   || u | u | o ||                                                               ||
32//! || g | r | .   || m | m | s ||                                                               ||
33//! || n | e |     ||   |   | i ||                                                               ||
34//! || a | d |     || d | c | t ||                                                               ||
35//! || t |   |     ||   |   | i ||                                                               ||
36//! || u | t |     || s | s | o ||                                                               ||
37//! || r | y |     || h | h | n ||                                                               ||
38//! || e | p |     || r | r |   ||                                                               ||
39//! ||   | e |     || e | e |   ||                                                               ||
40//! ||   |   |     || d | d |   ||                                                               ||
41//! |+---+---+---  |+---+---+---+|+--------------------------------------------------------------+|
42//! +---------------------------------------------------------------------------------------------+
43//!
44//! Notes:
45//! a) Coding shreds encode entire data shreds: both of the headers AND the payload.
46//! b) Coding shreds require their own headers for identification and etc.
47//! c) The erasure algorithm requires data shred and coding shred bytestreams to be equal in length.
48//!
49//! So, given a) - c), we must restrict data shred's payload length such that the entire coding
50//! payload can fit into one coding shred / packet.
51
52#[cfg(test)]
53pub(crate) use self::shred_code::MAX_CODE_SHREDS_PER_SLOT;
54pub(crate) use self::{merkle::SIZE_OF_MERKLE_ROOT, payload::serde_bytes_payload};
55pub use {
56    self::{
57        payload::Payload,
58        shred_data::ShredData,
59        stats::{ProcessShredsStats, ShredFetchStats},
60    },
61    crate::shredder::{ReedSolomonCache, Shredder},
62};
63use {
64    self::{shred_code::ShredCode, traits::Shred as _},
65    crate::blockstore::{self, MAX_DATA_SHREDS_PER_SLOT},
66    assert_matches::debug_assert_matches,
67    bitflags::bitflags,
68    num_enum::{IntoPrimitive, TryFromPrimitive},
69    rayon::ThreadPool,
70    serde::{Deserialize, Serialize},
71    clone_solana_entry::entry::{create_ticks, Entry},
72    clone_solana_perf::packet::Packet,
73    clone_solana_sdk::{
74        clock::Slot,
75        hash::{hashv, Hash},
76        pubkey::Pubkey,
77        signature::{Keypair, Signature, Signer, SIGNATURE_BYTES},
78    },
79    static_assertions::const_assert_eq,
80    std::{fmt::Debug, time::Instant},
81    thiserror::Error,
82};
83
84mod common;
85mod legacy;
86mod merkle;
87mod payload;
88pub mod shred_code;
89mod shred_data;
90mod stats;
91mod traits;
92pub mod wire;
93
94// Alias for shred::wire::* for the old code.
95// New code should use shred::wire::*.
96pub mod layout {
97    pub use super::wire::*;
98}
99
100pub type Nonce = u32;
101const_assert_eq!(SIZE_OF_NONCE, 4);
102pub const SIZE_OF_NONCE: usize = std::mem::size_of::<Nonce>();
103
104/// The following constants are computed by hand, and hardcoded.
105/// `test_shred_constants` ensures that the values are correct.
106/// Constants are used over lazy_static for performance reasons.
107const SIZE_OF_COMMON_SHRED_HEADER: usize = 83;
108const SIZE_OF_DATA_SHRED_HEADERS: usize = 88;
109const SIZE_OF_CODING_SHRED_HEADERS: usize = 89;
110const SIZE_OF_SIGNATURE: usize = SIGNATURE_BYTES;
111
112// Shreds are uniformly split into erasure batches with a "target" number of
113// data shreds per each batch as below. The actual number of data shreds in
114// each erasure batch depends on the number of shreds obtained from serializing
115// a &[Entry].
116pub const DATA_SHREDS_PER_FEC_BLOCK: usize = 32;
117
118// For legacy tests and benchmarks.
119const_assert_eq!(LEGACY_SHRED_DATA_CAPACITY, 1051);
120pub const LEGACY_SHRED_DATA_CAPACITY: usize = legacy::ShredData::CAPACITY;
121
122// LAST_SHRED_IN_SLOT also implies DATA_COMPLETE_SHRED.
123// So it cannot be LAST_SHRED_IN_SLOT if not also DATA_COMPLETE_SHRED.
124bitflags! {
125    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
126    pub struct ShredFlags:u8 {
127        const SHRED_TICK_REFERENCE_MASK = 0b0011_1111;
128        const DATA_COMPLETE_SHRED       = 0b0100_0000;
129        const LAST_SHRED_IN_SLOT        = 0b1100_0000;
130    }
131}
132
133impl ShredFlags {
134    /// Creates a new ShredFlags from the given reference_tick
135    ///
136    /// SHRED_TICK_REFERENCE_MASK is comprised of only six bits whereas the
137    /// reference_tick has 8 bits (u8). The reference_tick bits will saturate
138    /// in the event that reference_tick > SHRED_TICK_REFERENCE_MASK
139    pub(crate) fn from_reference_tick(reference_tick: u8) -> Self {
140        Self::from_bits_retain(Self::SHRED_TICK_REFERENCE_MASK.bits().min(reference_tick))
141    }
142}
143
144#[derive(Debug, Error)]
145pub enum Error {
146    #[error(transparent)]
147    BincodeError(#[from] bincode::Error),
148    #[error(transparent)]
149    ErasureError(#[from] reed_solomon_erasure::Error),
150    #[error("Invalid data size: {size}, payload: {payload}")]
151    InvalidDataSize { size: u16, payload: usize },
152    #[error("Invalid deshred set")]
153    InvalidDeshredSet,
154    #[error("Invalid erasure shard index: {0:?}")]
155    InvalidErasureShardIndex(/*headers:*/ Box<dyn Debug + Send>),
156    #[error("Invalid merkle proof")]
157    InvalidMerkleProof,
158    #[error("Invalid Merkle root")]
159    InvalidMerkleRoot,
160    #[error("Invalid num coding shreds: {0}")]
161    InvalidNumCodingShreds(u16),
162    #[error("Invalid parent_offset: {parent_offset}, slot: {slot}")]
163    InvalidParentOffset { slot: Slot, parent_offset: u16 },
164    #[error("Invalid parent slot: {parent_slot}, slot: {slot}")]
165    InvalidParentSlot { slot: Slot, parent_slot: Slot },
166    #[error("Invalid payload size: {0}")]
167    InvalidPayloadSize(/*payload size:*/ usize),
168    #[error("Invalid proof size: {0}")]
169    InvalidProofSize(/*proof_size:*/ u8),
170    #[error("Invalid recovered shred")]
171    InvalidRecoveredShred,
172    #[error("Invalid shard size: {0}")]
173    InvalidShardSize(/*shard_size:*/ usize),
174    #[error("Invalid shred flags: {0}")]
175    InvalidShredFlags(u8),
176    #[error("Invalid {0:?} shred index: {1}")]
177    InvalidShredIndex(ShredType, /*shred index:*/ u32),
178    #[error("Invalid shred type")]
179    InvalidShredType,
180    #[error("Invalid shred variant")]
181    InvalidShredVariant,
182    #[error(transparent)]
183    IoError(#[from] std::io::Error),
184    #[error("Unknown proof size")]
185    UnknownProofSize,
186}
187
188#[repr(u8)]
189#[cfg_attr(feature = "frozen-abi", derive(AbiExample, AbiEnumVisitor))]
190#[derive(
191    Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, IntoPrimitive, Serialize, TryFromPrimitive,
192)]
193#[serde(into = "u8", try_from = "u8")]
194pub enum ShredType {
195    Data = 0b1010_0101,
196    Code = 0b0101_1010,
197}
198
199#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
200#[serde(into = "u8", try_from = "u8")]
201enum ShredVariant {
202    LegacyCode, // 0b0101_1010
203    LegacyData, // 0b1010_0101
204    // proof_size is the number of Merkle proof entries, and is encoded in the
205    // lowest 4 bits of the binary representation. The first 4 bits identify
206    // the shred variant:
207    //   0b0100_????  MerkleCode
208    //   0b0110_????  MerkleCode chained
209    //   0b0111_????  MerkleCode chained resigned
210    //   0b1000_????  MerkleData
211    //   0b1001_????  MerkleData chained
212    //   0b1011_????  MerkleData chained resigned
213    MerkleCode {
214        proof_size: u8,
215        chained: bool,
216        resigned: bool,
217    }, // 0b01??_????
218    MerkleData {
219        proof_size: u8,
220        chained: bool,
221        resigned: bool,
222    }, // 0b10??_????
223}
224
225/// A common header that is present in data and code shred headers
226#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
227struct ShredCommonHeader {
228    signature: Signature,
229    shred_variant: ShredVariant,
230    slot: Slot,
231    index: u32,
232    version: u16,
233    fec_set_index: u32,
234}
235
236/// The data shred header has parent offset and flags
237#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
238struct DataShredHeader {
239    parent_offset: u16,
240    flags: ShredFlags,
241    size: u16, // common shred header + data shred header + data
242}
243
244/// The coding shred header has FEC information
245#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
246struct CodingShredHeader {
247    num_data_shreds: u16,
248    num_coding_shreds: u16,
249    position: u16, // [0..num_coding_shreds)
250}
251
252#[derive(Clone, Debug, PartialEq, Eq)]
253pub enum Shred {
254    ShredCode(ShredCode),
255    ShredData(ShredData),
256}
257
258#[derive(Debug, PartialEq, Eq)]
259pub(crate) enum SignedData<'a> {
260    Chunk(&'a [u8]), // Chunk of payload past signature.
261    MerkleRoot(Hash),
262}
263
264impl AsRef<[u8]> for SignedData<'_> {
265    fn as_ref(&self) -> &[u8] {
266        match self {
267            Self::Chunk(chunk) => chunk,
268            Self::MerkleRoot(root) => root.as_ref(),
269        }
270    }
271}
272
273/// Tuple which uniquely identifies a shred should it exists.
274#[derive(Clone, Copy, Eq, Debug, Hash, PartialEq)]
275pub struct ShredId(Slot, /*shred index:*/ u32, ShredType);
276
277impl ShredId {
278    #[inline]
279    pub(crate) fn new(slot: Slot, index: u32, shred_type: ShredType) -> ShredId {
280        ShredId(slot, index, shred_type)
281    }
282
283    #[inline]
284    pub fn slot(&self) -> Slot {
285        self.0
286    }
287
288    #[inline]
289    pub(crate) fn unpack(&self) -> (Slot, /*shred index:*/ u32, ShredType) {
290        (self.0, self.1, self.2)
291    }
292
293    pub fn seed(&self, leader: &Pubkey) -> [u8; 32] {
294        let ShredId(slot, index, shred_type) = self;
295        hashv(&[
296            &slot.to_le_bytes(),
297            &u8::from(*shred_type).to_le_bytes(),
298            &index.to_le_bytes(),
299            AsRef::<[u8]>::as_ref(leader),
300        ])
301        .to_bytes()
302    }
303}
304
305/// Tuple which identifies erasure coding set that the shred belongs to.
306#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
307pub(crate) struct ErasureSetId(Slot, /*fec_set_index:*/ u32);
308
309impl ErasureSetId {
310    pub(crate) fn new(slot: Slot, fec_set_index: u32) -> Self {
311        Self(slot, fec_set_index)
312    }
313
314    pub(crate) fn slot(&self) -> Slot {
315        self.0
316    }
317
318    // Storage key for ErasureMeta and MerkleRootMeta in blockstore db.
319    // Note: ErasureMeta column uses u64 so this will need to be typecast
320    pub(crate) fn store_key(&self) -> (Slot, /*fec_set_index:*/ u32) {
321        (self.0, self.1)
322    }
323}
324
325/// To be used with the [`Shred`] enum.
326///
327/// Writes a function implementation that forwards the invocation to an identically defined function
328/// in one of the two enum branches.
329///
330/// Due to an inability of a macro to match on the `self` shorthand syntax, this macro has 3
331/// branches.  But they are only different in the `self` argument matching.  Make sure to keep the
332/// identical otherwise.
333macro_rules! dispatch {
334    ($vis:vis fn $name:ident(&self $(, $arg:ident : $ty:ty)?) $(-> $out:ty)?) => {
335        #[inline]
336        $vis fn $name(&self $(, $arg:$ty)?) $(-> $out)? {
337            match self {
338                Self::ShredCode(shred) => shred.$name($($arg, )?),
339                Self::ShredData(shred) => shred.$name($($arg, )?),
340            }
341        }
342    };
343    ($vis:vis fn $name:ident(self $(, $arg:ident : $ty:ty)?) $(-> $out:ty)?) => {
344        #[inline]
345        $vis fn $name(self $(, $arg:$ty)?) $(-> $out)? {
346            match self {
347                Self::ShredCode(shred) => shred.$name($($arg, )?),
348                Self::ShredData(shred) => shred.$name($($arg, )?),
349            }
350        }
351    };
352    ($vis:vis fn $name:ident(&mut self $(, $arg:ident : $ty:ty)?) $(-> $out:ty)?) => {
353        #[inline]
354        $vis fn $name(&mut self $(, $arg:$ty)?) $(-> $out)? {
355            match self {
356                Self::ShredCode(shred) => shred.$name($($arg, )?),
357                Self::ShredData(shred) => shred.$name($($arg, )?),
358            }
359        }
360    }
361}
362
363use dispatch;
364
365impl Shred {
366    dispatch!(fn common_header(&self) -> &ShredCommonHeader);
367    dispatch!(fn set_signature(&mut self, signature: Signature));
368    dispatch!(fn signed_data(&self) -> Result<SignedData, Error>);
369
370    dispatch!(pub fn chained_merkle_root(&self) -> Result<Hash, Error>);
371    // Returns the portion of the shred's payload which is erasure coded.
372    dispatch!(pub(crate) fn erasure_shard(&self) -> Result<&[u8], Error>);
373    // Returns the shard index within the erasure coding set.
374    dispatch!(pub(crate) fn erasure_shard_index(&self) -> Result<usize, Error>);
375    dispatch!(pub(crate) fn retransmitter_signature(&self) -> Result<Signature, Error>);
376
377    dispatch!(pub fn into_payload(self) -> Payload);
378    dispatch!(pub fn merkle_root(&self) -> Result<Hash, Error>);
379    dispatch!(pub fn payload(&self) -> &Payload);
380    dispatch!(pub fn sanitize(&self) -> Result<(), Error>);
381
382    // Only for tests.
383    dispatch!(pub fn set_index(&mut self, index: u32));
384    dispatch!(pub fn set_slot(&mut self, slot: Slot));
385
386    pub fn copy_to_packet(&self, packet: &mut Packet) {
387        let payload = self.payload();
388        let size = payload.len();
389        packet.buffer_mut()[..size].copy_from_slice(&payload[..]);
390        packet.meta_mut().size = size;
391    }
392
393    // TODO: Should this sanitize output?
394    pub fn new_from_data(
395        slot: Slot,
396        index: u32,
397        parent_offset: u16,
398        data: &[u8],
399        flags: ShredFlags,
400        reference_tick: u8,
401        version: u16,
402        fec_set_index: u32,
403    ) -> Self {
404        Self::from(ShredData::new_from_data(
405            slot,
406            index,
407            parent_offset,
408            data,
409            flags,
410            reference_tick,
411            version,
412            fec_set_index,
413        ))
414    }
415
416    pub fn new_from_serialized_shred<T>(shred: T) -> Result<Self, Error>
417    where
418        T: AsRef<[u8]> + Into<Payload>,
419        Payload: From<T>,
420    {
421        Ok(match layout::get_shred_variant(shred.as_ref())? {
422            ShredVariant::LegacyCode => {
423                let shred = legacy::ShredCode::from_payload(shred)?;
424                Self::from(ShredCode::from(shred))
425            }
426            ShredVariant::LegacyData => {
427                let shred = legacy::ShredData::from_payload(shred)?;
428                Self::from(ShredData::from(shred))
429            }
430            ShredVariant::MerkleCode { .. } => {
431                let shred = merkle::ShredCode::from_payload(shred)?;
432                Self::from(ShredCode::from(shred))
433            }
434            ShredVariant::MerkleData { .. } => {
435                let shred = merkle::ShredData::from_payload(shred)?;
436                Self::from(ShredData::from(shred))
437            }
438        })
439    }
440
441    pub fn new_from_parity_shard(
442        slot: Slot,
443        index: u32,
444        parity_shard: &[u8],
445        fec_set_index: u32,
446        num_data_shreds: u16,
447        num_coding_shreds: u16,
448        position: u16,
449        version: u16,
450    ) -> Self {
451        Self::from(ShredCode::new_from_parity_shard(
452            slot,
453            index,
454            parity_shard,
455            fec_set_index,
456            num_data_shreds,
457            num_coding_shreds,
458            position,
459            version,
460        ))
461    }
462
463    /// Unique identifier for each shred.
464    pub fn id(&self) -> ShredId {
465        ShredId(self.slot(), self.index(), self.shred_type())
466    }
467
468    pub fn slot(&self) -> Slot {
469        self.common_header().slot
470    }
471
472    pub fn parent(&self) -> Result<Slot, Error> {
473        match self {
474            Self::ShredCode(_) => Err(Error::InvalidShredType),
475            Self::ShredData(shred) => shred.parent(),
476        }
477    }
478
479    pub fn index(&self) -> u32 {
480        self.common_header().index
481    }
482
483    // Possibly trimmed payload;
484    // Should only be used when storing shreds to blockstore.
485    pub(crate) fn bytes_to_store(&self) -> &[u8] {
486        match self {
487            Self::ShredCode(shred) => shred.payload(),
488            Self::ShredData(shred) => shred.bytes_to_store(),
489        }
490    }
491
492    pub fn fec_set_index(&self) -> u32 {
493        self.common_header().fec_set_index
494    }
495
496    pub(crate) fn first_coding_index(&self) -> Option<u32> {
497        match self {
498            Self::ShredCode(shred) => shred.first_coding_index(),
499            Self::ShredData(_) => None,
500        }
501    }
502
503    pub fn version(&self) -> u16 {
504        self.common_header().version
505    }
506
507    // Identifier for the erasure coding set that the shred belongs to.
508    pub(crate) fn erasure_set(&self) -> ErasureSetId {
509        ErasureSetId(self.slot(), self.fec_set_index())
510    }
511
512    pub fn signature(&self) -> &Signature {
513        &self.common_header().signature
514    }
515
516    pub fn sign(&mut self, keypair: &Keypair) {
517        let data = self.signed_data().unwrap();
518        let signature = keypair.sign_message(data.as_ref());
519        self.set_signature(signature);
520    }
521
522    #[inline]
523    pub fn shred_type(&self) -> ShredType {
524        ShredType::from(self.common_header().shred_variant)
525    }
526
527    #[inline]
528    pub fn is_data(&self) -> bool {
529        self.shred_type() == ShredType::Data
530    }
531
532    #[inline]
533    pub fn is_code(&self) -> bool {
534        self.shred_type() == ShredType::Code
535    }
536
537    pub fn last_in_slot(&self) -> bool {
538        match self {
539            Self::ShredCode(_) => false,
540            Self::ShredData(shred) => shred.last_in_slot(),
541        }
542    }
543
544    /// This is not a safe function. It only changes the meta information.
545    /// Use this only for test code which doesn't care about actual shred
546    pub fn set_last_in_slot(&mut self) {
547        match self {
548            Self::ShredCode(_) => (),
549            Self::ShredData(shred) => shred.set_last_in_slot(),
550        }
551    }
552
553    pub fn data_complete(&self) -> bool {
554        match self {
555            Self::ShredCode(_) => false,
556            Self::ShredData(shred) => shred.data_complete(),
557        }
558    }
559
560    pub(crate) fn reference_tick(&self) -> u8 {
561        match self {
562            Self::ShredCode(_) => ShredFlags::SHRED_TICK_REFERENCE_MASK.bits(),
563            Self::ShredData(shred) => shred.reference_tick(),
564        }
565    }
566
567    #[must_use]
568    pub fn verify(&self, pubkey: &Pubkey) -> bool {
569        match self.signed_data() {
570            Ok(data) => self.signature().verify(pubkey.as_ref(), data.as_ref()),
571            Err(_) => false,
572        }
573    }
574
575    // Returns true if the erasure coding of the two shreds mismatch.
576    pub(crate) fn erasure_mismatch(&self, other: &Self) -> Result<bool, Error> {
577        match (self, other) {
578            (Self::ShredCode(shred), Self::ShredCode(other)) => Ok(shred.erasure_mismatch(other)),
579            _ => Err(Error::InvalidShredType),
580        }
581    }
582
583    pub(crate) fn num_data_shreds(&self) -> Result<u16, Error> {
584        match self {
585            Self::ShredCode(shred) => Ok(shred.num_data_shreds()),
586            Self::ShredData(_) => Err(Error::InvalidShredType),
587        }
588    }
589
590    pub(crate) fn num_coding_shreds(&self) -> Result<u16, Error> {
591        match self {
592            Self::ShredCode(shred) => Ok(shred.num_coding_shreds()),
593            Self::ShredData(_) => Err(Error::InvalidShredType),
594        }
595    }
596
597    /// Returns true if the other shred has the same ShredId, i.e. (slot, index,
598    /// shred-type), but different payload.
599    /// Retransmitter's signature is ignored when comparing payloads.
600    pub fn is_shred_duplicate(&self, other: &Shred) -> bool {
601        if self.id() != other.id() {
602            return false;
603        }
604        fn get_payload(shred: &Shred) -> &[u8] {
605            let Ok(offset) = shred.retransmitter_signature_offset() else {
606                return shred.payload();
607            };
608            // Assert that the retransmitter's signature is at the very end of
609            // the shred payload.
610            debug_assert_eq!(offset + SIZE_OF_SIGNATURE, shred.payload().len());
611            shred
612                .payload()
613                .get(..offset)
614                .unwrap_or_else(|| shred.payload())
615        }
616        get_payload(self) != get_payload(other)
617    }
618
619    fn retransmitter_signature_offset(&self) -> Result<usize, Error> {
620        match self {
621            Self::ShredCode(ShredCode::Merkle(shred)) => shred.retransmitter_signature_offset(),
622            Self::ShredData(ShredData::Merkle(shred)) => shred.retransmitter_signature_offset(),
623            Self::ShredCode(ShredCode::Legacy(_)) => Err(Error::InvalidShredVariant),
624            Self::ShredData(ShredData::Legacy(_)) => Err(Error::InvalidShredVariant),
625        }
626    }
627}
628
629impl From<ShredCode> for Shred {
630    fn from(shred: ShredCode) -> Self {
631        Self::ShredCode(shred)
632    }
633}
634
635impl From<ShredData> for Shred {
636    fn from(shred: ShredData) -> Self {
637        Self::ShredData(shred)
638    }
639}
640
641impl From<merkle::Shred> for Shred {
642    fn from(shred: merkle::Shred) -> Self {
643        match shred {
644            merkle::Shred::ShredCode(shred) => Self::ShredCode(ShredCode::Merkle(shred)),
645            merkle::Shred::ShredData(shred) => Self::ShredData(ShredData::Merkle(shred)),
646        }
647    }
648}
649
650impl TryFrom<Shred> for merkle::Shred {
651    type Error = Error;
652
653    fn try_from(shred: Shred) -> Result<Self, Self::Error> {
654        match shred {
655            Shred::ShredCode(ShredCode::Legacy(_)) => Err(Error::InvalidShredVariant),
656            Shred::ShredCode(ShredCode::Merkle(shred)) => Ok(Self::ShredCode(shred)),
657            Shred::ShredData(ShredData::Legacy(_)) => Err(Error::InvalidShredVariant),
658            Shred::ShredData(ShredData::Merkle(shred)) => Ok(Self::ShredData(shred)),
659        }
660    }
661}
662
663impl From<ShredVariant> for ShredType {
664    #[inline]
665    fn from(shred_variant: ShredVariant) -> Self {
666        match shred_variant {
667            ShredVariant::LegacyCode => ShredType::Code,
668            ShredVariant::LegacyData => ShredType::Data,
669            ShredVariant::MerkleCode { .. } => ShredType::Code,
670            ShredVariant::MerkleData { .. } => ShredType::Data,
671        }
672    }
673}
674
675impl From<ShredVariant> for u8 {
676    #[inline]
677    fn from(shred_variant: ShredVariant) -> u8 {
678        match shred_variant {
679            ShredVariant::LegacyCode => u8::from(ShredType::Code),
680            ShredVariant::LegacyData => u8::from(ShredType::Data),
681            ShredVariant::MerkleCode {
682                proof_size,
683                chained: false,
684                resigned: false,
685            } => proof_size | 0x40,
686            ShredVariant::MerkleCode {
687                proof_size,
688                chained: true,
689                resigned: false,
690            } => proof_size | 0x60,
691            ShredVariant::MerkleCode {
692                proof_size,
693                chained: true,
694                resigned: true,
695            } => proof_size | 0x70,
696            ShredVariant::MerkleData {
697                proof_size,
698                chained: false,
699                resigned: false,
700            } => proof_size | 0x80,
701            ShredVariant::MerkleData {
702                proof_size,
703                chained: true,
704                resigned: false,
705            } => proof_size | 0x90,
706            ShredVariant::MerkleData {
707                proof_size,
708                chained: true,
709                resigned: true,
710            } => proof_size | 0xb0,
711            ShredVariant::MerkleCode {
712                proof_size: _,
713                chained: false,
714                resigned: true,
715            }
716            | ShredVariant::MerkleData {
717                proof_size: _,
718                chained: false,
719                resigned: true,
720            } => panic!("Invalid shred variant: {shred_variant:?}"),
721        }
722    }
723}
724
725impl TryFrom<u8> for ShredVariant {
726    type Error = Error;
727    #[inline]
728    fn try_from(shred_variant: u8) -> Result<Self, Self::Error> {
729        if shred_variant == u8::from(ShredType::Code) {
730            Ok(ShredVariant::LegacyCode)
731        } else if shred_variant == u8::from(ShredType::Data) {
732            Ok(ShredVariant::LegacyData)
733        } else {
734            let proof_size = shred_variant & 0x0F;
735            match shred_variant & 0xF0 {
736                0x40 => Ok(ShredVariant::MerkleCode {
737                    proof_size,
738                    chained: false,
739                    resigned: false,
740                }),
741                0x60 => Ok(ShredVariant::MerkleCode {
742                    proof_size,
743                    chained: true,
744                    resigned: false,
745                }),
746                0x70 => Ok(ShredVariant::MerkleCode {
747                    proof_size,
748                    chained: true,
749                    resigned: true,
750                }),
751                0x80 => Ok(ShredVariant::MerkleData {
752                    proof_size,
753                    chained: false,
754                    resigned: false,
755                }),
756                0x90 => Ok(ShredVariant::MerkleData {
757                    proof_size,
758                    chained: true,
759                    resigned: false,
760                }),
761                0xb0 => Ok(ShredVariant::MerkleData {
762                    proof_size,
763                    chained: true,
764                    resigned: true,
765                }),
766                _ => Err(Error::InvalidShredVariant),
767            }
768        }
769    }
770}
771
772pub(crate) fn recover(
773    shreds: impl IntoIterator<Item = Shred>,
774    reed_solomon_cache: &ReedSolomonCache,
775) -> Result<impl Iterator<Item = Result<Shred, Error>>, Error> {
776    let shreds = shreds
777        .into_iter()
778        .map(|shred| {
779            debug_assert_matches!(
780                shred.common_header().shred_variant,
781                ShredVariant::MerkleCode { .. } | ShredVariant::MerkleData { .. }
782            );
783            merkle::Shred::try_from(shred)
784        })
785        .collect::<Result<_, _>>()?;
786    // With Merkle shreds, leader signs the Merkle root of the erasure batch
787    // and all shreds within the same erasure batch have the same signature.
788    // For recovered shreds, the (unique) signature is copied from shreds which
789    // were received from turbine (or repair) and are already sig-verified.
790    // The same signature also verifies for recovered shreds because when
791    // reconstructing the Merkle tree for the erasure batch, we will obtain the
792    // same Merkle root.
793    let shreds = merkle::recover(shreds, reed_solomon_cache)?;
794    Ok(shreds.map(|shred| shred.map(Shred::from)))
795}
796
797#[allow(clippy::too_many_arguments)]
798pub(crate) fn make_merkle_shreds_from_entries(
799    thread_pool: &ThreadPool,
800    keypair: &Keypair,
801    entries: &[Entry],
802    slot: Slot,
803    parent_slot: Slot,
804    shred_version: u16,
805    reference_tick: u8,
806    is_last_in_slot: bool,
807    chained_merkle_root: Option<Hash>,
808    next_shred_index: u32,
809    next_code_index: u32,
810    reed_solomon_cache: &ReedSolomonCache,
811    stats: &mut ProcessShredsStats,
812) -> Result<impl Iterator<Item = Shred>, Error> {
813    let now = Instant::now();
814    let entries = bincode::serialize(entries)?;
815    stats.serialize_elapsed += now.elapsed().as_micros() as u64;
816    let shreds = merkle::make_shreds_from_data(
817        thread_pool,
818        keypair,
819        chained_merkle_root,
820        &entries[..],
821        slot,
822        parent_slot,
823        shred_version,
824        reference_tick,
825        is_last_in_slot,
826        next_shred_index,
827        next_code_index,
828        reed_solomon_cache,
829        stats,
830    )?;
831    Ok(shreds.into_iter().map(Shred::from))
832}
833
834// Accepts shreds in the slot range [root + 1, max_slot].
835#[must_use]
836pub fn should_discard_shred(
837    packet: &Packet,
838    root: Slot,
839    max_slot: Slot,
840    shred_version: u16,
841    drop_unchained_merkle_shreds: impl Fn(Slot) -> bool,
842    stats: &mut ShredFetchStats,
843) -> bool {
844    debug_assert!(root < max_slot);
845    let shred = match layout::get_shred(packet) {
846        None => {
847            stats.index_overrun += 1;
848            return true;
849        }
850        Some(shred) => shred,
851    };
852    match layout::get_version(shred) {
853        None => {
854            stats.index_overrun += 1;
855            return true;
856        }
857        Some(version) => {
858            if version != shred_version {
859                stats.shred_version_mismatch += 1;
860                return true;
861            }
862        }
863    }
864    let Ok(shred_variant) = layout::get_shred_variant(shred) else {
865        stats.bad_shred_type += 1;
866        return true;
867    };
868    let slot = match layout::get_slot(shred) {
869        Some(slot) => {
870            if slot > max_slot {
871                stats.slot_out_of_range += 1;
872                return true;
873            }
874            slot
875        }
876        None => {
877            stats.slot_bad_deserialize += 1;
878            return true;
879        }
880    };
881    let Some(index) = layout::get_index(shred) else {
882        stats.index_bad_deserialize += 1;
883        return true;
884    };
885    match ShredType::from(shred_variant) {
886        ShredType::Code => {
887            if index >= shred_code::MAX_CODE_SHREDS_PER_SLOT as u32 {
888                stats.index_out_of_bounds += 1;
889                return true;
890            }
891            if slot <= root {
892                stats.slot_out_of_range += 1;
893                return true;
894            }
895        }
896        ShredType::Data => {
897            if index >= MAX_DATA_SHREDS_PER_SLOT as u32 {
898                stats.index_out_of_bounds += 1;
899                return true;
900            }
901            let Some(parent_offset) = layout::get_parent_offset(shred) else {
902                stats.bad_parent_offset += 1;
903                return true;
904            };
905            let Some(parent) = slot.checked_sub(Slot::from(parent_offset)) else {
906                stats.bad_parent_offset += 1;
907                return true;
908            };
909            if !blockstore::verify_shred_slots(slot, parent, root) {
910                stats.slot_out_of_range += 1;
911                return true;
912            }
913        }
914    }
915    match shred_variant {
916        ShredVariant::LegacyCode | ShredVariant::LegacyData => {
917            return true;
918        }
919        ShredVariant::MerkleCode { chained: false, .. } => {
920            if drop_unchained_merkle_shreds(slot) {
921                return true;
922            }
923            stats.num_shreds_merkle_code = stats.num_shreds_merkle_code.saturating_add(1);
924        }
925        ShredVariant::MerkleCode { chained: true, .. } => {
926            stats.num_shreds_merkle_code_chained =
927                stats.num_shreds_merkle_code_chained.saturating_add(1);
928        }
929        ShredVariant::MerkleData { chained: false, .. } => {
930            if drop_unchained_merkle_shreds(slot) {
931                return true;
932            }
933            stats.num_shreds_merkle_data = stats.num_shreds_merkle_data.saturating_add(1);
934        }
935        ShredVariant::MerkleData { chained: true, .. } => {
936            stats.num_shreds_merkle_data_chained =
937                stats.num_shreds_merkle_data_chained.saturating_add(1);
938        }
939    }
940    false
941}
942
943pub fn max_ticks_per_n_shreds(num_shreds: u64, shred_data_size: Option<usize>) -> u64 {
944    let ticks = create_ticks(1, 0, Hash::default());
945    max_entries_per_n_shred(&ticks[0], num_shreds, shred_data_size)
946}
947
948pub fn max_entries_per_n_shred(
949    entry: &Entry,
950    num_shreds: u64,
951    shred_data_size: Option<usize>,
952) -> u64 {
953    // Default 32:32 erasure batches yields 64 shreds; log2(64) = 6.
954    let merkle_variant = Some((
955        /*proof_size:*/ 6, /*chained:*/ true, /*resigned:*/ true,
956    ));
957    let data_buffer_size = ShredData::capacity(merkle_variant).unwrap();
958    let shred_data_size = shred_data_size.unwrap_or(data_buffer_size) as u64;
959    let vec_size = bincode::serialized_size(&vec![entry]).unwrap();
960    let entry_size = bincode::serialized_size(entry).unwrap();
961    let count_size = vec_size - entry_size;
962
963    (shred_data_size * num_shreds - count_size) / entry_size
964}
965
966pub fn verify_test_data_shred(
967    shred: &Shred,
968    index: u32,
969    slot: Slot,
970    parent: Slot,
971    pk: &Pubkey,
972    verify: bool,
973    is_last_in_slot: bool,
974    is_last_data: bool,
975) {
976    shred.sanitize().unwrap();
977    assert!(shred.is_data());
978    assert_eq!(shred.index(), index);
979    assert_eq!(shred.slot(), slot);
980    assert_eq!(shred.parent().unwrap(), parent);
981    assert_eq!(verify, shred.verify(pk));
982    if is_last_in_slot {
983        assert!(shred.last_in_slot());
984    } else {
985        assert!(!shred.last_in_slot());
986    }
987    if is_last_data {
988        assert!(shred.data_complete());
989    } else {
990        assert!(!shred.data_complete());
991    }
992}
993
994#[cfg(test)]
995mod tests {
996    use {
997        super::*,
998        assert_matches::assert_matches,
999        bincode::serialized_size,
1000        itertools::Itertools,
1001        rand::Rng,
1002        rand_chacha::{rand_core::SeedableRng, ChaChaRng},
1003        rayon::ThreadPoolBuilder,
1004        clone_solana_sdk::{shred_version, signature::Signer, signer::keypair::keypair_from_seed},
1005        std::io::{Cursor, Seek, SeekFrom, Write},
1006        test_case::test_case,
1007    };
1008
1009    const SIZE_OF_SHRED_INDEX: usize = 4;
1010    const SIZE_OF_SHRED_SLOT: usize = 8;
1011    const SIZE_OF_SHRED_VARIANT: usize = 1;
1012
1013    const OFFSET_OF_SHRED_SLOT: usize = SIZE_OF_SIGNATURE + SIZE_OF_SHRED_VARIANT;
1014    const OFFSET_OF_SHRED_INDEX: usize = OFFSET_OF_SHRED_SLOT + SIZE_OF_SHRED_SLOT;
1015    const OFFSET_OF_SHRED_VARIANT: usize = SIZE_OF_SIGNATURE;
1016
1017    fn bs58_decode<T: AsRef<[u8]>>(data: T) -> Vec<u8> {
1018        bs58::decode(data).into_vec().unwrap()
1019    }
1020
1021    pub(super) fn make_merkle_shreds_for_tests<R: Rng>(
1022        rng: &mut R,
1023        slot: Slot,
1024        data_size: usize,
1025        chained: bool,
1026        is_last_in_slot: bool,
1027    ) -> Result<Vec<merkle::Shred>, Error> {
1028        let thread_pool = ThreadPoolBuilder::new().num_threads(2).build().unwrap();
1029        let chained_merkle_root = chained.then(|| Hash::new_from_array(rng.gen()));
1030        let parent_offset = rng.gen_range(1..=u16::try_from(slot).unwrap_or(u16::MAX));
1031        let parent_slot = slot.checked_sub(u64::from(parent_offset)).unwrap();
1032        let mut data = vec![0u8; data_size];
1033        rng.fill(&mut data[..]);
1034        merkle::make_shreds_from_data(
1035            &thread_pool,
1036            &Keypair::new(),
1037            chained_merkle_root,
1038            &data[..],
1039            slot,
1040            parent_slot,
1041            rng.gen(),            // shred_version
1042            rng.gen_range(1..64), // reference_tick
1043            is_last_in_slot,
1044            rng.gen_range(0..671), // next_shred_index
1045            rng.gen_range(0..781), // next_code_index
1046            &ReedSolomonCache::default(),
1047            &mut ProcessShredsStats::default(),
1048        )
1049    }
1050
1051    #[test]
1052    fn test_shred_constants() {
1053        let common_header = ShredCommonHeader {
1054            signature: Signature::default(),
1055            shred_variant: ShredVariant::LegacyCode,
1056            slot: Slot::MAX,
1057            index: u32::MAX,
1058            version: u16::MAX,
1059            fec_set_index: u32::MAX,
1060        };
1061        let data_shred_header = DataShredHeader {
1062            parent_offset: u16::MAX,
1063            flags: ShredFlags::all(),
1064            size: u16::MAX,
1065        };
1066        let coding_shred_header = CodingShredHeader {
1067            num_data_shreds: u16::MAX,
1068            num_coding_shreds: u16::MAX,
1069            position: u16::MAX,
1070        };
1071        assert_eq!(
1072            SIZE_OF_COMMON_SHRED_HEADER,
1073            serialized_size(&common_header).unwrap() as usize
1074        );
1075        assert_eq!(
1076            SIZE_OF_CODING_SHRED_HEADERS - SIZE_OF_COMMON_SHRED_HEADER,
1077            serialized_size(&coding_shred_header).unwrap() as usize
1078        );
1079        assert_eq!(
1080            SIZE_OF_DATA_SHRED_HEADERS - SIZE_OF_COMMON_SHRED_HEADER,
1081            serialized_size(&data_shred_header).unwrap() as usize
1082        );
1083        let data_shred_header_with_size = DataShredHeader {
1084            size: 1000,
1085            ..data_shred_header
1086        };
1087        assert_eq!(
1088            SIZE_OF_DATA_SHRED_HEADERS - SIZE_OF_COMMON_SHRED_HEADER,
1089            serialized_size(&data_shred_header_with_size).unwrap() as usize
1090        );
1091        assert_eq!(
1092            SIZE_OF_SIGNATURE,
1093            bincode::serialized_size(&Signature::default()).unwrap() as usize
1094        );
1095        assert_eq!(
1096            SIZE_OF_SHRED_VARIANT,
1097            bincode::serialized_size(&ShredVariant::MerkleCode {
1098                proof_size: 15,
1099                chained: true,
1100                resigned: true
1101            })
1102            .unwrap() as usize
1103        );
1104        assert_eq!(
1105            SIZE_OF_SHRED_SLOT,
1106            bincode::serialized_size(&Slot::default()).unwrap() as usize
1107        );
1108        assert_eq!(
1109            SIZE_OF_SHRED_INDEX,
1110            bincode::serialized_size(&common_header.index).unwrap() as usize
1111        );
1112    }
1113
1114    #[test]
1115    fn test_shred_flags_reference_tick_saturates() {
1116        const MAX_REFERENCE_TICK: u8 = ShredFlags::SHRED_TICK_REFERENCE_MASK.bits();
1117        for tick in 0..=u8::MAX {
1118            let flags = ShredFlags::from_reference_tick(tick);
1119            assert_eq!(flags.bits(), tick.min(MAX_REFERENCE_TICK));
1120        }
1121    }
1122
1123    #[test]
1124    fn test_version_from_hash() {
1125        let hash = [
1126            0xa5u8, 0xa5, 0x5a, 0x5a, 0xa5, 0xa5, 0x5a, 0x5a, 0xa5, 0xa5, 0x5a, 0x5a, 0xa5, 0xa5,
1127            0x5a, 0x5a, 0xa5, 0xa5, 0x5a, 0x5a, 0xa5, 0xa5, 0x5a, 0x5a, 0xa5, 0xa5, 0x5a, 0x5a,
1128            0xa5, 0xa5, 0x5a, 0x5a,
1129        ];
1130        let version = shred_version::version_from_hash(&Hash::new_from_array(hash));
1131        assert_eq!(version, 1);
1132        let hash = [
1133            0xa5u8, 0xa5, 0x5a, 0x5a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1134            0, 0, 0, 0, 0, 0, 0, 0,
1135        ];
1136        let version = shred_version::version_from_hash(&Hash::new_from_array(hash));
1137        assert_eq!(version, 0xffff);
1138        let hash = [
1139            0xa5u8, 0xa5, 0x5a, 0x5a, 0xa5, 0xa5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1140            0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1141        ];
1142        let version = shred_version::version_from_hash(&Hash::new_from_array(hash));
1143        assert_eq!(version, 0x5a5b);
1144    }
1145
1146    #[test]
1147    fn test_invalid_parent_offset() {
1148        let shred = Shred::new_from_data(10, 0, 1000, &[1, 2, 3], ShredFlags::empty(), 0, 1, 0);
1149        let mut packet = Packet::default();
1150        shred.copy_to_packet(&mut packet);
1151        let shred_res = Shred::new_from_serialized_shred(packet.data(..).unwrap().to_vec());
1152        assert_matches!(
1153            shred.parent(),
1154            Err(Error::InvalidParentOffset {
1155                slot: 10,
1156                parent_offset: 1000
1157            })
1158        );
1159        assert_matches!(
1160            shred_res,
1161            Err(Error::InvalidParentOffset {
1162                slot: 10,
1163                parent_offset: 1000
1164            })
1165        );
1166    }
1167
1168    #[test_case(false, false)]
1169    #[test_case(false, true)]
1170    #[test_case(true, false)]
1171    #[test_case(true, true)]
1172    fn test_should_discard_shred(chained: bool, is_last_in_slot: bool) {
1173        clone_solana_logger::setup();
1174        let mut rng = rand::thread_rng();
1175        let slot = 18_291;
1176        let shreds = make_merkle_shreds_for_tests(
1177            &mut rng,
1178            slot,
1179            1200 * 5, // data_size
1180            chained,
1181            is_last_in_slot,
1182        )
1183        .unwrap();
1184        let shreds: Vec<_> = shreds.into_iter().map(Shred::from).collect();
1185        assert_eq!(shreds.iter().map(Shred::fec_set_index).dedup().count(), 1);
1186
1187        assert_matches!(shreds[0].shred_type(), ShredType::Data);
1188        let parent_slot = shreds[0].parent().unwrap();
1189        let shred_version = shreds[0].common_header().version;
1190
1191        let root = rng.gen_range(0..parent_slot);
1192        let max_slot = slot + rng.gen_range(1..65536);
1193        let mut packet = Packet::default();
1194
1195        // Data shred sanity checks!
1196        {
1197            let shred = shreds.first().unwrap();
1198            assert_eq!(shred.shred_type(), ShredType::Data);
1199            shred.copy_to_packet(&mut packet);
1200            let mut stats = ShredFetchStats::default();
1201            assert!(!should_discard_shred(
1202                &packet,
1203                root,
1204                max_slot,
1205                shred_version,
1206                |_| false, // drop_unchained_merkle_shreds
1207                &mut stats
1208            ));
1209        }
1210        {
1211            let mut packet = packet.clone();
1212            let mut stats = ShredFetchStats::default();
1213            packet.meta_mut().size = OFFSET_OF_SHRED_VARIANT;
1214            assert!(should_discard_shred(
1215                &packet,
1216                root,
1217                max_slot,
1218                shred_version,
1219                |_| false, // drop_unchained_merkle_shreds
1220                &mut stats
1221            ));
1222            assert_eq!(stats.index_overrun, 1);
1223
1224            packet.meta_mut().size = OFFSET_OF_SHRED_INDEX;
1225            assert!(should_discard_shred(
1226                &packet,
1227                root,
1228                max_slot,
1229                shred_version,
1230                |_| false, // drop_unchained_merkle_shreds
1231                &mut stats
1232            ));
1233            assert_eq!(stats.index_overrun, 2);
1234
1235            packet.meta_mut().size = OFFSET_OF_SHRED_INDEX + 1;
1236            assert!(should_discard_shred(
1237                &packet,
1238                root,
1239                max_slot,
1240                shred_version,
1241                |_| false, // drop_unchained_merkle_shreds
1242                &mut stats
1243            ));
1244            assert_eq!(stats.index_overrun, 3);
1245
1246            packet.meta_mut().size = OFFSET_OF_SHRED_INDEX + SIZE_OF_SHRED_INDEX - 1;
1247            assert!(should_discard_shred(
1248                &packet,
1249                root,
1250                max_slot,
1251                shred_version,
1252                |_| false, // drop_unchained_merkle_shreds
1253                &mut stats
1254            ));
1255            assert_eq!(stats.index_overrun, 4);
1256
1257            packet.meta_mut().size = OFFSET_OF_SHRED_INDEX + SIZE_OF_SHRED_INDEX + 2;
1258            assert!(should_discard_shred(
1259                &packet,
1260                root,
1261                max_slot,
1262                shred_version,
1263                |_| false, // drop_unchained_merkle_shreds
1264                &mut stats
1265            ));
1266            assert_eq!(stats.index_overrun, 5);
1267        }
1268        {
1269            let mut stats = ShredFetchStats::default();
1270            assert!(should_discard_shred(
1271                &packet,
1272                root,
1273                max_slot,
1274                shred_version.wrapping_add(1),
1275                |_| false, // drop_unchained_merkle_shreds
1276                &mut stats
1277            ));
1278            assert_eq!(stats.shred_version_mismatch, 1);
1279        }
1280        {
1281            let mut stats = ShredFetchStats::default();
1282            assert!(should_discard_shred(
1283                &packet,
1284                parent_slot + 1, // root
1285                max_slot,
1286                shred_version,
1287                |_| false, // drop_unchained_merkle_shreds
1288                &mut stats
1289            ));
1290            assert_eq!(stats.slot_out_of_range, 1);
1291        }
1292        {
1293            let parent_offset = 0u16;
1294            {
1295                let mut cursor = Cursor::new(packet.buffer_mut());
1296                cursor.seek(SeekFrom::Start(83)).unwrap();
1297                cursor.write_all(&parent_offset.to_le_bytes()).unwrap();
1298            }
1299            assert_eq!(
1300                layout::get_parent_offset(packet.data(..).unwrap()),
1301                Some(parent_offset)
1302            );
1303            let mut stats = ShredFetchStats::default();
1304            assert!(should_discard_shred(
1305                &packet,
1306                root,
1307                max_slot,
1308                shred_version,
1309                |_| false, // drop_unchained_merkle_shreds
1310                &mut stats
1311            ));
1312            assert_eq!(stats.slot_out_of_range, 1);
1313        }
1314        {
1315            let parent_offset = u16::try_from(slot + 1).unwrap();
1316            {
1317                let mut cursor = Cursor::new(packet.buffer_mut());
1318                cursor.seek(SeekFrom::Start(83)).unwrap();
1319                cursor.write_all(&parent_offset.to_le_bytes()).unwrap();
1320            }
1321            assert_eq!(
1322                layout::get_parent_offset(packet.data(..).unwrap()),
1323                Some(parent_offset)
1324            );
1325            let mut stats = ShredFetchStats::default();
1326            assert!(should_discard_shred(
1327                &packet,
1328                root,
1329                max_slot,
1330                shred_version,
1331                |_| false, // drop_unchained_merkle_shreds
1332                &mut stats
1333            ));
1334            assert_eq!(stats.bad_parent_offset, 1);
1335        }
1336        {
1337            let index = u32::MAX - 10;
1338            {
1339                let mut cursor = Cursor::new(packet.buffer_mut());
1340                cursor
1341                    .seek(SeekFrom::Start(OFFSET_OF_SHRED_INDEX as u64))
1342                    .unwrap();
1343                cursor.write_all(&index.to_le_bytes()).unwrap();
1344            }
1345            assert_eq!(layout::get_index(packet.data(..).unwrap()), Some(index));
1346            let mut stats = ShredFetchStats::default();
1347            assert!(should_discard_shred(
1348                &packet,
1349                root,
1350                max_slot,
1351                shred_version,
1352                |_| false, // drop_unchained_merkle_shreds
1353                &mut stats
1354            ));
1355            assert_eq!(stats.index_out_of_bounds, 1);
1356        }
1357
1358        // Coding shred sanity checks!
1359        {
1360            let shred = shreds.last().unwrap();
1361            assert_eq!(shred.shred_type(), ShredType::Code);
1362            shreds.last().unwrap().copy_to_packet(&mut packet);
1363            let mut stats = ShredFetchStats::default();
1364            assert!(!should_discard_shred(
1365                &packet,
1366                root,
1367                max_slot,
1368                shred_version,
1369                |_| false, // drop_unchained_merkle_shreds
1370                &mut stats
1371            ));
1372        }
1373        {
1374            let mut stats = ShredFetchStats::default();
1375            assert!(should_discard_shred(
1376                &packet,
1377                root,
1378                max_slot,
1379                shred_version.wrapping_add(1),
1380                |_| false, // drop_unchained_merkle_shreds
1381                &mut stats
1382            ));
1383            assert_eq!(stats.shred_version_mismatch, 1);
1384        }
1385        {
1386            let mut stats = ShredFetchStats::default();
1387            assert!(should_discard_shred(
1388                &packet,
1389                slot, // root
1390                max_slot,
1391                shred_version,
1392                |_| false, // drop_unchained_merkle_shreds
1393                &mut stats
1394            ));
1395            assert_eq!(stats.slot_out_of_range, 1);
1396        }
1397        {
1398            let index = u32::try_from(MAX_CODE_SHREDS_PER_SLOT).unwrap();
1399            {
1400                let mut cursor = Cursor::new(packet.buffer_mut());
1401                cursor
1402                    .seek(SeekFrom::Start(OFFSET_OF_SHRED_INDEX as u64))
1403                    .unwrap();
1404                cursor.write_all(&index.to_le_bytes()).unwrap();
1405            }
1406            assert_eq!(layout::get_index(packet.data(..).unwrap()), Some(index));
1407            let mut stats = ShredFetchStats::default();
1408            assert!(should_discard_shred(
1409                &packet,
1410                root,
1411                max_slot,
1412                shred_version,
1413                |_| false, // drop_unchained_merkle_shreds
1414                &mut stats
1415            ));
1416            assert_eq!(stats.index_out_of_bounds, 1);
1417        }
1418    }
1419
1420    // Asserts that ShredType is backward compatible with u8.
1421    #[test]
1422    fn test_shred_type_compat() {
1423        assert_eq!(std::mem::size_of::<ShredType>(), std::mem::size_of::<u8>());
1424        assert_matches!(ShredType::try_from(0u8), Err(_));
1425        assert_matches!(ShredType::try_from(1u8), Err(_));
1426        assert_matches!(bincode::deserialize::<ShredType>(&[0u8]), Err(_));
1427        assert_matches!(bincode::deserialize::<ShredType>(&[1u8]), Err(_));
1428        // data shred
1429        assert_eq!(ShredType::Data as u8, 0b1010_0101);
1430        assert_eq!(u8::from(ShredType::Data), 0b1010_0101);
1431        assert_eq!(ShredType::try_from(0b1010_0101), Ok(ShredType::Data));
1432        let buf = bincode::serialize(&ShredType::Data).unwrap();
1433        assert_eq!(buf, vec![0b1010_0101]);
1434        assert_matches!(
1435            bincode::deserialize::<ShredType>(&[0b1010_0101]),
1436            Ok(ShredType::Data)
1437        );
1438        // coding shred
1439        assert_eq!(ShredType::Code as u8, 0b0101_1010);
1440        assert_eq!(u8::from(ShredType::Code), 0b0101_1010);
1441        assert_eq!(ShredType::try_from(0b0101_1010), Ok(ShredType::Code));
1442        let buf = bincode::serialize(&ShredType::Code).unwrap();
1443        assert_eq!(buf, vec![0b0101_1010]);
1444        assert_matches!(
1445            bincode::deserialize::<ShredType>(&[0b0101_1010]),
1446            Ok(ShredType::Code)
1447        );
1448    }
1449
1450    #[test]
1451    fn test_shred_variant_compat() {
1452        assert_matches!(ShredVariant::try_from(0u8), Err(_));
1453        assert_matches!(ShredVariant::try_from(1u8), Err(_));
1454        assert_matches!(ShredVariant::try_from(0b0101_0000), Err(_));
1455        assert_matches!(ShredVariant::try_from(0b1010_0000), Err(_));
1456        assert_matches!(bincode::deserialize::<ShredVariant>(&[0b0101_0000]), Err(_));
1457        assert_matches!(bincode::deserialize::<ShredVariant>(&[0b1010_0000]), Err(_));
1458        // Legacy coding shred.
1459        assert_eq!(u8::from(ShredVariant::LegacyCode), 0b0101_1010);
1460        assert_eq!(ShredType::from(ShredVariant::LegacyCode), ShredType::Code);
1461        assert_matches!(
1462            ShredVariant::try_from(0b0101_1010),
1463            Ok(ShredVariant::LegacyCode)
1464        );
1465        let buf = bincode::serialize(&ShredVariant::LegacyCode).unwrap();
1466        assert_eq!(buf, vec![0b0101_1010]);
1467        assert_matches!(
1468            bincode::deserialize::<ShredVariant>(&[0b0101_1010]),
1469            Ok(ShredVariant::LegacyCode)
1470        );
1471        // Legacy data shred.
1472        assert_eq!(u8::from(ShredVariant::LegacyData), 0b1010_0101);
1473        assert_eq!(ShredType::from(ShredVariant::LegacyData), ShredType::Data);
1474        assert_matches!(
1475            ShredVariant::try_from(0b1010_0101),
1476            Ok(ShredVariant::LegacyData)
1477        );
1478        let buf = bincode::serialize(&ShredVariant::LegacyData).unwrap();
1479        assert_eq!(buf, vec![0b1010_0101]);
1480        assert_matches!(
1481            bincode::deserialize::<ShredVariant>(&[0b1010_0101]),
1482            Ok(ShredVariant::LegacyData)
1483        );
1484    }
1485
1486    #[test_case(false, false, 0b0100_0000)]
1487    #[test_case(true, false, 0b0110_0000)]
1488    #[test_case(true, true, 0b0111_0000)]
1489    fn test_shred_variant_compat_merkle_code(chained: bool, resigned: bool, byte: u8) {
1490        for proof_size in 0..=15u8 {
1491            let byte = byte | proof_size;
1492            assert_eq!(
1493                u8::from(ShredVariant::MerkleCode {
1494                    proof_size,
1495                    chained,
1496                    resigned,
1497                }),
1498                byte
1499            );
1500            assert_eq!(
1501                ShredType::from(ShredVariant::MerkleCode {
1502                    proof_size,
1503                    chained,
1504                    resigned,
1505                }),
1506                ShredType::Code
1507            );
1508            assert_eq!(
1509                ShredVariant::try_from(byte).unwrap(),
1510                ShredVariant::MerkleCode {
1511                    proof_size,
1512                    chained,
1513                    resigned,
1514                },
1515            );
1516            let buf = bincode::serialize(&ShredVariant::MerkleCode {
1517                proof_size,
1518                chained,
1519                resigned,
1520            })
1521            .unwrap();
1522            assert_eq!(buf, vec![byte]);
1523            assert_eq!(
1524                bincode::deserialize::<ShredVariant>(&[byte]).unwrap(),
1525                ShredVariant::MerkleCode {
1526                    proof_size,
1527                    chained,
1528                    resigned,
1529                }
1530            );
1531        }
1532    }
1533
1534    #[test_case(false, false, 0b1000_0000)]
1535    #[test_case(true, false, 0b1001_0000)]
1536    #[test_case(true, true, 0b1011_0000)]
1537    fn test_shred_variant_compat_merkle_data(chained: bool, resigned: bool, byte: u8) {
1538        for proof_size in 0..=15u8 {
1539            let byte = byte | proof_size;
1540            assert_eq!(
1541                u8::from(ShredVariant::MerkleData {
1542                    proof_size,
1543                    chained,
1544                    resigned,
1545                }),
1546                byte
1547            );
1548            assert_eq!(
1549                ShredType::from(ShredVariant::MerkleData {
1550                    proof_size,
1551                    chained,
1552                    resigned,
1553                }),
1554                ShredType::Data
1555            );
1556            assert_eq!(
1557                ShredVariant::try_from(byte).unwrap(),
1558                ShredVariant::MerkleData {
1559                    proof_size,
1560                    chained,
1561                    resigned
1562                }
1563            );
1564            let buf = bincode::serialize(&ShredVariant::MerkleData {
1565                proof_size,
1566                chained,
1567                resigned,
1568            })
1569            .unwrap();
1570            assert_eq!(buf, vec![byte]);
1571            assert_eq!(
1572                bincode::deserialize::<ShredVariant>(&[byte]).unwrap(),
1573                ShredVariant::MerkleData {
1574                    proof_size,
1575                    chained,
1576                    resigned
1577                }
1578            );
1579        }
1580    }
1581
1582    #[test]
1583    fn test_shred_seed() {
1584        let mut rng = ChaChaRng::from_seed([147u8; 32]);
1585        let leader = Pubkey::new_from_array(rng.gen());
1586        let key = ShredId(
1587            141939602, // slot
1588            28685,     // index
1589            ShredType::Data,
1590        );
1591        assert_eq!(
1592            bs58::encode(key.seed(&leader)).into_string(),
1593            "Gp4kUM4ZpWGQN5XSCyM9YHYWEBCAZLa94ZQuSgDE4r56"
1594        );
1595        let leader = Pubkey::new_from_array(rng.gen());
1596        let key = ShredId(
1597            141945197, // slot
1598            23418,     // index
1599            ShredType::Code,
1600        );
1601        assert_eq!(
1602            bs58::encode(key.seed(&leader)).into_string(),
1603            "G1gmFe1QUM8nhDApk6BqvPgw3TQV2Qc5bpKppa96qbVb"
1604        );
1605    }
1606
1607    fn verify_shred_layout(shred: &Shred, packet: &Packet) {
1608        let data = layout::get_shred(packet).unwrap();
1609        assert_eq!(data, packet.data(..).unwrap());
1610        assert_eq!(layout::get_slot(data), Some(shred.slot()));
1611        assert_eq!(layout::get_index(data), Some(shred.index()));
1612        assert_eq!(layout::get_version(data), Some(shred.version()));
1613        assert_eq!(layout::get_shred_id(data), Some(shred.id()));
1614        assert_eq!(layout::get_signature(data), Some(*shred.signature()));
1615        assert_eq!(layout::get_shred_type(data).unwrap(), shred.shred_type());
1616        match shred.shred_type() {
1617            ShredType::Code => {
1618                assert_matches!(
1619                    layout::get_reference_tick(data),
1620                    Err(Error::InvalidShredType)
1621                );
1622            }
1623            ShredType::Data => {
1624                assert_eq!(
1625                    layout::get_reference_tick(data).unwrap(),
1626                    shred.reference_tick()
1627                );
1628                let parent_offset = layout::get_parent_offset(data).unwrap();
1629                let slot = layout::get_slot(data).unwrap();
1630                let parent = slot.checked_sub(Slot::from(parent_offset)).unwrap();
1631                assert_eq!(parent, shred.parent().unwrap());
1632            }
1633        }
1634    }
1635
1636    #[test]
1637    fn test_serde_compat_shred_data() {
1638        const SEED: &str = "6qG9NGWEtoTugS4Zgs46u8zTccEJuRHtrNMiUayLHCxt";
1639        const PAYLOAD: &str = "hNX8YgJCQwSFGJkZ6qZLiepwPjpctC9UCsMD1SNNQurBXv\
1640        rm7KKfLmPRMM9CpWHt6MsJuEWpDXLGwH9qdziJzGKhBMfYH63avcchjdaUiMqzVip7cUD\
1641        kqZ9zZJMrHCCUDnxxKMupsJWKroUSjKeo7hrug2KfHah85VckXpRna4R9QpH7tf2WVBTD\
1642        M4m3EerctsEQs8eZaTRxzTVkhtJYdNf74KZbH58dc3Yn2qUxF1mexWoPS6L5oZBatx";
1643        let mut rng = {
1644            let seed = <[u8; 32]>::try_from(bs58_decode(SEED)).unwrap();
1645            ChaChaRng::from_seed(seed)
1646        };
1647        let mut data = [0u8; legacy::ShredData::CAPACITY];
1648        rng.fill(&mut data[..]);
1649
1650        let mut seed = [0u8; Keypair::SECRET_KEY_LENGTH];
1651        rng.fill(&mut seed[..]);
1652        let keypair = keypair_from_seed(&seed).unwrap();
1653        let mut shred = Shred::new_from_data(
1654            141939602, // slot
1655            28685,     // index
1656            36390,     // parent_offset
1657            &data,     // data
1658            ShredFlags::LAST_SHRED_IN_SLOT,
1659            37,    // reference_tick
1660            45189, // version
1661            28657, // fec_set_index
1662        );
1663        shred.sign(&keypair);
1664        assert!(shred.verify(&keypair.pubkey()));
1665        assert_matches!(shred.sanitize(), Ok(()));
1666        let mut payload = bs58_decode(PAYLOAD);
1667        payload.extend({
1668            let skip = payload.len() - SIZE_OF_DATA_SHRED_HEADERS;
1669            data.iter().skip(skip).copied()
1670        });
1671        let mut packet = Packet::default();
1672        packet.buffer_mut()[..payload.len()].copy_from_slice(&payload);
1673        packet.meta_mut().size = payload.len();
1674        assert_eq!(shred.bytes_to_store(), payload);
1675        assert_eq!(shred, Shred::new_from_serialized_shred(payload).unwrap());
1676        verify_shred_layout(&shred, &packet);
1677    }
1678
1679    #[test]
1680    fn test_serde_compat_shred_data_empty() {
1681        const SEED: &str = "E3M5hm8yAEB7iPhQxFypAkLqxNeZCTuGBDMa8Jdrghoo";
1682        const PAYLOAD: &str = "nRNFVBEsV9FEM5KfmsCXJsgELRSkCV55drTavdy5aZPnsp\
1683        B8WvsgY99ZuNHDnwkrqe6Lx7ARVmercwugR5HwDcLA9ivKMypk9PNucDPLs67TXWy6k9R\
1684        ozKmy";
1685        let mut rng = {
1686            let seed = <[u8; 32]>::try_from(bs58_decode(SEED)).unwrap();
1687            ChaChaRng::from_seed(seed)
1688        };
1689        let mut seed = [0u8; Keypair::SECRET_KEY_LENGTH];
1690        rng.fill(&mut seed[..]);
1691        let keypair = keypair_from_seed(&seed).unwrap();
1692        let mut shred = Shred::new_from_data(
1693            142076266, // slot
1694            21443,     // index
1695            51279,     // parent_offset
1696            &[],       // data
1697            ShredFlags::DATA_COMPLETE_SHRED,
1698            49,    // reference_tick
1699            59445, // version
1700            21414, // fec_set_index
1701        );
1702        shred.sign(&keypair);
1703        assert!(shred.verify(&keypair.pubkey()));
1704        assert_matches!(shred.sanitize(), Ok(()));
1705        let payload = bs58_decode(PAYLOAD);
1706        let mut packet = Packet::default();
1707        packet.buffer_mut()[..payload.len()].copy_from_slice(&payload);
1708        packet.meta_mut().size = payload.len();
1709        assert_eq!(shred.bytes_to_store(), payload);
1710        assert_eq!(shred, Shred::new_from_serialized_shred(payload).unwrap());
1711        verify_shred_layout(&shred, &packet);
1712    }
1713
1714    #[test]
1715    fn test_serde_compat_shred_code() {
1716        const SEED: &str = "4jfjh3UZVyaEgvyG9oQmNyFY9yHDmbeH9eUhnBKkrcrN";
1717        const PAYLOAD: &str = "3xGsXwzkPpLFuKwbbfKMUxt1B6VqQPzbvvAkxRNCX9kNEP\
1718        sa2VifwGBtFuNm3CWXdmQizDz5vJjDHu6ZqqaBCSfrHurag87qAXwTtjNPhZzKEew5pLc\
1719        aY6cooiAch2vpfixNYSDjnirozje5cmUtGuYs1asXwsAKSN3QdWHz3XGParWkZeUMAzRV\
1720        1UPEDZ7vETKbxeNixKbzZzo47Lakh3C35hS74ocfj23CWoW1JpkETkXjUpXcfcv6cS";
1721        let mut rng = {
1722            let seed = <[u8; 32]>::try_from(bs58_decode(SEED)).unwrap();
1723            ChaChaRng::from_seed(seed)
1724        };
1725        let mut parity_shard = vec![0u8; legacy::SIZE_OF_ERASURE_ENCODED_SLICE];
1726        rng.fill(&mut parity_shard[..]);
1727        let mut seed = [0u8; Keypair::SECRET_KEY_LENGTH];
1728        rng.fill(&mut seed[..]);
1729        let keypair = keypair_from_seed(&seed).unwrap();
1730        let mut shred = Shred::new_from_parity_shard(
1731            141945197, // slot
1732            23418,     // index
1733            &parity_shard,
1734            21259, // fec_set_index
1735            32,    // num_data_shreds
1736            58,    // num_coding_shreds
1737            43,    // position
1738            47298, // version
1739        );
1740        shred.sign(&keypair);
1741        assert!(shred.verify(&keypair.pubkey()));
1742        assert_matches!(shred.sanitize(), Ok(()));
1743        let mut payload = bs58_decode(PAYLOAD);
1744        payload.extend({
1745            let skip = payload.len() - SIZE_OF_CODING_SHRED_HEADERS;
1746            parity_shard.iter().skip(skip).copied()
1747        });
1748        let mut packet = Packet::default();
1749        packet.buffer_mut()[..payload.len()].copy_from_slice(&payload);
1750        packet.meta_mut().size = payload.len();
1751        assert_eq!(shred.bytes_to_store(), payload);
1752        assert_eq!(shred, Shred::new_from_serialized_shred(payload).unwrap());
1753        verify_shred_layout(&shred, &packet);
1754    }
1755
1756    #[test]
1757    fn test_shred_flags() {
1758        fn make_shred(is_last_data: bool, is_last_in_slot: bool, reference_tick: u8) -> Shred {
1759            let flags = if is_last_in_slot {
1760                assert!(is_last_data);
1761                ShredFlags::LAST_SHRED_IN_SLOT
1762            } else if is_last_data {
1763                ShredFlags::DATA_COMPLETE_SHRED
1764            } else {
1765                ShredFlags::empty()
1766            };
1767            Shred::new_from_data(
1768                0,   // slot
1769                0,   // index
1770                0,   // parent_offset
1771                &[], // data
1772                flags,
1773                reference_tick,
1774                0, // version
1775                0, // fec_set_index
1776            )
1777        }
1778        fn check_shred_flags(
1779            shred: &Shred,
1780            is_last_data: bool,
1781            is_last_in_slot: bool,
1782            reference_tick: u8,
1783        ) {
1784            assert_eq!(shred.data_complete(), is_last_data);
1785            assert_eq!(shred.last_in_slot(), is_last_in_slot);
1786            assert_eq!(shred.reference_tick(), reference_tick.min(63u8));
1787            assert_eq!(
1788                layout::get_reference_tick(shred.payload()).unwrap(),
1789                reference_tick.min(63u8),
1790            );
1791        }
1792        for is_last_data in [false, true] {
1793            for is_last_in_slot in [false, true] {
1794                // LAST_SHRED_IN_SLOT also implies DATA_COMPLETE_SHRED. So it
1795                // cannot be LAST_SHRED_IN_SLOT if not DATA_COMPLETE_SHRED.
1796                let is_last_in_slot = is_last_in_slot && is_last_data;
1797                for reference_tick in [0, 37, 63, 64, 80, 128, 255] {
1798                    let mut shred = make_shred(is_last_data, is_last_in_slot, reference_tick);
1799                    check_shred_flags(&shred, is_last_data, is_last_in_slot, reference_tick);
1800                    shred.set_last_in_slot();
1801                    check_shred_flags(&shred, true, true, reference_tick);
1802                }
1803            }
1804        }
1805    }
1806
1807    #[test]
1808    fn test_shred_flags_serde() {
1809        let flags: ShredFlags = bincode::deserialize(&[0b0001_0101]).unwrap();
1810        assert_eq!(flags, ShredFlags::from_bits(0b0001_0101).unwrap());
1811        assert!(!flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1812        assert!(!flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1813        assert_eq!((flags & ShredFlags::SHRED_TICK_REFERENCE_MASK).bits(), 21u8);
1814        assert_eq!(bincode::serialize(&flags).unwrap(), [0b0001_0101]);
1815
1816        let flags: ShredFlags = bincode::deserialize(&[0b0111_0001]).unwrap();
1817        assert_eq!(flags, ShredFlags::from_bits(0b0111_0001).unwrap());
1818        assert!(flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1819        assert!(!flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1820        assert_eq!((flags & ShredFlags::SHRED_TICK_REFERENCE_MASK).bits(), 49u8);
1821        assert_eq!(bincode::serialize(&flags).unwrap(), [0b0111_0001]);
1822
1823        let flags: ShredFlags = bincode::deserialize(&[0b1110_0101]).unwrap();
1824        assert_eq!(flags, ShredFlags::from_bits(0b1110_0101).unwrap());
1825        assert!(flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1826        assert!(flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1827        assert_eq!((flags & ShredFlags::SHRED_TICK_REFERENCE_MASK).bits(), 37u8);
1828        assert_eq!(bincode::serialize(&flags).unwrap(), [0b1110_0101]);
1829
1830        let flags: ShredFlags = bincode::deserialize(&[0b1011_1101]).unwrap();
1831        assert_eq!(flags, ShredFlags::from_bits(0b1011_1101).unwrap());
1832        assert!(!flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1833        assert!(!flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1834        assert_eq!((flags & ShredFlags::SHRED_TICK_REFERENCE_MASK).bits(), 61u8);
1835        assert_eq!(bincode::serialize(&flags).unwrap(), [0b1011_1101]);
1836    }
1837
1838    // Verifies that LAST_SHRED_IN_SLOT also implies DATA_COMPLETE_SHRED.
1839    #[test]
1840    fn test_shred_flags_data_complete() {
1841        let mut flags = ShredFlags::empty();
1842        assert!(!flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1843        assert!(!flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1844        flags.insert(ShredFlags::LAST_SHRED_IN_SLOT);
1845        assert!(flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1846        assert!(flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1847
1848        let mut flags = ShredFlags::from_bits(0b0011_1111).unwrap();
1849        assert!(!flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1850        assert!(!flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1851        flags |= ShredFlags::LAST_SHRED_IN_SLOT;
1852        assert!(flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1853        assert!(flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1854
1855        let mut flags: ShredFlags = bincode::deserialize(&[0b1011_1111]).unwrap();
1856        assert!(!flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1857        assert!(!flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1858        flags.insert(ShredFlags::LAST_SHRED_IN_SLOT);
1859        assert!(flags.contains(ShredFlags::DATA_COMPLETE_SHRED));
1860        assert!(flags.contains(ShredFlags::LAST_SHRED_IN_SLOT));
1861    }
1862
1863    #[test_case(false, false)]
1864    #[test_case(false, true)]
1865    #[test_case(true, false)]
1866    #[test_case(true, true)]
1867    fn test_is_shred_duplicate(chained: bool, is_last_in_slot: bool) {
1868        fn fill_retransmitter_signature<R: Rng>(
1869            rng: &mut R,
1870            shred: Shred,
1871            chained: bool,
1872            is_last_in_slot: bool,
1873        ) -> Shred {
1874            let mut shred = shred.into_payload();
1875            let mut signature = [0u8; SIGNATURE_BYTES];
1876            rng.fill(&mut signature[..]);
1877            let out = layout::set_retransmitter_signature(&mut shred, &Signature::from(signature));
1878            if chained && is_last_in_slot {
1879                assert_matches!(out, Ok(()));
1880            } else {
1881                assert_matches!(out, Err(Error::InvalidShredVariant));
1882            }
1883            Shred::new_from_serialized_shred(shred).unwrap()
1884        }
1885        let mut rng = rand::thread_rng();
1886        let slot = 285_376_049 + rng.gen_range(0..100_000);
1887        let shreds: Vec<_> = make_merkle_shreds_for_tests(
1888            &mut rng,
1889            slot,
1890            1200 * 5, // data_size
1891            chained,
1892            is_last_in_slot,
1893        )
1894        .unwrap()
1895        .into_iter()
1896        .map(Shred::from)
1897        .map(|shred| fill_retransmitter_signature(&mut rng, shred, chained, is_last_in_slot))
1898        .collect();
1899        {
1900            let num_data_shreds = shreds.iter().filter(|shred| shred.is_data()).count();
1901            let num_coding_shreds = shreds.iter().filter(|shred| shred.is_code()).count();
1902            assert!(num_data_shreds > if is_last_in_slot { 31 } else { 5 });
1903            assert!(num_coding_shreds > if is_last_in_slot { 31 } else { 20 });
1904        }
1905        // Shreds of different (slot, index, shred-type) are not duplicate.
1906        // A shred is not a duplicate of itself either.
1907        for shred in &shreds {
1908            for other in &shreds {
1909                assert!(!shred.is_shred_duplicate(other));
1910            }
1911        }
1912        // Different retransmitter signature does not make shreds duplicate.
1913        for shred in &shreds {
1914            let other =
1915                fill_retransmitter_signature(&mut rng, shred.clone(), chained, is_last_in_slot);
1916            if chained && is_last_in_slot {
1917                assert_ne!(shred.payload(), other.payload());
1918            }
1919            assert!(!shred.is_shred_duplicate(&other));
1920            assert!(!other.is_shred_duplicate(shred));
1921        }
1922        // Shreds of the same (slot, index, shred-type) with different payload
1923        // (ignoring retransmitter signature) are duplicate.
1924        for shred in &shreds {
1925            let mut other = shred.payload().clone();
1926            other[90] = other[90].wrapping_add(1);
1927            let other = Shred::new_from_serialized_shred(other).unwrap();
1928            assert_ne!(shred.payload(), other.payload());
1929            assert_eq!(
1930                layout::get_retransmitter_signature(shred.payload()).ok(),
1931                layout::get_retransmitter_signature(other.payload()).ok()
1932            );
1933            assert!(shred.is_shred_duplicate(&other));
1934            assert!(other.is_shred_duplicate(shred));
1935        }
1936    }
1937}