Skip to content

Commit 091fd01

Browse files
behzadnourijeffwashington
authored andcommitted
removes shred wire layout specs from sigverify (solana-labs#25520)
sigverify_shreds relies on wire layout specs of shreds: https://github.com/solana-labs/solana/blob/0376ab41a/ledger/src/sigverify_shreds.rs#L39-L46 https://github.com/solana-labs/solana/blob/0376ab41a/ledger/src/sigverify_shreds.rs#L298-L305 In preparation of solana-labs#25237 which adds a new shred variant with different layout and signed message, this commit removes shred layout specification from sigverify and instead encapsulate that in shred module.
1 parent 61893ab commit 091fd01

File tree

6 files changed

+198
-208
lines changed

6 files changed

+198
-208
lines changed

core/src/repair_response.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,17 @@ mod test {
9292
.iter()
9393
.cloned()
9494
.collect();
95-
let rv = verify_shred_cpu(&packet, &leader_slots);
96-
assert_eq!(rv, Some(1));
95+
assert!(verify_shred_cpu(&packet, &leader_slots));
9796

9897
let wrong_keypair = Keypair::new();
9998
let leader_slots = [(slot, wrong_keypair.pubkey().to_bytes())]
10099
.iter()
101100
.cloned()
102101
.collect();
103-
let rv = verify_shred_cpu(&packet, &leader_slots);
104-
assert_eq!(rv, Some(0));
102+
assert!(!verify_shred_cpu(&packet, &leader_slots));
105103

106104
let leader_slots = HashMap::new();
107-
let rv = verify_shred_cpu(&packet, &leader_slots);
108-
assert_eq!(rv, None);
105+
assert!(!verify_shred_cpu(&packet, &leader_slots));
109106
}
110107

111108
#[test]

core/src/sigverify_shreds.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use {
77
},
88
crossbeam_channel::Sender,
99
solana_ledger::{
10-
leader_schedule_cache::LeaderScheduleCache, shred::Shred,
11-
sigverify_shreds::verify_shreds_gpu,
10+
leader_schedule_cache::LeaderScheduleCache, shred, sigverify_shreds::verify_shreds_gpu,
1211
},
1312
solana_perf::{self, packet::PacketBatch, recycler_cache::RecyclerCache},
1413
solana_runtime::bank_forks::BankForks,
@@ -43,7 +42,9 @@ impl ShredSigVerifier {
4342
fn read_slots(batches: &[PacketBatch]) -> HashSet<u64> {
4443
batches
4544
.iter()
46-
.flat_map(|batch| batch.iter().filter_map(Shred::get_slot_from_packet))
45+
.flat_map(PacketBatch::iter)
46+
.map(shred::layout::get_shred)
47+
.filter_map(shred::layout::get_slot)
4748
.collect()
4849
}
4950
}

ledger/src/blockstore.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1859,7 +1859,7 @@ impl Blockstore {
18591859
let upper_index = cmp::min(current_index, end_index);
18601860
// the tick that will be used to figure out the timeout for this hole
18611861
let data = db_iterator.value().expect("couldn't read value");
1862-
let reference_tick = u64::from(Shred::reference_tick_from_data(data).unwrap());
1862+
let reference_tick = u64::from(shred::layout::get_reference_tick(data).unwrap());
18631863
if ticks_since_first_insert < reference_tick + MAX_TURBINE_DELAY_IN_TICKS {
18641864
// The higher index holes have not timed out yet
18651865
break 'outer;

ledger/src/shred.rs

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ use {
5656
num_enum::{IntoPrimitive, TryFromPrimitive},
5757
serde::{Deserialize, Serialize},
5858
solana_entry::entry::{create_ticks, Entry},
59-
solana_perf::packet::Packet,
59+
solana_perf::packet::{deserialize_from_with_limit, Packet},
6060
solana_runtime::bank::Bank,
6161
solana_sdk::{
6262
clock::Slot,
@@ -317,7 +317,7 @@ impl Shred {
317317
}
318318

319319
pub fn new_from_serialized_shred(shred: Vec<u8>) -> Result<Self, Error> {
320-
Ok(match Self::shred_type_from_payload(&shred)? {
320+
Ok(match layout::get_shred_type(&shred)? {
321321
ShredType::Code => Self::from(ShredCode::from_payload(shred)?),
322322
ShredType::Data => Self::from(ShredData::from_payload(shred)?),
323323
})
@@ -383,7 +383,7 @@ impl Shred {
383383

384384
// Possibly zero pads bytes stored in blockstore.
385385
pub(crate) fn resize_stored_shred(shred: Vec<u8>) -> Result<Vec<u8>, Error> {
386-
match Self::shred_type_from_payload(&shred)? {
386+
match layout::get_shred_type(&shred)? {
387387
ShredType::Code => ShredCode::resize_stored_shred(shred),
388388
ShredType::Data => ShredData::resize_stored_shred(shred),
389389
}
@@ -441,16 +441,6 @@ impl Shred {
441441
self.common_header().shred_type
442442
}
443443

444-
fn shred_type_from_payload(shred: &[u8]) -> Result<ShredType, Error> {
445-
match shred.get(OFFSET_OF_SHRED_TYPE) {
446-
None => Err(Error::InvalidPayloadSize(shred.len())),
447-
Some(shred_type) => match ShredType::try_from(*shred_type) {
448-
Err(_) => Err(Error::InvalidShredType),
449-
Ok(shred_type) => Ok(shred_type),
450-
},
451-
}
452-
}
453-
454444
pub fn is_data(&self) -> bool {
455445
self.shred_type() == ShredType::Data
456446
}
@@ -488,25 +478,6 @@ impl Shred {
488478
}
489479
}
490480

491-
// Get slot from a shred packet with partial deserialize
492-
pub fn get_slot_from_packet(p: &Packet) -> Option<Slot> {
493-
let slot_start = OFFSET_OF_SHRED_SLOT;
494-
let slot_end = slot_start + SIZE_OF_SHRED_SLOT;
495-
p.deserialize_slice(slot_start..slot_end).ok()
496-
}
497-
498-
pub(crate) fn reference_tick_from_data(data: &[u8]) -> Result<u8, Error> {
499-
const SHRED_FLAGS_OFFSET: usize = SIZE_OF_COMMON_SHRED_HEADER + std::mem::size_of::<u16>();
500-
if Self::shred_type_from_payload(data)? != ShredType::Data {
501-
return Err(Error::InvalidShredType);
502-
}
503-
let flags = match data.get(SHRED_FLAGS_OFFSET) {
504-
None => return Err(Error::InvalidPayloadSize(data.len())),
505-
Some(flags) => flags,
506-
};
507-
Ok(flags & ShredFlags::SHRED_TICK_REFERENCE_MASK.bits())
508-
}
509-
510481
pub fn verify(&self, pubkey: &Pubkey) -> bool {
511482
let message = self.signed_payload();
512483
self.signature().verify(pubkey.as_ref(), message)
@@ -535,6 +506,73 @@ impl Shred {
535506
}
536507
}
537508

509+
// Helper methods to extract pieces of the shred from the payload
510+
// without deserializing the entire payload.
511+
pub mod layout {
512+
use {super::*, std::ops::Range};
513+
514+
fn get_shred_size(packet: &Packet) -> usize {
515+
if packet.meta.repair() {
516+
packet.meta.size.saturating_sub(SIZE_OF_NONCE)
517+
} else {
518+
packet.meta.size
519+
}
520+
}
521+
522+
pub fn get_shred(packet: &Packet) -> &[u8] {
523+
&packet.data()[..get_shred_size(packet)]
524+
}
525+
526+
pub(crate) fn get_signature(shred: &[u8]) -> Option<Signature> {
527+
Some(Signature::new(shred.get(..SIZE_OF_SIGNATURE)?))
528+
}
529+
530+
pub(crate) const fn get_signature_range() -> Range<usize> {
531+
0..SIZE_OF_SIGNATURE
532+
}
533+
534+
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+
}
542+
}
543+
544+
pub fn get_slot(shred: &[u8]) -> Option<Slot> {
545+
deserialize_from_with_limit(shred.get(OFFSET_OF_SHRED_SLOT..)?).ok()
546+
}
547+
548+
pub(super) fn get_index(shred: &[u8]) -> Option<u32> {
549+
deserialize_from_with_limit(shred.get(OFFSET_OF_SHRED_INDEX..)?).ok()
550+
}
551+
552+
// Returns chunk of the payload which is signed.
553+
pub(crate) fn get_signed_message(shred: &[u8]) -> Option<&[u8]> {
554+
shred.get(SIZE_OF_SIGNATURE..)
555+
}
556+
557+
// Returns slice range of the packet payload which is signed.
558+
pub(crate) fn get_signed_message_range(packet: &Packet) -> Range<usize> {
559+
SIZE_OF_SIGNATURE..get_shred_size(packet)
560+
}
561+
562+
pub(crate) fn get_reference_tick(shred: &[u8]) -> Result<u8, Error> {
563+
const SIZE_OF_PARENT_OFFSET: usize = std::mem::size_of::<u16>();
564+
const OFFSET_OF_SHRED_FLAGS: usize = SIZE_OF_COMMON_SHRED_HEADER + SIZE_OF_PARENT_OFFSET;
565+
if get_shred_type(shred)? != ShredType::Data {
566+
return Err(Error::InvalidShredType);
567+
}
568+
let flags = match shred.get(OFFSET_OF_SHRED_FLAGS) {
569+
None => return Err(Error::InvalidPayloadSize(shred.len())),
570+
Some(flags) => flags,
571+
};
572+
Ok(flags & ShredFlags::SHRED_TICK_REFERENCE_MASK.bits())
573+
}
574+
}
575+
538576
impl From<ShredCode> for Shred {
539577
fn from(shred: ShredCode) -> Self {
540578
Self::ShredCode(shred)
@@ -549,50 +587,39 @@ impl From<ShredData> for Shred {
549587

550588
// Get slot, index, and type from a packet with partial deserialize
551589
pub fn get_shred_slot_index_type(
552-
p: &Packet,
590+
packet: &Packet,
553591
stats: &mut ShredFetchStats,
554592
) -> Option<(Slot, u32, ShredType)> {
555-
let index_start = OFFSET_OF_SHRED_INDEX;
556-
let index_end = index_start + SIZE_OF_SHRED_INDEX;
557-
let slot_start = OFFSET_OF_SHRED_SLOT;
558-
let slot_end = slot_start + SIZE_OF_SHRED_SLOT;
559-
560-
debug_assert!(index_end > slot_end);
561-
debug_assert!(index_end > OFFSET_OF_SHRED_TYPE);
562-
563-
if index_end > p.meta.size {
593+
let shred = layout::get_shred(packet);
594+
if OFFSET_OF_SHRED_INDEX + SIZE_OF_SHRED_INDEX > shred.len() {
564595
stats.index_overrun += 1;
565596
return None;
566597
}
567-
568-
let index = match p.deserialize_slice(index_start..index_end) {
569-
Ok(x) => x,
570-
Err(_e) => {
571-
stats.index_bad_deserialize += 1;
598+
let shred_type = match layout::get_shred_type(shred) {
599+
Ok(shred_type) => shred_type,
600+
Err(_) => {
601+
stats.bad_shred_type += 1;
572602
return None;
573603
}
574604
};
575-
576-
if index >= MAX_DATA_SHREDS_PER_SLOT as u32 {
577-
stats.index_out_of_bounds += 1;
578-
return None;
579-
}
580-
581-
let slot = match p.deserialize_slice(slot_start..slot_end) {
582-
Ok(x) => x,
583-
Err(_e) => {
605+
let slot = match layout::get_slot(shred) {
606+
Some(slot) => slot,
607+
None => {
584608
stats.slot_bad_deserialize += 1;
585609
return None;
586610
}
587611
};
588-
589-
let shred_type = match ShredType::try_from(p.data()[OFFSET_OF_SHRED_TYPE]) {
590-
Err(_) => {
591-
stats.bad_shred_type += 1;
612+
let index = match layout::get_index(shred) {
613+
Some(index) => index,
614+
None => {
615+
stats.index_bad_deserialize += 1;
592616
return None;
593617
}
594-
Ok(shred_type) => shred_type,
595618
};
619+
if index >= MAX_DATA_SHREDS_PER_SLOT as u32 {
620+
stats.index_out_of_bounds += 1;
621+
return None;
622+
}
596623
Some((slot, index, shred_type))
597624
}
598625

@@ -924,9 +951,9 @@ mod tests {
924951
assert_eq!(shred, Shred::new_from_serialized_shred(payload).unwrap());
925952
assert_eq!(
926953
shred.reference_tick(),
927-
Shred::reference_tick_from_data(packet.data()).unwrap()
954+
layout::get_reference_tick(packet.data()).unwrap()
928955
);
929-
assert_eq!(Shred::get_slot_from_packet(&packet), Some(shred.slot()));
956+
assert_eq!(layout::get_slot(packet.data()), Some(shred.slot()));
930957
assert_eq!(
931958
get_shred_slot_index_type(&packet, &mut ShredFetchStats::default()),
932959
Some((shred.slot(), shred.index(), shred.shred_type()))
@@ -965,9 +992,9 @@ mod tests {
965992
assert_eq!(shred, Shred::new_from_serialized_shred(payload).unwrap());
966993
assert_eq!(
967994
shred.reference_tick(),
968-
Shred::reference_tick_from_data(packet.data()).unwrap()
995+
layout::get_reference_tick(packet.data()).unwrap()
969996
);
970-
assert_eq!(Shred::get_slot_from_packet(&packet), Some(shred.slot()));
997+
assert_eq!(layout::get_slot(packet.data()), Some(shred.slot()));
971998
assert_eq!(
972999
get_shred_slot_index_type(&packet, &mut ShredFetchStats::default()),
9731000
Some((shred.slot(), shred.index(), shred.shred_type()))
@@ -1011,7 +1038,7 @@ mod tests {
10111038
packet.meta.size = payload.len();
10121039
assert_eq!(shred.bytes_to_store(), payload);
10131040
assert_eq!(shred, Shred::new_from_serialized_shred(payload).unwrap());
1014-
assert_eq!(Shred::get_slot_from_packet(&packet), Some(shred.slot()));
1041+
assert_eq!(layout::get_slot(packet.data()), Some(shred.slot()));
10151042
assert_eq!(
10161043
get_shred_slot_index_type(&packet, &mut ShredFetchStats::default()),
10171044
Some((shred.slot(), shred.index(), shred.shred_type()))
@@ -1050,7 +1077,7 @@ mod tests {
10501077
assert_eq!(shred.last_in_slot(), is_last_in_slot);
10511078
assert_eq!(shred.reference_tick(), reference_tick.min(63u8));
10521079
assert_eq!(
1053-
Shred::reference_tick_from_data(shred.payload()).unwrap(),
1080+
layout::get_reference_tick(shred.payload()).unwrap(),
10541081
reference_tick.min(63u8),
10551082
);
10561083
}

ledger/src/shredder.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@ mod tests {
353353
use {
354354
super::*,
355355
crate::shred::{
356-
max_entries_per_n_shred, max_ticks_per_n_shreds, verify_test_data_shred, ShredType,
356+
self, max_entries_per_n_shred, max_ticks_per_n_shreds, verify_test_data_shred,
357+
ShredType,
357358
},
358359
bincode::serialized_size,
359360
matches::assert_matches,
@@ -519,7 +520,7 @@ mod tests {
519520
);
520521
data_shreds.iter().for_each(|s| {
521522
assert_eq!(s.reference_tick(), 5);
522-
assert_eq!(Shred::reference_tick_from_data(s.payload()).unwrap(), 5);
523+
assert_eq!(shred::layout::get_reference_tick(s.payload()).unwrap(), 5);
523524
});
524525

525526
let deserialized_shred =
@@ -555,7 +556,7 @@ mod tests {
555556
ShredFlags::SHRED_TICK_REFERENCE_MASK.bits()
556557
);
557558
assert_eq!(
558-
Shred::reference_tick_from_data(s.payload()).unwrap(),
559+
shred::layout::get_reference_tick(s.payload()).unwrap(),
559560
ShredFlags::SHRED_TICK_REFERENCE_MASK.bits()
560561
);
561562
});

0 commit comments

Comments
 (0)