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}
35
36#[derive(Debug, Default, Clone, Eq, PartialEq)]
39pub struct ReconstructedContents {
40 pub segment_header: Option<SegmentHeader>,
42 pub blocks: Vec<(BlockNumber, Vec<u8>)>,
44}
45
46#[derive(Debug, Clone)]
48pub struct Reconstructor {
49 erasure_coding: ErasureCoding,
51 last_segment_index: Option<LocalSegmentIndex>,
53 partial_block: Option<Vec<u8>>,
55}
56
57impl Reconstructor {
58 pub fn new(erasure_coding: ErasureCoding) -> Self {
60 Self {
61 erasure_coding,
62 last_segment_index: None,
63 partial_block: None,
64 }
65 }
66
67 pub fn reconstruct_segment(
73 &self,
74 segment_pieces: &[Option<Piece>],
76 ) -> Result<Segment, ReconstructorError> {
77 if segment_pieces.len() < ArchivedHistorySegment::NUM_PIECES {
78 return Err(ReconstructorError::NotEnoughShards {
79 num_shards: segment_pieces.len(),
80 });
81 }
82 let mut segment_data = RecordedHistorySegment::new_boxed();
83
84 if !segment_pieces
85 .iter()
86 .zip(segment_data.iter_mut())
87 .all(|(maybe_piece, record)| {
88 if let Some(piece) = maybe_piece {
89 record.copy_from_slice(&*piece.record);
92 true
93 } else {
94 false
95 }
96 })
97 {
98 let (source_segment_pieces, parity_segment_pieces) =
101 segment_pieces.split_at(RecordedHistorySegment::NUM_RAW_RECORDS);
102
103 let mut source_present = ShardsBitmap::none();
104 for (index, (output_record, maybe_source_piece)) in segment_data
105 .iter_mut()
106 .zip(source_segment_pieces)
107 .enumerate()
108 {
109 if let Some(input_piece) = maybe_source_piece {
110 output_record.copy_from_slice(&*input_piece.record);
113 source_present.set(index);
114 }
115 }
116
117 let parity_records = array::from_fn(|index| {
119 parity_segment_pieces[index]
120 .as_ref()
121 .map(|input_piece| &input_piece.record)
122 });
123
124 self.erasure_coding.recover_source_scattered(
125 {
126 let records: &mut [_; RecordedHistorySegment::NUM_RAW_RECORDS] =
127 segment_data.as_mut();
128 let mut records = records.iter_mut();
129
130 array::from_fn::<_, { RecordedHistorySegment::NUM_RAW_RECORDS }, _>(|_| {
131 records
132 .next()
133 .expect("Number of records matches the array size; qed")
134 })
135 },
136 &source_present,
137 parity_records,
138 )?;
139 }
140
141 let segment = Segment::decode(&mut AsRef::<[u8]>::as_ref(segment_data.as_ref()))
142 .map_err(ReconstructorError::SegmentDecoding)?;
143
144 Ok(segment)
145 }
146
147 pub fn add_segment(
155 &mut self,
156 segment_pieces: &[Option<Piece>],
157 ) -> Result<ReconstructedContents, ReconstructorError> {
158 let segment = self.reconstruct_segment(segment_pieces)?;
159
160 let mut reconstructed_contents = ReconstructedContents::default();
161 let mut next_block_number = BlockNumber::ZERO;
162 let mut partial_block = self.partial_block.take().unwrap_or_default();
163
164 for segment_item in segment.items {
165 #[expect(
166 clippy::rest_pattern_accessible_field,
167 reason = "Do not need other fields"
168 )]
169 match segment_item {
170 SegmentItem::Padding => {
171 }
173 SegmentItem::Block { bytes, .. } => {
174 if !partial_block.is_empty() {
175 reconstructed_contents
176 .blocks
177 .push((next_block_number, mem::take(&mut partial_block)));
178
179 next_block_number += BlockNumber::ONE;
180 }
181
182 reconstructed_contents
183 .blocks
184 .push((next_block_number, Vec::from(bytes)));
185
186 next_block_number += BlockNumber::ONE;
187 }
188 SegmentItem::BlockStart { bytes, .. } => {
189 if !partial_block.is_empty() {
190 reconstructed_contents
191 .blocks
192 .push((next_block_number, mem::take(&mut partial_block)));
193
194 next_block_number += BlockNumber::ONE;
195 }
196
197 partial_block = Vec::from(bytes);
198 }
199 SegmentItem::BlockContinuation { bytes, .. } => {
200 if partial_block.is_empty() {
201 continue;
204 }
205
206 partial_block.extend_from_slice(&bytes);
207 }
208 SegmentItem::ParentSegmentHeader(segment_header) => {
209 let segment_index = segment_header.index.as_inner();
210
211 if let Some(last_segment_index) = self.last_segment_index
212 && last_segment_index != segment_index
213 {
214 return Err(ReconstructorError::IncorrectSegmentOrder {
215 expected_segment_index: last_segment_index + LocalSegmentIndex::ONE,
216 actual_segment_index: segment_index + LocalSegmentIndex::ONE,
217 });
218 }
219
220 self.last_segment_index
221 .replace(segment_index + LocalSegmentIndex::ONE);
222
223 let LastArchivedBlock {
224 number,
225 archived_progress,
226 } = segment_header.last_archived_block;
227
228 reconstructed_contents
229 .segment_header
230 .replace(segment_header);
231
232 match archived_progress.partial() {
233 None => {
234 reconstructed_contents
235 .blocks
236 .push((next_block_number, mem::take(&mut partial_block)));
237
238 next_block_number = number.as_inner() + BlockNumber::ONE;
239 }
240 Some(_bytes) => {
241 next_block_number = number.as_inner();
242
243 if partial_block.is_empty() {
244 next_block_number += BlockNumber::ONE;
246 }
247 }
248 }
249 }
250 }
251 }
252
253 if !partial_block.is_empty() {
254 self.partial_block.replace(partial_block);
255 }
256
257 if self.last_segment_index.is_none() {
258 self.last_segment_index.replace(LocalSegmentIndex::ZERO);
259 }
260
261 Ok(reconstructed_contents)
262 }
263}