diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 2df91a639c..941a3fa52c 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -2460,7 +2460,8 @@ impl Blockstore { .collect(); let data_shreds = data_shreds?; - assert!(data_shreds.last().unwrap().data_complete()); + let last_shred = data_shreds.last().unwrap(); + assert!(last_shred.data_complete() || last_shred.last_in_slot()); let deshred_payload = Shredder::deshred(&data_shreds).map_err(|e| { BlockstoreError::InvalidShredData(Box::new(bincode::ErrorKind::Custom(format!( @@ -7367,4 +7368,24 @@ pub mod tests { ); assert_eq!(completed_data_indexes, vec![0, 1, 3]); } + + #[test] + fn test_remove_shred_data_complete_flag() { + let (mut shreds, entries) = make_slot_entries(0, 0, 1); + + let ledger_path = get_tmp_ledger_path!(); + let ledger = Blockstore::open(&ledger_path).unwrap(); + + // Remove the data complete flag from the last shred + shreds[0].unset_data_complete(); + + ledger.insert_shreds(shreds, None, false).unwrap(); + + // Check that the `data_complete` flag was unset in the stored shred, but the + // `last_in_slot` flag is set. + let stored_shred = &ledger.get_data_shreds_for_slot(0, 0).unwrap()[0]; + assert!(!stored_shred.data_complete()); + assert!(stored_shred.last_in_slot()); + assert_eq!(entries, ledger.get_any_valid_slot_entries(0, 0)); + } } diff --git a/ledger/src/shred.rs b/ledger/src/shred.rs index 3f41b29bb5..547afe16e7 100644 --- a/ledger/src/shred.rs +++ b/ledger/src/shred.rs @@ -405,6 +405,24 @@ impl Shred { } } + #[cfg(test)] + pub fn unset_data_complete(&mut self) { + if self.is_data() { + self.data_header.flags &= !DATA_COMPLETE_SHRED; + } + + // Data header starts after the shred common header + let mut start = SIZE_OF_COMMON_SHRED_HEADER; + let size_of_data_shred_header = SIZE_OF_DATA_SHRED_HEADER; + Self::serialize_obj_into( + &mut start, + size_of_data_shred_header, + &mut self.payload, + &self.data_header, + ) + .expect("Failed to write data header into shred buffer"); + } + pub fn data_complete(&self) -> bool { if self.is_data() { self.data_header.flags & DATA_COMPLETE_SHRED == DATA_COMPLETE_SHRED