Skip to content

Commit 92ca725

Browse files
authored
patches shred::merkle::make_shreds_from_data when data is empty (solana-labs#29295)
If data is empty, make_shreds_from_data will now return one data shred with empty data. This preserves invariants verified in tests regardless of data size.
1 parent 426be3d commit 92ca725

File tree

3 files changed

+19
-13
lines changed

3 files changed

+19
-13
lines changed

ledger/src/shred.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ mod stats;
8888
mod traits;
8989

9090
pub type Nonce = u32;
91-
pub const SIZE_OF_NONCE: usize = 4;
91+
const_assert_eq!(SIZE_OF_NONCE, 4);
92+
pub const SIZE_OF_NONCE: usize = std::mem::size_of::<Nonce>();
9293

9394
/// The following constants are computed by hand, and hardcoded.
9495
/// `test_shred_constants` ensures that the values are correct.

ledger/src/shred/merkle.rs

+16-11
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use {
1616
shredder::{self, ReedSolomonCache},
1717
},
1818
assert_matches::debug_assert_matches,
19-
itertools::Itertools,
19+
itertools::{Either, Itertools},
2020
rayon::{prelude::*, ThreadPool},
2121
reed_solomon_erasure::Error::{InvalidIndex, TooFewParityShards, TooFewShards},
2222
solana_perf::packet::deserialize_from_with_limit,
@@ -875,21 +875,30 @@ pub(super) fn make_shreds_from_data(
875875
}
876876
data = rest;
877877
}
878-
if !data.is_empty() {
878+
// If shreds.is_empty() then the data argument was empty. In that case we
879+
// want to generate one data shred with empty data.
880+
if !data.is_empty() || shreds.is_empty() {
879881
// Find the Merkle proof_size and data_buffer_size
880882
// which can embed the remaining data.
881883
let (proof_size, data_buffer_size) = (1u8..32)
882884
.find_map(|proof_size| {
883885
let data_buffer_size = ShredData::capacity(proof_size).ok()?;
884886
let num_data_shreds = (data.len() + data_buffer_size - 1) / data_buffer_size;
887+
let num_data_shreds = num_data_shreds.max(1);
885888
let erasure_batch_size = shredder::get_erasure_batch_size(num_data_shreds);
886889
(proof_size == get_proof_size(erasure_batch_size))
887890
.then_some((proof_size, data_buffer_size))
888891
})
889892
.ok_or(Error::UnknownProofSize)?;
890893
common_header.shred_variant = ShredVariant::MerkleData(proof_size);
891894
common_header.fec_set_index = common_header.index;
892-
for shred in data.chunks(data_buffer_size) {
895+
let chunks = if data.is_empty() {
896+
// Generate one data shred with empty data.
897+
Either::Left(std::iter::once(data))
898+
} else {
899+
Either::Right(data.chunks(data_buffer_size))
900+
};
901+
for shred in chunks {
893902
let shred = new_shred_data(common_header, data_header, shred);
894903
shreds.push(shred);
895904
common_header.index += 1;
@@ -1274,12 +1283,8 @@ mod test {
12741283
assert_eq!(shreds.iter().map(Shred::signature).dedup().count(), 1);
12751284
for size in num_data_shreds..num_shreds {
12761285
let mut shreds = shreds.clone();
1277-
let mut removed_shreds = Vec::new();
1278-
while shreds.len() > size {
1279-
let index = rng.gen_range(0, shreds.len());
1280-
removed_shreds.push(shreds.swap_remove(index));
1281-
}
12821286
shreds.shuffle(rng);
1287+
let mut removed_shreds = shreds.split_off(size);
12831288
// Should at least contain one coding shred.
12841289
if shreds.iter().all(|shred| {
12851290
matches!(
@@ -1337,9 +1342,9 @@ mod test {
13371342
#[test_case(46800)]
13381343
fn test_make_shreds_from_data(data_size: usize) {
13391344
let mut rng = rand::thread_rng();
1340-
let data_size = data_size.saturating_sub(16).max(1);
1345+
let data_size = data_size.saturating_sub(16);
13411346
let reed_solomon_cache = ReedSolomonCache::default();
1342-
for data_size in (data_size..data_size + 32).step_by(3) {
1347+
for data_size in data_size..data_size + 32 {
13431348
run_make_shreds_from_data(&mut rng, data_size, &reed_solomon_cache);
13441349
}
13451350
}
@@ -1392,7 +1397,7 @@ mod test {
13921397
Shred::ShredData(shred) => Some(shred),
13931398
})
13941399
.collect();
1395-
// Assert that the input data can be recovered from data sherds.
1400+
// Assert that the input data can be recovered from data shreds.
13961401
assert_eq!(
13971402
data,
13981403
data_shreds

ledger/src/sigverify_shreds.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ pub fn sign_shreds_cpu(keypair: &Keypair, batches: &mut [PacketBatch]) {
295295
.for_each(|p| sign_shred_cpu(keypair, p));
296296
});
297297
});
298-
inc_new_counter_debug!("ed25519_shred_verify_cpu", packet_count);
298+
inc_new_counter_debug!("ed25519_shred_sign_cpu", packet_count);
299299
}
300300

301301
pub fn sign_shreds_gpu_pinned_keypair(keypair: &Keypair, cache: &RecyclerCache) -> PinnedVec<u8> {

0 commit comments

Comments
 (0)