ab_archiving/
reconstructor.rs1use crate::archiver::{Segment, SegmentItem};
2use ab_core_primitives::block::BlockNumber;
3use ab_core_primitives::pieces::Piece;
4use ab_core_primitives::segments::{
5 ArchivedHistorySegment, LastArchivedBlock, LocalSegmentIndex, RecordedHistorySegment,
6 SegmentHeader,
7};
8use ab_erasure_coding::{ErasureCoding, ErasureCodingError, ShardsBitmap};
9use alloc::vec::Vec;
10use core::{array, mem};
11use parity_scale_codec::Decode;
12
13#[derive(Debug, Clone, PartialEq, thiserror::Error)]
15pub enum ReconstructorError {
16 #[error("Error during data shards reconstruction: {0}")]
18 DataShardsReconstruction(#[from] ErasureCodingError),
19 #[error("Not enough shards: {num_shards}")]
21 NotEnoughShards { num_shards: usize },
22 #[error("Error during segment decoding: {0}")]
24 SegmentDecoding(parity_scale_codec::Error),
25 #[error(
27 "Incorrect segment order, expected index {expected_segment_index}, actual \
28 {actual_segment_index}"
29 )]
30 IncorrectSegmentOrder {
31 expected_segment_index: LocalSegmentIndex,
32 actual_segment_index: LocalSegmentIndex,
33 },
34 #[error("Parent segment header is not the first item of the segment")]
36 UnexpectedParentSegmentHeader,
37 #[error("Segment index overflow, no segment can follow the segment with the maximum index")]
39 SegmentIndexOverflow,
40 #[error("Block number overflow")]
42 BlockNumberOverflow,
43}
44
45#[derive(Debug, Default, Clone, Eq, PartialEq)]
48pub struct ReconstructedContents {
49 pub segment_header: Option<SegmentHeader>,
51 pub blocks: Vec<(BlockNumber, Vec<u8>)>,
53}
54
55#[derive(Debug, Clone)]
57pub struct Reconstructor {
58 erasure_coding: ErasureCoding,
60 last_segment_index: Option<LocalSegmentIndex>,
62 partial_block: Vec<u8>,
64}
65
66impl Reconstructor {
67 pub fn new(erasure_coding: ErasureCoding) -> Self {
69 Self {
70 erasure_coding,
71 last_segment_index: None,
72 partial_block: Vec::new(),
73 }
74 }
75
76 pub fn reconstruct_segment(
82 &self,
83 segment_pieces: &[Option<Piece>],
85 ) -> Result<Segment, ReconstructorError> {
86 if segment_pieces.len() < ArchivedHistorySegment::NUM_PIECES {
87 return Err(ReconstructorError::NotEnoughShards {
88 num_shards: segment_pieces.len(),
89 });
90 }
91 let mut segment_data = RecordedHistorySegment::new_boxed();
92
93 if !segment_pieces
94 .iter()
95 .zip(segment_data.iter_mut())
96 .all(|(maybe_piece, record)| {
97 if let Some(piece) = maybe_piece {
98 record.copy_from_slice(&*piece.record);
101 true
102 } else {
103 false
104 }
105 })
106 {
107 let (source_segment_pieces, parity_segment_pieces) =
110 segment_pieces.split_at(RecordedHistorySegment::NUM_RAW_RECORDS);
111
112 let mut source_present = ShardsBitmap::none();
113 for (index, (output_record, maybe_source_piece)) in segment_data
114 .iter_mut()
115 .zip(source_segment_pieces)
116 .enumerate()
117 {
118 if let Some(input_piece) = maybe_source_piece {
119 output_record.copy_from_slice(&*input_piece.record);
122 source_present.set(index);
123 }
124 }
125
126 let parity_records = array::from_fn(|index| {
128 parity_segment_pieces[index]
129 .as_ref()
130 .map(|input_piece| &input_piece.record)
131 });
132
133 #[cfg(not(feature = "parallel"))]
134 let recover_source = ErasureCoding::recover_source_scattered;
135 #[cfg(feature = "parallel")]
136 let recover_source = ErasureCoding::par_recover_source_scattered;
137
138 recover_source(
139 &self.erasure_coding,
140 {
141 let records: &mut [_; RecordedHistorySegment::NUM_RAW_RECORDS] =
142 segment_data.as_mut();
143 let mut records = records.iter_mut();
144
145 array::from_fn::<_, { RecordedHistorySegment::NUM_RAW_RECORDS }, _>(|_| {
146 records
147 .next()
148 .expect("Number of records matches the array size; qed")
149 })
150 },
151 &source_present,
152 parity_records,
153 )?;
154 }
155
156 let segment = Segment::decode(&mut AsRef::<[u8]>::as_ref(segment_data.as_ref()))
157 .map_err(ReconstructorError::SegmentDecoding)?;
158
159 Ok(segment)
160 }
161
162 pub fn add_segment(
171 mut self,
172 segment_pieces: &[Option<Piece>],
173 ) -> Result<(Self, ReconstructedContents), ReconstructorError> {
174 let segment = self.reconstruct_segment(segment_pieces)?;
175 let mut segment_items = segment.items.into_iter().peekable();
176
177 let mut reconstructed_contents = ReconstructedContents::default();
178 let mut next_block_number = BlockNumber::ZERO;
179
180 let segment_index = if let Some(&SegmentItem::ParentSegmentHeader(parent_segment_header)) =
182 segment_items.peek()
183 {
184 segment_items.next();
185
186 let LastArchivedBlock {
187 number,
188 archived_progress,
189 } = parent_segment_header.last_archived_block;
190
191 reconstructed_contents
192 .segment_header
193 .replace(parent_segment_header);
194
195 match archived_progress.partial() {
196 None => {
197 if !self.partial_block.is_empty() {
200 reconstructed_contents
201 .blocks
202 .push((number.as_inner(), mem::take(&mut self.partial_block)));
203 }
204
205 next_block_number = number
206 .as_inner()
207 .checked_add(BlockNumber::ONE)
208 .ok_or(ReconstructorError::BlockNumberOverflow)?;
209 }
210 Some(_bytes) => {
211 next_block_number = number.as_inner();
212
213 if self.partial_block.is_empty() {
214 next_block_number = next_block_number
216 .checked_add(BlockNumber::ONE)
217 .ok_or(ReconstructorError::BlockNumberOverflow)?;
218 }
219 }
220 }
221
222 parent_segment_header
223 .index
224 .as_inner()
225 .checked_add(LocalSegmentIndex::ONE)
226 .ok_or(ReconstructorError::SegmentIndexOverflow)?
227 } else {
228 LocalSegmentIndex::ZERO
229 };
230
231 if let Some(last_segment_index) = self.last_segment_index {
232 let expected_segment_index = last_segment_index
233 .checked_add(LocalSegmentIndex::ONE)
234 .ok_or(ReconstructorError::SegmentIndexOverflow)?;
235
236 if segment_index != expected_segment_index {
237 return Err(ReconstructorError::IncorrectSegmentOrder {
238 expected_segment_index,
239 actual_segment_index: segment_index,
240 });
241 }
242 }
243
244 self.last_segment_index.replace(segment_index);
245
246 for segment_item in segment_items {
247 #[expect(
248 clippy::rest_pattern_accessible_field,
249 reason = "Do not need other fields"
250 )]
251 match segment_item {
252 SegmentItem::Padding => {
253 }
255 SegmentItem::Block { bytes, .. } => {
256 if !self.partial_block.is_empty() {
257 reconstructed_contents
258 .blocks
259 .push((next_block_number, mem::take(&mut self.partial_block)));
260
261 next_block_number = next_block_number
262 .checked_add(BlockNumber::ONE)
263 .ok_or(ReconstructorError::BlockNumberOverflow)?;
264 }
265
266 reconstructed_contents
267 .blocks
268 .push((next_block_number, Vec::from(bytes)));
269
270 next_block_number = next_block_number
271 .checked_add(BlockNumber::ONE)
272 .ok_or(ReconstructorError::BlockNumberOverflow)?;
273 }
274 SegmentItem::BlockStart { bytes, .. } => {
275 if !self.partial_block.is_empty() {
276 reconstructed_contents
277 .blocks
278 .push((next_block_number, mem::take(&mut self.partial_block)));
279
280 next_block_number = next_block_number
281 .checked_add(BlockNumber::ONE)
282 .ok_or(ReconstructorError::BlockNumberOverflow)?;
283 }
284
285 self.partial_block = Vec::from(bytes);
286 }
287 SegmentItem::BlockContinuation { bytes, .. } => {
288 if self.partial_block.is_empty() {
289 continue;
292 }
293
294 self.partial_block.extend_from_slice(&bytes);
295 }
296 SegmentItem::ParentSegmentHeader(_) => {
297 return Err(ReconstructorError::UnexpectedParentSegmentHeader);
298 }
299 }
300 }
301
302 Ok((self, reconstructed_contents))
303 }
304}