Skip to content

Commit 2e2f88b

Browse files
committed
embeds versioning into shred binary
In preparation of solana-labs#25237 which adds a new shred variant with merkle tree branches, the commit embeds versioning into shred binary by encoding a new ShredVariant type at byte 65 of payload replacing previously ShredType at this offset. * 0b0101_1010 indicates a legacy coding shred, which is also equal to ShredType::Code for backward compatibility. * 0b1010_0101 indicates a legacy data shred, which is also equal to ShredType::Data for backward compatibility. Following commits will add merkle variants to this type.
1 parent d4e7ebf commit 2e2f88b

File tree

2 files changed

+115
-27
lines changed

2 files changed

+115
-27
lines changed

ledger/src/shred.rs

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ const SIZE_OF_COMMON_SHRED_HEADER: usize = 83;
9191
const SIZE_OF_DATA_SHRED_HEADER: usize = 5;
9292
const SIZE_OF_CODING_SHRED_HEADER: usize = 6;
9393
const SIZE_OF_SIGNATURE: usize = 64;
94-
const SIZE_OF_SHRED_TYPE: usize = 1;
94+
const SIZE_OF_SHRED_VARIANT: usize = 1;
9595
const SIZE_OF_SHRED_SLOT: usize = 8;
9696
const SIZE_OF_SHRED_INDEX: usize = 4;
9797
pub const SIZE_OF_NONCE: usize = 4;
@@ -108,8 +108,8 @@ pub const SIZE_OF_DATA_SHRED_PAYLOAD: usize = PACKET_DATA_SIZE
108108
const_assert_eq!(SHRED_DATA_OFFSET, 88);
109109
const SHRED_DATA_OFFSET: usize = SIZE_OF_COMMON_SHRED_HEADER + SIZE_OF_DATA_SHRED_HEADER;
110110

111-
const OFFSET_OF_SHRED_TYPE: usize = SIZE_OF_SIGNATURE;
112-
const OFFSET_OF_SHRED_SLOT: usize = SIZE_OF_SIGNATURE + SIZE_OF_SHRED_TYPE;
111+
const OFFSET_OF_SHRED_VARIANT: usize = SIZE_OF_SIGNATURE;
112+
const OFFSET_OF_SHRED_SLOT: usize = SIZE_OF_SIGNATURE + SIZE_OF_SHRED_VARIANT;
113113
const OFFSET_OF_SHRED_INDEX: usize = OFFSET_OF_SHRED_SLOT + SIZE_OF_SHRED_SLOT;
114114
const_assert_eq!(SHRED_PAYLOAD_SIZE, 1228);
115115
const SHRED_PAYLOAD_SIZE: usize = PACKET_DATA_SIZE - SIZE_OF_NONCE;
@@ -151,6 +151,8 @@ pub enum Error {
151151
InvalidShredFlags(u8),
152152
#[error("Invalid shred type")]
153153
InvalidShredType,
154+
#[error("Invalid shred variant")]
155+
InvalidShredVariant,
154156
}
155157

156158
#[repr(u8)]
@@ -174,11 +176,18 @@ pub enum ShredType {
174176
Code = 0b0101_1010,
175177
}
176178

179+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
180+
#[serde(into = "u8", try_from = "u8")]
181+
enum ShredVariant {
182+
LegacyCode,
183+
LegacyData,
184+
}
185+
177186
/// A common header that is present in data and code shred headers
178187
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
179188
struct ShredCommonHeader {
180189
signature: Signature,
181-
shred_type: ShredType,
190+
shred_variant: ShredVariant,
182191
slot: Slot,
183192
index: u32,
184193
version: u16,
@@ -317,9 +326,15 @@ impl Shred {
317326
}
318327

319328
pub fn new_from_serialized_shred(shred: Vec<u8>) -> Result<Self, Error> {
320-
Ok(match layout::get_shred_type(&shred)? {
321-
ShredType::Code => Self::from(ShredCode::from_payload(shred)?),
322-
ShredType::Data => Self::from(ShredData::from_payload(shred)?),
329+
Ok(match layout::get_shred_variant(&shred)? {
330+
ShredVariant::LegacyCode => {
331+
let shred = legacy::ShredCode::from_payload(shred)?;
332+
Self::from(ShredCode::from(shred))
333+
}
334+
ShredVariant::LegacyData => {
335+
let shred = legacy::ShredData::from_payload(shred)?;
336+
Self::from(ShredData::from(shred))
337+
}
323338
})
324339
}
325340

@@ -438,7 +453,7 @@ impl Shred {
438453

439454
#[inline]
440455
pub fn shred_type(&self) -> ShredType {
441-
self.common_header().shred_type
456+
ShredType::from(self.common_header().shred_variant)
442457
}
443458

444459
pub fn is_data(&self) -> bool {
@@ -531,14 +546,17 @@ pub mod layout {
531546
0..SIZE_OF_SIGNATURE
532547
}
533548

549+
pub(super) fn get_shred_variant(shred: &[u8]) -> Result<ShredVariant, Error> {
550+
let shred_variant = match shred.get(OFFSET_OF_SHRED_VARIANT) {
551+
None => return Err(Error::InvalidPayloadSize(shred.len())),
552+
Some(shred_variant) => *shred_variant,
553+
};
554+
ShredVariant::try_from(shred_variant).map_err(|_| Error::InvalidShredVariant)
555+
}
556+
534557
pub(super) fn get_shred_type(shred: &[u8]) -> Result<ShredType, Error> {
535-
match shred.get(OFFSET_OF_SHRED_TYPE) {
536-
None => Err(Error::InvalidPayloadSize(shred.len())),
537-
Some(shred_type) => match ShredType::try_from(*shred_type) {
538-
Err(_) => Err(Error::InvalidShredType),
539-
Ok(shred_type) => Ok(shred_type),
540-
},
541-
}
558+
let shred_variant = get_shred_variant(shred)?;
559+
Ok(ShredType::from(shred_variant))
542560
}
543561

544562
pub fn get_slot(shred: &[u8]) -> Option<Slot> {
@@ -585,6 +603,38 @@ impl From<ShredData> for Shred {
585603
}
586604
}
587605

606+
impl From<ShredVariant> for ShredType {
607+
#[inline]
608+
fn from(shred_variant: ShredVariant) -> Self {
609+
match shred_variant {
610+
ShredVariant::LegacyCode => ShredType::Code,
611+
ShredVariant::LegacyData => ShredType::Data,
612+
}
613+
}
614+
}
615+
616+
impl From<ShredVariant> for u8 {
617+
fn from(shred_variant: ShredVariant) -> u8 {
618+
match shred_variant {
619+
ShredVariant::LegacyCode => u8::from(ShredType::Code),
620+
ShredVariant::LegacyData => u8::from(ShredType::Data),
621+
}
622+
}
623+
}
624+
625+
impl TryFrom<u8> for ShredVariant {
626+
type Error = Error;
627+
fn try_from(shred_variant: u8) -> Result<Self, Self::Error> {
628+
if shred_variant == u8::from(ShredType::Code) {
629+
Ok(ShredVariant::LegacyCode)
630+
} else if shred_variant == u8::from(ShredType::Data) {
631+
Ok(ShredVariant::LegacyData)
632+
} else {
633+
Err(Error::InvalidShredVariant)
634+
}
635+
}
636+
}
637+
588638
// Get slot, index, and type from a packet with partial deserialize
589639
pub fn get_shred_slot_index_type(
590640
packet: &Packet,
@@ -704,7 +754,7 @@ mod tests {
704754
fn test_shred_constants() {
705755
let common_header = ShredCommonHeader {
706756
signature: Signature::default(),
707-
shred_type: ShredType::Code,
757+
shred_variant: ShredVariant::LegacyCode,
708758
slot: Slot::MAX,
709759
index: u32::MAX,
710760
version: u16::MAX,
@@ -745,8 +795,8 @@ mod tests {
745795
bincode::serialized_size(&Signature::default()).unwrap() as usize
746796
);
747797
assert_eq!(
748-
SIZE_OF_SHRED_TYPE,
749-
bincode::serialized_size(&ShredType::Code).unwrap() as usize
798+
SIZE_OF_SHRED_VARIANT,
799+
bincode::serialized_size(&ShredVariant::LegacyCode).unwrap() as usize
750800
);
751801
assert_eq!(
752802
SIZE_OF_SHRED_SLOT,
@@ -814,7 +864,7 @@ mod tests {
814864
assert_eq!(Some((1, 3, ShredType::Data)), ret);
815865
assert_eq!(stats, ShredFetchStats::default());
816866

817-
packet.meta.size = OFFSET_OF_SHRED_TYPE;
867+
packet.meta.size = OFFSET_OF_SHRED_VARIANT;
818868
assert_eq!(None, get_shred_slot_index_type(&packet, &mut stats));
819869
assert_eq!(stats.index_overrun, 1);
820870

@@ -878,7 +928,7 @@ mod tests {
878928
200, // version
879929
);
880930
shred.copy_to_packet(&mut packet);
881-
packet.buffer_mut()[OFFSET_OF_SHRED_TYPE] = u8::MAX;
931+
packet.buffer_mut()[OFFSET_OF_SHRED_VARIANT] = u8::MAX;
882932

883933
assert_eq!(None, get_shred_slot_index_type(&packet, &mut stats));
884934
assert_eq!(1, stats.bad_shred_type);
@@ -894,6 +944,7 @@ mod tests {
894944
assert_matches!(bincode::deserialize::<ShredType>(&[1u8]), Err(_));
895945
// data shred
896946
assert_eq!(ShredType::Data as u8, 0b1010_0101);
947+
assert_eq!(u8::from(ShredType::Data), 0b1010_0101);
897948
assert_eq!(ShredType::try_from(0b1010_0101), Ok(ShredType::Data));
898949
let buf = bincode::serialize(&ShredType::Data).unwrap();
899950
assert_eq!(buf, vec![0b1010_0101]);
@@ -903,6 +954,7 @@ mod tests {
903954
);
904955
// coding shred
905956
assert_eq!(ShredType::Code as u8, 0b0101_1010);
957+
assert_eq!(u8::from(ShredType::Code), 0b0101_1010);
906958
assert_eq!(ShredType::try_from(0b0101_1010), Ok(ShredType::Code));
907959
let buf = bincode::serialize(&ShredType::Code).unwrap();
908960
assert_eq!(buf, vec![0b0101_1010]);
@@ -912,6 +964,42 @@ mod tests {
912964
);
913965
}
914966

967+
#[test]
968+
fn test_shred_variant_compat() {
969+
assert_matches!(ShredVariant::try_from(0u8), Err(_));
970+
assert_matches!(ShredVariant::try_from(1u8), Err(_));
971+
assert_matches!(ShredVariant::try_from(0b0101_0000), Err(_));
972+
assert_matches!(ShredVariant::try_from(0b1010_0000), Err(_));
973+
assert_matches!(bincode::deserialize::<ShredVariant>(&[0b0101_0000]), Err(_));
974+
assert_matches!(bincode::deserialize::<ShredVariant>(&[0b1010_0000]), Err(_));
975+
// Legacy coding shred.
976+
assert_eq!(u8::from(ShredVariant::LegacyCode), 0b0101_1010);
977+
assert_eq!(ShredType::from(ShredVariant::LegacyCode), ShredType::Code);
978+
assert_matches!(
979+
ShredVariant::try_from(0b0101_1010),
980+
Ok(ShredVariant::LegacyCode)
981+
);
982+
let buf = bincode::serialize(&ShredVariant::LegacyCode).unwrap();
983+
assert_eq!(buf, vec![0b0101_1010]);
984+
assert_matches!(
985+
bincode::deserialize::<ShredVariant>(&[0b0101_1010]),
986+
Ok(ShredVariant::LegacyCode)
987+
);
988+
// Legacy data shred.
989+
assert_eq!(u8::from(ShredVariant::LegacyData), 0b1010_0101);
990+
assert_eq!(ShredType::from(ShredVariant::LegacyData), ShredType::Data);
991+
assert_matches!(
992+
ShredVariant::try_from(0b1010_0101),
993+
Ok(ShredVariant::LegacyData)
994+
);
995+
let buf = bincode::serialize(&ShredVariant::LegacyData).unwrap();
996+
assert_eq!(buf, vec![0b1010_0101]);
997+
assert_matches!(
998+
bincode::deserialize::<ShredVariant>(&[0b1010_0101]),
999+
Ok(ShredVariant::LegacyData)
1000+
);
1001+
}
1002+
9151003
#[test]
9161004
fn test_serde_compat_shred_data() {
9171005
const SEED: &str = "6qG9NGWEtoTugS4Zgs46u8zTccEJuRHtrNMiUayLHCxt";

ledger/src/shred/legacy.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use {
22
crate::shred::{
33
traits::{Shred, ShredCode as _, ShredData as _},
4-
CodingShredHeader, DataShredHeader, Error, ShredCommonHeader, ShredFlags, ShredType,
4+
CodingShredHeader, DataShredHeader, Error, ShredCommonHeader, ShredFlags, ShredVariant,
55
MAX_DATA_SHREDS_PER_FEC_BLOCK, MAX_DATA_SHREDS_PER_SLOT, SHRED_DATA_OFFSET,
66
SHRED_PAYLOAD_SIZE, SIZE_OF_CODING_SHRED_HEADERS, SIZE_OF_COMMON_SHRED_HEADER,
77
SIZE_OF_DATA_SHRED_HEADER, SIZE_OF_DATA_SHRED_PAYLOAD, SIZE_OF_SIGNATURE,
@@ -78,8 +78,8 @@ impl Shred for ShredData {
7878
fn from_payload(mut payload: Vec<u8>) -> Result<Self, Error> {
7979
let mut cursor = Cursor::new(&payload[..]);
8080
let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?;
81-
if common_header.shred_type != ShredType::Data {
82-
return Err(Error::InvalidShredType);
81+
if common_header.shred_variant != ShredVariant::LegacyData {
82+
return Err(Error::InvalidShredVariant);
8383
}
8484
let data_header = deserialize_from_with_limit(&mut cursor)?;
8585
// see: https://github.com/solana-labs/solana/pull/16602
@@ -163,8 +163,8 @@ impl Shred for ShredCode {
163163
fn from_payload(mut payload: Vec<u8>) -> Result<Self, Error> {
164164
let mut cursor = Cursor::new(&payload[..]);
165165
let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?;
166-
if common_header.shred_type != ShredType::Code {
167-
return Err(Error::InvalidShredType);
166+
if common_header.shred_variant != ShredVariant::LegacyCode {
167+
return Err(Error::InvalidShredVariant);
168168
}
169169
let coding_header = deserialize_from_with_limit(&mut cursor)?;
170170
// see: https://github.com/solana-labs/solana/pull/10109
@@ -295,7 +295,7 @@ impl ShredData {
295295
let mut payload = vec![0; SHRED_PAYLOAD_SIZE];
296296
let common_header = ShredCommonHeader {
297297
signature: Signature::default(),
298-
shred_type: ShredType::Data,
298+
shred_variant: ShredVariant::LegacyData,
299299
slot,
300300
index,
301301
version,
@@ -343,7 +343,7 @@ impl ShredCode {
343343
) -> Self {
344344
let common_header = ShredCommonHeader {
345345
signature: Signature::default(),
346-
shred_type: ShredType::Code,
346+
shred_variant: ShredVariant::LegacyCode,
347347
index,
348348
slot,
349349
version,

0 commit comments

Comments
 (0)