Skip to main content

clone_solana_ledger/shred/
shred_code.rs

1use {
2    crate::shred::{
3        common::dispatch,
4        legacy, merkle,
5        payload::Payload,
6        traits::{Shred, ShredCode as ShredCodeTrait},
7        CodingShredHeader, Error, ShredCommonHeader, ShredType, SignedData,
8        DATA_SHREDS_PER_FEC_BLOCK, MAX_DATA_SHREDS_PER_SLOT, SIZE_OF_NONCE,
9    },
10    clone_solana_sdk::{clock::Slot, hash::Hash, packet::PACKET_DATA_SIZE, signature::Signature},
11    static_assertions::const_assert_eq,
12};
13
14const_assert_eq!(MAX_CODE_SHREDS_PER_SLOT, 32_768);
15pub const MAX_CODE_SHREDS_PER_SLOT: usize = MAX_DATA_SHREDS_PER_SLOT;
16
17const_assert_eq!(ShredCode::SIZE_OF_PAYLOAD, 1228);
18
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub enum ShredCode {
21    Legacy(legacy::ShredCode),
22    Merkle(merkle::ShredCode),
23}
24
25impl ShredCode {
26    pub(super) const SIZE_OF_PAYLOAD: usize = PACKET_DATA_SIZE - SIZE_OF_NONCE;
27
28    dispatch!(fn coding_header(&self) -> &CodingShredHeader);
29
30    dispatch!(pub(super) fn common_header(&self) -> &ShredCommonHeader);
31    dispatch!(pub(super) fn erasure_shard(&self) -> Result<&[u8], Error>);
32    dispatch!(pub(super) fn erasure_shard_index(&self) -> Result<usize, Error>);
33    dispatch!(pub(super) fn first_coding_index(&self) -> Option<u32>);
34    dispatch!(pub(super) fn into_payload(self) -> Payload);
35    dispatch!(pub(super) fn payload(&self) -> &Payload);
36    dispatch!(pub(super) fn sanitize(&self) -> Result<(), Error>);
37    dispatch!(pub(super) fn set_signature(&mut self, signature: Signature));
38
39    // Only for tests.
40    dispatch!(pub(super) fn set_index(&mut self, index: u32));
41    dispatch!(pub(super) fn set_slot(&mut self, slot: Slot));
42
43    pub(super) fn signed_data(&self) -> Result<SignedData, Error> {
44        match self {
45            Self::Legacy(shred) => Ok(SignedData::Chunk(shred.signed_data()?)),
46            Self::Merkle(shred) => Ok(SignedData::MerkleRoot(shred.signed_data()?)),
47        }
48    }
49
50    pub(super) fn chained_merkle_root(&self) -> Result<Hash, Error> {
51        match self {
52            Self::Legacy(_) => Err(Error::InvalidShredType),
53            Self::Merkle(shred) => shred.chained_merkle_root(),
54        }
55    }
56
57    pub(super) fn merkle_root(&self) -> Result<Hash, Error> {
58        match self {
59            Self::Legacy(_) => Err(Error::InvalidShredType),
60            Self::Merkle(shred) => shred.merkle_root(),
61        }
62    }
63
64    pub(super) fn new_from_parity_shard(
65        slot: Slot,
66        index: u32,
67        parity_shard: &[u8],
68        fec_set_index: u32,
69        num_data_shreds: u16,
70        num_coding_shreds: u16,
71        position: u16,
72        version: u16,
73    ) -> Self {
74        Self::from(legacy::ShredCode::new_from_parity_shard(
75            slot,
76            index,
77            parity_shard,
78            fec_set_index,
79            num_data_shreds,
80            num_coding_shreds,
81            position,
82            version,
83        ))
84    }
85
86    pub(super) fn num_data_shreds(&self) -> u16 {
87        self.coding_header().num_data_shreds
88    }
89
90    pub(super) fn num_coding_shreds(&self) -> u16 {
91        self.coding_header().num_coding_shreds
92    }
93
94    // Returns true if the erasure coding of the two shreds mismatch.
95    pub(super) fn erasure_mismatch(&self, other: &ShredCode) -> bool {
96        match (self, other) {
97            (Self::Legacy(shred), Self::Legacy(other)) => erasure_mismatch(shred, other),
98            (Self::Legacy(_), Self::Merkle(_)) => true,
99            (Self::Merkle(_), Self::Legacy(_)) => true,
100            (Self::Merkle(shred), Self::Merkle(other)) => {
101                // Merkle shreds within the same erasure batch have the same
102                // merkle root. The root of the merkle tree is signed. So
103                // either the signatures match or one fails sigverify.
104                erasure_mismatch(shred, other)
105                    || shred.common_header().signature != other.common_header().signature
106            }
107        }
108    }
109
110    pub(super) fn retransmitter_signature(&self) -> Result<Signature, Error> {
111        match self {
112            Self::Legacy(_) => Err(Error::InvalidShredVariant),
113            Self::Merkle(shred) => shred.retransmitter_signature(),
114        }
115    }
116}
117
118impl From<legacy::ShredCode> for ShredCode {
119    fn from(shred: legacy::ShredCode) -> Self {
120        Self::Legacy(shred)
121    }
122}
123
124impl From<merkle::ShredCode> for ShredCode {
125    fn from(shred: merkle::ShredCode) -> Self {
126        Self::Merkle(shred)
127    }
128}
129
130#[inline]
131pub(super) fn erasure_shard_index<T: ShredCodeTrait>(shred: &T) -> Option<usize> {
132    // Assert that the last shred index in the erasure set does not
133    // overshoot MAX_{DATA,CODE}_SHREDS_PER_SLOT.
134    let common_header = shred.common_header();
135    let coding_header = shred.coding_header();
136    if common_header
137        .fec_set_index
138        .checked_add(u32::from(coding_header.num_data_shreds.checked_sub(1)?))? as usize
139        >= MAX_DATA_SHREDS_PER_SLOT
140    {
141        return None;
142    }
143    if shred
144        .first_coding_index()?
145        .checked_add(u32::from(coding_header.num_coding_shreds.checked_sub(1)?))? as usize
146        >= MAX_CODE_SHREDS_PER_SLOT
147    {
148        return None;
149    }
150    let num_data_shreds = usize::from(coding_header.num_data_shreds);
151    let num_coding_shreds = usize::from(coding_header.num_coding_shreds);
152    let position = usize::from(coding_header.position);
153    let fec_set_size = num_data_shreds.checked_add(num_coding_shreds)?;
154    let index = position.checked_add(num_data_shreds)?;
155    (index < fec_set_size).then_some(index)
156}
157
158pub(super) fn sanitize<T: ShredCodeTrait>(shred: &T) -> Result<(), Error> {
159    if shred.payload().len() != T::SIZE_OF_PAYLOAD {
160        return Err(Error::InvalidPayloadSize(shred.payload().len()));
161    }
162    let common_header = shred.common_header();
163    let coding_header = shred.coding_header();
164    if common_header.index as usize >= MAX_CODE_SHREDS_PER_SLOT {
165        return Err(Error::InvalidShredIndex(
166            ShredType::Code,
167            common_header.index,
168        ));
169    }
170    let num_coding_shreds = usize::from(coding_header.num_coding_shreds);
171    if num_coding_shreds > 8 * DATA_SHREDS_PER_FEC_BLOCK {
172        return Err(Error::InvalidNumCodingShreds(
173            coding_header.num_coding_shreds,
174        ));
175    }
176    let _shard_index = shred.erasure_shard_index()?;
177    let _erasure_shard = shred.erasure_shard()?;
178    Ok(())
179}
180
181pub(super) fn erasure_mismatch<T: ShredCodeTrait>(shred: &T, other: &T) -> bool {
182    let CodingShredHeader {
183        num_data_shreds,
184        num_coding_shreds,
185        position: _,
186    } = shred.coding_header();
187    *num_coding_shreds != other.coding_header().num_coding_shreds
188        || *num_data_shreds != other.coding_header().num_data_shreds
189        || shred.first_coding_index() != other.first_coding_index()
190}