Skip to content

Commit

Permalink
adds test coverage for Shred::is_shred_duplicate (solana-labs#2723)
Browse files Browse the repository at this point in the history
  • Loading branch information
behzadnouri authored Aug 23, 2024
1 parent 0ad9fe1 commit 09f5f5b
Showing 1 changed file with 95 additions and 0 deletions.
95 changes: 95 additions & 0 deletions ledger/src/shred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2122,4 +2122,99 @@ mod tests {
assert_eq!((flags & ShredFlags::SHRED_TICK_REFERENCE_MASK).bits(), 61u8);
assert_eq!(bincode::serialize(&flags).unwrap(), [0b1011_1101]);
}

#[test_case(false, false)]
#[test_case(false, true)]
#[test_case(true, false)]
#[test_case(true, true)]
fn test_is_shred_duplicate(chained: bool, is_last_in_slot: bool) {
fn fill_retransmitter_signature<R: Rng>(
rng: &mut R,
shred: Shred,
chained: bool,
is_last_in_slot: bool,
) -> Shred {
let mut shred = shred.into_payload();
let mut signature = [0u8; SIGNATURE_BYTES];
rng.fill(&mut signature[..]);
let out = layout::set_retransmitter_signature(&mut shred, &Signature::from(signature));
if chained && is_last_in_slot {
assert_matches!(out, Ok(()));
} else {
assert_matches!(out, Err(Error::InvalidShredVariant));
}
Shred::new_from_serialized_shred(shred).unwrap()
}
let mut rng = rand::thread_rng();
let thread_pool = ThreadPoolBuilder::new().num_threads(2).build().unwrap();
let reed_solomon_cache = ReedSolomonCache::default();
let keypair = Keypair::new();
let chained_merkle_root = chained.then(|| Hash::new_from_array(rng.gen()));
let slot = 285_376_049 + rng.gen_range(0..100_000);
let parent_slot = slot - rng.gen_range(1..=65535);
let shred_version = rng.gen();
let reference_tick = rng.gen_range(1..64);
let next_shred_index = rng.gen_range(0..671);
let next_code_index = rng.gen_range(0..781);
let mut data = vec![0u8; 1200 * 5];
rng.fill(&mut data[..]);
let shreds: Vec<_> = merkle::make_shreds_from_data(
&thread_pool,
&keypair,
chained_merkle_root,
&data[..],
slot,
parent_slot,
shred_version,
reference_tick,
is_last_in_slot,
next_shred_index,
next_code_index,
&reed_solomon_cache,
&mut ProcessShredsStats::default(),
)
.unwrap()
.into_iter()
.flatten()
.map(Shred::from)
.map(|shred| fill_retransmitter_signature(&mut rng, shred, chained, is_last_in_slot))
.collect();
{
let num_data_shreds = shreds.iter().filter(|shred| shred.is_data()).count();
let num_coding_shreds = shreds.iter().filter(|shred| shred.is_code()).count();
assert!(num_data_shreds > if is_last_in_slot { 31 } else { 5 });
assert!(num_coding_shreds > if is_last_in_slot { 31 } else { 20 });
}
// Shreds of different (slot, index, shred-type) are not duplicate.
// A shred is not a duplicate of itself either.
for shred in &shreds {
for other in &shreds {
assert!(!shred.is_shred_duplicate(other));
}
}
// Different retransmitter signature does not make shreds duplicate.
for shred in &shreds {
let other =
fill_retransmitter_signature(&mut rng, shred.clone(), chained, is_last_in_slot);
if chained && is_last_in_slot {
assert_ne!(shred.payload(), other.payload());
}
assert!(!shred.is_shred_duplicate(&other));
assert!(!other.is_shred_duplicate(shred));
}
// Shreds of the same (slot, index, shred-type) with different payload
// (ignoring retransmitter signature) are duplicate.
for shred in &shreds {
let mut other = shred.payload().clone();
other[90] = other[90].wrapping_add(1);
let other = Shred::new_from_serialized_shred(other).unwrap();
assert_ne!(shred.payload(), other.payload());
assert_eq!(
layout::get_retransmitter_signature(shred.payload()).ok(),
layout::get_retransmitter_signature(other.payload()).ok()
);
assert!(shred.is_shred_duplicate(&other));
assert!(other.is_shred_duplicate(shred));
}
}
}

0 comments on commit 09f5f5b

Please sign in to comment.