|
|
|
@ -14,7 +14,7 @@ use std::{cmp, io};
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone, Deserialize, PartialEq, Debug)]
|
|
|
|
|
pub enum Shred {
|
|
|
|
|
FirstInSlot(FirstDataShred),
|
|
|
|
|
FirstInSlot(DataShred),
|
|
|
|
|
FirstInFECSet(DataShred),
|
|
|
|
|
Data(DataShred),
|
|
|
|
|
LastInFECSet(DataShred),
|
|
|
|
@ -25,8 +25,8 @@ pub enum Shred {
|
|
|
|
|
impl Shred {
|
|
|
|
|
pub fn slot(&self) -> u64 {
|
|
|
|
|
match self {
|
|
|
|
|
Shred::FirstInSlot(s) => s.header.data_header.common_header.slot,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.header.common_header.slot,
|
|
|
|
@ -36,30 +36,36 @@ impl Shred {
|
|
|
|
|
|
|
|
|
|
pub fn parent(&self) -> u64 {
|
|
|
|
|
match self {
|
|
|
|
|
Shred::FirstInSlot(s) => s.header.data_header.parent,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.header.parent,
|
|
|
|
|
| Shred::LastInSlot(s) => {
|
|
|
|
|
s.header.common_header.slot - u64::from(s.header.parent_offset)
|
|
|
|
|
}
|
|
|
|
|
Shred::Coding(_) => std::u64::MAX,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_slot(&mut self, slot: u64) {
|
|
|
|
|
let parent = self.parent();
|
|
|
|
|
match self {
|
|
|
|
|
Shred::FirstInSlot(s) => s.header.data_header.common_header.slot = slot,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.header.common_header.slot = slot,
|
|
|
|
|
| Shred::LastInSlot(s) => {
|
|
|
|
|
s.header.common_header.slot = slot;
|
|
|
|
|
s.header.parent_offset = (slot - parent) as u16;
|
|
|
|
|
}
|
|
|
|
|
Shred::Coding(s) => s.header.common_header.slot = slot,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn index(&self) -> u32 {
|
|
|
|
|
match self {
|
|
|
|
|
Shred::FirstInSlot(s) => s.header.data_header.common_header.index,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.header.common_header.index,
|
|
|
|
@ -69,8 +75,8 @@ impl Shred {
|
|
|
|
|
|
|
|
|
|
pub fn set_index(&mut self, index: u32) {
|
|
|
|
|
match self {
|
|
|
|
|
Shred::FirstInSlot(s) => s.header.data_header.common_header.index = index,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.header.common_header.index = index,
|
|
|
|
@ -80,8 +86,8 @@ impl Shred {
|
|
|
|
|
|
|
|
|
|
pub fn signature(&self) -> Signature {
|
|
|
|
|
match self {
|
|
|
|
|
Shred::FirstInSlot(s) => s.header.data_header.common_header.signature,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.header.common_header.signature,
|
|
|
|
@ -93,8 +99,8 @@ impl Shred {
|
|
|
|
|
let mut seed = [0; 32];
|
|
|
|
|
let seed_len = seed.len();
|
|
|
|
|
let sig = match self {
|
|
|
|
|
Shred::FirstInSlot(s) => &s.header.data_header.common_header.signature,
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => &s.header.common_header.signature,
|
|
|
|
@ -142,17 +148,10 @@ pub struct ShredCommonHeader {
|
|
|
|
|
pub struct DataShredHeader {
|
|
|
|
|
_reserved: CodingShredHeader,
|
|
|
|
|
pub common_header: ShredCommonHeader,
|
|
|
|
|
pub parent: u64,
|
|
|
|
|
pub parent_offset: u16,
|
|
|
|
|
pub last_in_slot: u8,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The first data shred also has parent slot value in it
|
|
|
|
|
#[derive(Serialize, Clone, Deserialize, Default, PartialEq, Debug)]
|
|
|
|
|
pub struct FirstDataShredHeader {
|
|
|
|
|
pub data_header: DataShredHeader,
|
|
|
|
|
pub parent: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The coding shred header has FEC information
|
|
|
|
|
#[derive(Serialize, Clone, Deserialize, Default, PartialEq, Debug)]
|
|
|
|
|
pub struct CodingShredHeader {
|
|
|
|
@ -163,12 +162,6 @@ pub struct CodingShredHeader {
|
|
|
|
|
pub payload: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone, Deserialize, PartialEq, Debug)]
|
|
|
|
|
pub struct FirstDataShred {
|
|
|
|
|
pub header: FirstDataShredHeader,
|
|
|
|
|
pub payload: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone, Deserialize, PartialEq, Debug)]
|
|
|
|
|
pub struct DataShred {
|
|
|
|
|
pub header: DataShredHeader,
|
|
|
|
@ -180,18 +173,6 @@ pub struct CodingShred {
|
|
|
|
|
pub header: CodingShredHeader,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Default shred is sized correctly to meet MTU/Packet size requirements
|
|
|
|
|
impl Default for FirstDataShred {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
let size = PACKET_DATA_SIZE
|
|
|
|
|
- serialized_size(&Shred::FirstInSlot(Self::empty_shred())).unwrap() as usize;
|
|
|
|
|
FirstDataShred {
|
|
|
|
|
header: FirstDataShredHeader::default(),
|
|
|
|
|
payload: vec![0; size],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Default shred is sized correctly to meet MTU/Packet size requirements
|
|
|
|
|
impl Default for DataShred {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
@ -231,30 +212,6 @@ pub trait ShredCommon {
|
|
|
|
|
fn empty_shred() -> Self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ShredCommon for FirstDataShred {
|
|
|
|
|
fn write_at(&mut self, offset: usize, buf: &[u8]) -> (usize, usize) {
|
|
|
|
|
let mut capacity = self.payload.len().saturating_sub(offset);
|
|
|
|
|
let slice_len = cmp::min(capacity, buf.len());
|
|
|
|
|
capacity -= slice_len;
|
|
|
|
|
if slice_len > 0 {
|
|
|
|
|
self.payload[offset..offset + slice_len].copy_from_slice(&buf[..slice_len]);
|
|
|
|
|
}
|
|
|
|
|
(slice_len, capacity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn overhead() -> usize {
|
|
|
|
|
(bincode::serialized_size(&Shred::FirstInSlot(Self::empty_shred())).unwrap()
|
|
|
|
|
- bincode::serialized_size(&vec![0u8; 0]).unwrap()) as usize
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn empty_shred() -> Self {
|
|
|
|
|
FirstDataShred {
|
|
|
|
|
header: FirstDataShredHeader::default(),
|
|
|
|
|
payload: vec![],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ShredCommon for DataShred {
|
|
|
|
|
fn write_at(&mut self, offset: usize, buf: &[u8]) -> (usize, usize) {
|
|
|
|
|
let mut capacity = self.payload.len().saturating_sub(offset);
|
|
|
|
@ -305,8 +262,7 @@ impl ShredCommon for CodingShred {
|
|
|
|
|
pub struct Shredder {
|
|
|
|
|
slot: u64,
|
|
|
|
|
pub index: u32,
|
|
|
|
|
pub parent: Option<u64>,
|
|
|
|
|
parent_slot: u64,
|
|
|
|
|
parent_offset: u16,
|
|
|
|
|
fec_rate: f32,
|
|
|
|
|
signer: Arc<Keypair>,
|
|
|
|
|
pub shreds: Vec<Vec<u8>>,
|
|
|
|
@ -320,32 +276,20 @@ impl Write for Shredder {
|
|
|
|
|
.active_shred
|
|
|
|
|
.take()
|
|
|
|
|
.or_else(|| {
|
|
|
|
|
Some(
|
|
|
|
|
self.parent
|
|
|
|
|
.take()
|
|
|
|
|
.map(|parent| {
|
|
|
|
|
self.parent_slot = parent;
|
|
|
|
|
// If parent slot is available
|
|
|
|
|
if self.index == 0 {
|
|
|
|
|
Some(if self.index == 0 {
|
|
|
|
|
// If index is 0, it's the first shred in slot
|
|
|
|
|
Shred::FirstInSlot(self.new_first_shred(parent))
|
|
|
|
|
Shred::FirstInSlot(self.new_data_shred())
|
|
|
|
|
} else {
|
|
|
|
|
// Or, it is the first shred in FEC set
|
|
|
|
|
// Else, it is the first shred in FEC set
|
|
|
|
|
Shred::FirstInFECSet(self.new_data_shred())
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else(||
|
|
|
|
|
// If parent slot is not provided, and since there's no existing shred,
|
|
|
|
|
// assume it's first shred in FEC block
|
|
|
|
|
Shred::FirstInFECSet(self.new_data_shred())),
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let written = self.active_offset;
|
|
|
|
|
let (slice_len, capacity) = match current_shred.borrow_mut() {
|
|
|
|
|
Shred::FirstInSlot(s) => s.write_at(written, buf),
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
Shred::FirstInSlot(s)
|
|
|
|
|
| Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => s.write_at(written, buf),
|
|
|
|
@ -389,7 +333,7 @@ pub struct DeshredResult {
|
|
|
|
|
impl Shredder {
|
|
|
|
|
pub fn new(
|
|
|
|
|
slot: u64,
|
|
|
|
|
parent: Option<u64>,
|
|
|
|
|
parent: u64,
|
|
|
|
|
fec_rate: f32,
|
|
|
|
|
signer: &Arc<Keypair>,
|
|
|
|
|
index: u32,
|
|
|
|
@ -402,12 +346,19 @@ impl Shredder {
|
|
|
|
|
fec_rate
|
|
|
|
|
),
|
|
|
|
|
)))
|
|
|
|
|
} else if slot < parent || slot - parent > u64::from(std::u16::MAX) {
|
|
|
|
|
Err(Error::IO(IOError::new(
|
|
|
|
|
ErrorKind::Other,
|
|
|
|
|
format!(
|
|
|
|
|
"Current slot {:?} must be > Parent slot {:?}, but the difference must not be > {:?}",
|
|
|
|
|
slot, parent, std::u16::MAX
|
|
|
|
|
),
|
|
|
|
|
)))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(Shredder {
|
|
|
|
|
slot,
|
|
|
|
|
index,
|
|
|
|
|
parent,
|
|
|
|
|
parent_slot: 0,
|
|
|
|
|
parent_offset: (slot - parent) as u16,
|
|
|
|
|
fec_rate,
|
|
|
|
|
signer: signer.clone(),
|
|
|
|
|
..Shredder::default()
|
|
|
|
@ -440,20 +391,10 @@ impl Shredder {
|
|
|
|
|
let mut data_shred = DataShred::default();
|
|
|
|
|
data_shred.header.common_header.slot = self.slot;
|
|
|
|
|
data_shred.header.common_header.index = self.index;
|
|
|
|
|
data_shred.header.parent = self.parent_slot;
|
|
|
|
|
data_shred.header.parent_offset = self.parent_offset;
|
|
|
|
|
data_shred
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a new data shred that's also first in the slot
|
|
|
|
|
fn new_first_shred(&self, parent: u64) -> FirstDataShred {
|
|
|
|
|
let mut first_shred = FirstDataShred::default();
|
|
|
|
|
first_shred.header.parent = parent;
|
|
|
|
|
first_shred.header.data_header.parent = parent;
|
|
|
|
|
first_shred.header.data_header.common_header.slot = self.slot;
|
|
|
|
|
first_shred.header.data_header.common_header.index = self.index;
|
|
|
|
|
first_shred
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn new_coding_shred(
|
|
|
|
|
slot: u64,
|
|
|
|
|
index: u32,
|
|
|
|
@ -568,26 +509,24 @@ impl Shredder {
|
|
|
|
|
first_index: usize,
|
|
|
|
|
expected_index: usize,
|
|
|
|
|
present: &mut [bool],
|
|
|
|
|
) -> (Vec<Vec<u8>>, bool, usize) {
|
|
|
|
|
let (index, mut first_shred_in_slot) = Self::get_shred_index(shred, num_data);
|
|
|
|
|
) -> (Vec<Vec<u8>>, usize) {
|
|
|
|
|
let index = Self::get_shred_index(shred, num_data);
|
|
|
|
|
|
|
|
|
|
// The index of current shred must be within the range of shreds that are being
|
|
|
|
|
// recovered
|
|
|
|
|
if !(first_index..first_index + num_data + num_coding).contains(&index) {
|
|
|
|
|
return (vec![], false, index);
|
|
|
|
|
return (vec![], index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut missing_blocks: Vec<Vec<u8>> = (expected_index..index)
|
|
|
|
|
.map(|missing| {
|
|
|
|
|
present[missing.saturating_sub(first_index)] = false;
|
|
|
|
|
// If index 0 shred is missing, then first shred in slot will also be recovered
|
|
|
|
|
first_shred_in_slot |= missing == 0;
|
|
|
|
|
Shredder::new_empty_missing_shred(num_data, num_coding, slot, first_index, missing)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let shred_buf = bincode::serialize(shred).unwrap();
|
|
|
|
|
missing_blocks.push(shred_buf);
|
|
|
|
|
(missing_blocks, first_shred_in_slot, index)
|
|
|
|
|
(missing_blocks, index)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn new_empty_missing_shred(
|
|
|
|
@ -598,9 +537,9 @@ impl Shredder {
|
|
|
|
|
missing: usize,
|
|
|
|
|
) -> Vec<u8> {
|
|
|
|
|
let missing_shred = if missing == 0 {
|
|
|
|
|
let mut data_shred = FirstDataShred::default();
|
|
|
|
|
data_shred.header.data_header.common_header.slot = slot;
|
|
|
|
|
data_shred.header.data_header.common_header.index = 0;
|
|
|
|
|
let mut data_shred = DataShred::default();
|
|
|
|
|
data_shred.header.common_header.slot = slot;
|
|
|
|
|
data_shred.header.common_header.index = missing as u32;
|
|
|
|
|
Shred::FirstInSlot(data_shred)
|
|
|
|
|
} else if missing < first_index + num_data {
|
|
|
|
|
let mut data_shred = DataShred::default();
|
|
|
|
@ -642,7 +581,7 @@ impl Shredder {
|
|
|
|
|
let mut shred_bufs: Vec<Vec<u8>> = shreds
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|shred| {
|
|
|
|
|
let (blocks, _first_shred, last_index) = Self::fill_in_missing_shreds(
|
|
|
|
|
let (blocks, last_index) = Self::fill_in_missing_shreds(
|
|
|
|
|
shred,
|
|
|
|
|
num_data,
|
|
|
|
|
num_coding,
|
|
|
|
@ -736,9 +675,8 @@ impl Shredder {
|
|
|
|
|
/// (lower to higher index, and data shreds before coding shreds)
|
|
|
|
|
pub fn deshred(shreds: &[Shred]) -> Result<Vec<u8>, reed_solomon_erasure::Error> {
|
|
|
|
|
let num_data = shreds.len();
|
|
|
|
|
let (data_shred_bufs, first_shred) = {
|
|
|
|
|
let (first_index, first_shred_in_slot) =
|
|
|
|
|
Shredder::get_shred_index(shreds.first().unwrap(), num_data);
|
|
|
|
|
let data_shred_bufs = {
|
|
|
|
|
let first_index = Shredder::get_shred_index(shreds.first().unwrap(), num_data);
|
|
|
|
|
|
|
|
|
|
let last_index = match shreds.last().unwrap() {
|
|
|
|
|
Shred::LastInFECSet(s) | Shred::LastInSlot(s) => {
|
|
|
|
@ -755,43 +693,26 @@ impl Shredder {
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|shred| bincode::serialize(shred).unwrap())
|
|
|
|
|
.collect();
|
|
|
|
|
(shred_bufs, first_shred_in_slot)
|
|
|
|
|
shred_bufs
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(Self::reassemble_payload(
|
|
|
|
|
num_data,
|
|
|
|
|
data_shred_bufs,
|
|
|
|
|
first_shred,
|
|
|
|
|
))
|
|
|
|
|
Ok(Self::reassemble_payload(num_data, data_shred_bufs))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_shred_index(shred: &Shred, num_data: usize) -> (usize, bool) {
|
|
|
|
|
let (first_index, first_shred_in_slot) = match shred {
|
|
|
|
|
Shred::FirstInSlot(s) => (s.header.data_header.common_header.index as usize, true),
|
|
|
|
|
Shred::FirstInFECSet(s)
|
|
|
|
|
| Shred::Data(s)
|
|
|
|
|
| Shred::LastInFECSet(s)
|
|
|
|
|
| Shred::LastInSlot(s) => (s.header.common_header.index as usize, false),
|
|
|
|
|
Shred::Coding(s) => (s.header.common_header.index as usize + num_data, false),
|
|
|
|
|
};
|
|
|
|
|
(first_index, first_shred_in_slot)
|
|
|
|
|
fn get_shred_index(shred: &Shred, num_data: usize) -> usize {
|
|
|
|
|
if let Shred::Coding(_) = shred {
|
|
|
|
|
shred.index() as usize + num_data
|
|
|
|
|
} else {
|
|
|
|
|
shred.index() as usize
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn reassemble_payload(
|
|
|
|
|
num_data: usize,
|
|
|
|
|
data_shred_bufs: Vec<Vec<u8>>,
|
|
|
|
|
first_shred: bool,
|
|
|
|
|
) -> Vec<u8> {
|
|
|
|
|
fn reassemble_payload(num_data: usize, data_shred_bufs: Vec<Vec<u8>>) -> Vec<u8> {
|
|
|
|
|
data_shred_bufs[..num_data]
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.flat_map(|(i, data)| {
|
|
|
|
|
let offset = if i == 0 && first_shred {
|
|
|
|
|
bincode::serialized_size(&Shred::FirstInSlot(FirstDataShred::empty_shred()))
|
|
|
|
|
.unwrap()
|
|
|
|
|
} else {
|
|
|
|
|
bincode::serialized_size(&Shred::Data(DataShred::empty_shred())).unwrap()
|
|
|
|
|
};
|
|
|
|
|
.flat_map(|data| {
|
|
|
|
|
let offset =
|
|
|
|
|
bincode::serialized_size(&Shred::Data(DataShred::empty_shred())).unwrap();
|
|
|
|
|
data[offset as usize..].iter()
|
|
|
|
|
})
|
|
|
|
|
.cloned()
|
|
|
|
@ -807,14 +728,22 @@ mod tests {
|
|
|
|
|
fn test_data_shredder() {
|
|
|
|
|
let keypair = Arc::new(Keypair::new());
|
|
|
|
|
let slot = 0x123456789abcdef0;
|
|
|
|
|
|
|
|
|
|
// Test that parent cannot be > current slot
|
|
|
|
|
assert_matches!(Shredder::new(slot, slot + 1, 1.001, &keypair, 0), Err(_));
|
|
|
|
|
// Test that slot - parent cannot be > u16 MAX
|
|
|
|
|
assert_matches!(
|
|
|
|
|
Shredder::new(slot, slot - 1 - 0xffff, 1.001, &keypair, 0),
|
|
|
|
|
Err(_)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let mut shredder =
|
|
|
|
|
Shredder::new(slot, Some(5), 0.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
Shredder::new(slot, slot - 5, 0.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
assert!(shredder.shreds.is_empty());
|
|
|
|
|
assert_eq!(shredder.active_shred, None);
|
|
|
|
|
assert_eq!(shredder.active_offset, 0);
|
|
|
|
|
|
|
|
|
|
assert!(FirstDataShred::overhead() < PACKET_DATA_SIZE);
|
|
|
|
|
assert!(DataShred::overhead() < PACKET_DATA_SIZE);
|
|
|
|
|
assert!(CodingShred::overhead() < PACKET_DATA_SIZE);
|
|
|
|
|
|
|
|
|
@ -854,6 +783,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::FirstInSlot(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 0);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
let seed0 = deserialized_shred.seed();
|
|
|
|
|
// Test that same seed is generated for a given shred
|
|
|
|
@ -880,6 +810,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 1);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
// Test that same seed is NOT generated for two different shreds
|
|
|
|
|
assert_ne!(seed0, deserialized_shred.seed());
|
|
|
|
@ -902,6 +833,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::FirstInFECSet(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 2);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
// Test8: Write more data to generate an intermediate data shred
|
|
|
|
@ -919,6 +851,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 3);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
// Test9: Write some data to shredder
|
|
|
|
@ -939,6 +872,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::LastInSlot(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 4);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -948,7 +882,7 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
let slot = 0x123456789abcdef0;
|
|
|
|
|
let mut shredder =
|
|
|
|
|
Shredder::new(slot, Some(5), 0.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
Shredder::new(slot, slot - 5, 0.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
assert!(shredder.shreds.is_empty());
|
|
|
|
|
assert_eq!(shredder.active_shred, None);
|
|
|
|
@ -972,6 +906,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::FirstInSlot(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 0);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
let shred = shredder.shreds.remove(0);
|
|
|
|
@ -980,10 +915,10 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 1);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
// Try shredder when no parent is provided
|
|
|
|
|
let mut shredder = Shredder::new(0x123456789abcdef0, None, 0.0, &keypair, 2)
|
|
|
|
|
let mut shredder = Shredder::new(0x123456789abcdef0, slot - 5, 0.0, &keypair, 2)
|
|
|
|
|
.expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
assert!(shredder.shreds.is_empty());
|
|
|
|
@ -1007,6 +942,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 2);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1016,9 +952,9 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
let slot = 0x123456789abcdef0;
|
|
|
|
|
// Test that FEC rate cannot be > 1.0
|
|
|
|
|
assert_matches!(Shredder::new(slot, Some(5), 1.001, &keypair, 0), Err(_));
|
|
|
|
|
assert_matches!(Shredder::new(slot, slot - 5, 1.001, &keypair, 0), Err(_));
|
|
|
|
|
|
|
|
|
|
let mut shredder = Shredder::new(0x123456789abcdef0, Some(5), 1.0, &keypair, 0)
|
|
|
|
|
let mut shredder = Shredder::new(0x123456789abcdef0, slot - 5, 1.0, &keypair, 0)
|
|
|
|
|
.expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
assert!(shredder.shreds.is_empty());
|
|
|
|
@ -1044,6 +980,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::FirstInSlot(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 0);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
let shred = shredder.shreds.remove(0);
|
|
|
|
@ -1052,6 +989,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 1);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
let shred = shredder.shreds.remove(0);
|
|
|
|
@ -1060,6 +998,7 @@ mod tests {
|
|
|
|
|
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
|
|
|
|
assert_eq!(deserialized_shred.index(), 2);
|
|
|
|
|
assert_eq!(deserialized_shred.slot(), slot);
|
|
|
|
|
assert_eq!(deserialized_shred.parent(), slot - 5);
|
|
|
|
|
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
|
|
|
|
|
|
|
|
|
let shred = shredder.shreds.remove(0);
|
|
|
|
@ -1092,7 +1031,7 @@ mod tests {
|
|
|
|
|
let keypair = Arc::new(Keypair::new());
|
|
|
|
|
let slot = 0x123456789abcdef0;
|
|
|
|
|
let mut shredder =
|
|
|
|
|
Shredder::new(slot, Some(5), 1.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
Shredder::new(slot, slot - 5, 1.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
assert!(shredder.shreds.is_empty());
|
|
|
|
|
assert_eq!(shredder.active_shred, None);
|
|
|
|
@ -1183,6 +1122,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 1);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(1, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1190,6 +1130,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 3);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(3, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1252,6 +1193,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::FirstInSlot(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 0);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(0, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1259,6 +1201,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 2);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(2, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1266,6 +1209,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::LastInFECSet(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 4);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(4, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1293,7 +1237,7 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
// Test4: Try recovery/reassembly full slot with 3 missing data shreds + 2 coding shreds. Hint: should work
|
|
|
|
|
let mut shredder =
|
|
|
|
|
Shredder::new(slot, Some(5), 1.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
Shredder::new(slot, slot - 5, 1.0, &keypair, 0).expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
let mut offset = shredder.write(&data).unwrap();
|
|
|
|
|
let approx_shred_payload_size = offset;
|
|
|
|
@ -1343,6 +1287,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::FirstInSlot(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 0);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(0, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1350,6 +1295,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 2);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(2, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1357,6 +1303,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::LastInSlot(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 4);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(4, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1404,7 +1351,7 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
// Test6: Try recovery/reassembly with non zero index full slot with 3 missing data shreds + 2 coding shreds. Hint: should work
|
|
|
|
|
let mut shredder =
|
|
|
|
|
Shredder::new(slot, Some(5), 1.0, &keypair, 25).expect("Failed in creating shredder");
|
|
|
|
|
Shredder::new(slot, slot - 5, 1.0, &keypair, 25).expect("Failed in creating shredder");
|
|
|
|
|
|
|
|
|
|
let mut offset = shredder.write(&data).unwrap();
|
|
|
|
|
let approx_shred_payload_size = offset;
|
|
|
|
@ -1454,6 +1401,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 25);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(0, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1461,6 +1409,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::Data(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 27);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(2, recovered_shred);
|
|
|
|
|
|
|
|
|
@ -1468,6 +1417,7 @@ mod tests {
|
|
|
|
|
assert_matches!(recovered_shred, Shred::LastInSlot(_));
|
|
|
|
|
assert_eq!(recovered_shred.index(), 29);
|
|
|
|
|
assert_eq!(recovered_shred.slot(), slot);
|
|
|
|
|
assert_eq!(recovered_shred.parent(), slot - 5);
|
|
|
|
|
assert!(recovered_shred.verify(&keypair.pubkey()));
|
|
|
|
|
shreds.insert(4, recovered_shred);
|
|
|
|
|
|
|
|
|
|