1#[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
94pub 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
104const 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
112pub const DATA_SHREDS_PER_FEC_BLOCK: usize = 32;
117
118const_assert_eq!(LEGACY_SHRED_DATA_CAPACITY, 1051);
120pub const LEGACY_SHRED_DATA_CAPACITY: usize = legacy::ShredData::CAPACITY;
121
122bitflags! {
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 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(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(usize),
168 #[error("Invalid proof size: {0}")]
169 InvalidProofSize(u8),
170 #[error("Invalid recovered shred")]
171 InvalidRecoveredShred,
172 #[error("Invalid shard size: {0}")]
173 InvalidShardSize(usize),
174 #[error("Invalid shred flags: {0}")]
175 InvalidShredFlags(u8),
176 #[error("Invalid {0:?} shred index: {1}")]
177 InvalidShredIndex(ShredType, 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, LegacyData, MerkleCode {
214 proof_size: u8,
215 chained: bool,
216 resigned: bool,
217 }, MerkleData {
219 proof_size: u8,
220 chained: bool,
221 resigned: bool,
222 }, }
224
225#[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#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
238struct DataShredHeader {
239 parent_offset: u16,
240 flags: ShredFlags,
241 size: u16, }
243
244#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
246struct CodingShredHeader {
247 num_data_shreds: u16,
248 num_coding_shreds: u16,
249 position: u16, }
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]), 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#[derive(Clone, Copy, Eq, Debug, Hash, PartialEq)]
275pub struct ShredId(Slot, 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, 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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
307pub(crate) struct ErasureSetId(Slot, 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 pub(crate) fn store_key(&self) -> (Slot, u32) {
321 (self.0, self.1)
322 }
323}
324
325macro_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 dispatch!(pub(crate) fn erasure_shard(&self) -> Result<&[u8], Error>);
373 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 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 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 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 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 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 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 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 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 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 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#[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 let merkle_variant = Some((
955 6, true, 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(), rng.gen_range(1..64), is_last_in_slot,
1044 rng.gen_range(0..671), rng.gen_range(0..781), &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, 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 {
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, &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, &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, &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, &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, &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, &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, &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, max_slot,
1286 shred_version,
1287 |_| false, &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, &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, &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, &mut stats
1354 ));
1355 assert_eq!(stats.index_out_of_bounds, 1);
1356 }
1357
1358 {
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, &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, &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, max_slot,
1391 shred_version,
1392 |_| false, &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, &mut stats
1415 ));
1416 assert_eq!(stats.index_out_of_bounds, 1);
1417 }
1418 }
1419
1420 #[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 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 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 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 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, 28685, 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, 23418, 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, 28685, 36390, &data, ShredFlags::LAST_SHRED_IN_SLOT,
1659 37, 45189, 28657, );
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, 21443, 51279, &[], ShredFlags::DATA_COMPLETE_SHRED,
1698 49, 59445, 21414, );
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, 23418, &parity_shard,
1734 21259, 32, 58, 43, 47298, );
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, 0, 0, &[], flags,
1773 reference_tick,
1774 0, 0, )
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 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 #[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, 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 for shred in &shreds {
1908 for other in &shreds {
1909 assert!(!shred.is_shred_duplicate(other));
1910 }
1911 }
1912 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 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}