1use crate::objects::{BlockObject, GlobalObject};
2use ab_core_primitives::block::BlockNumber;
3use ab_core_primitives::hashes::Blake3Hash;
4use ab_core_primitives::pieces::{
5 InnerPiece, PieceHeader, PiecePosition, Record, RecordChunksRoot, RecordProof,
6};
7use ab_core_primitives::segments::{
8 ArchivedBlockProgress, ArchivedHistorySegment, LastArchivedBlock, LocalSegmentIndex,
9 RecordedHistorySegment, SegmentHeader, SegmentRoot,
10};
11use ab_core_primitives::shard::ShardIndex;
12use ab_erasure_coding::ErasureCoding;
13use ab_merkle_tree::blake3::BalancedMerkleTree;
14use alloc::collections::VecDeque;
15use alloc::vec;
16use alloc::vec::Vec;
17use core::cmp::Ordering;
18use core::num::NonZeroU32;
19use core::ops::Deref;
20use parity_scale_codec::{Decode, Encode, Input, Output};
21#[cfg(feature = "parallel")]
22use rayon::prelude::*;
23
24struct ArchivedHistorySegmentOutput<'a> {
25 segment: &'a mut ArchivedHistorySegment,
26 offset: usize,
27}
28
29impl Output for ArchivedHistorySegmentOutput<'_> {
30 #[inline]
31 fn write(&mut self, mut bytes: &[u8]) {
32 while !bytes.is_empty() {
33 let piece = self
34 .segment
35 .get_mut(self.offset / Record::SIZE)
36 .expect("Encoding never exceeds the segment size; qed");
37 let output = &mut piece.record.as_flattened_mut()[self.offset % Record::SIZE..];
38 let bytes_to_write = output.len().min(bytes.len());
39 output[..bytes_to_write].copy_from_slice(&bytes[..bytes_to_write]);
40 self.offset += bytes_to_write;
41 bytes = &bytes[bytes_to_write..];
42 }
43 }
44}
45
46#[derive(Debug, Default, Clone, Eq, PartialEq)]
48pub struct Segment {
49 pub items: Vec<SegmentItem>,
51}
52
53impl Encode for Segment {
54 #[inline(always)]
55 fn size_hint(&self) -> usize {
56 RecordedHistorySegment::SIZE
57 }
58
59 #[inline]
60 fn encode_to<O>(&self, dest: &mut O)
61 where
62 O: Output + ?Sized,
63 {
64 for item in &self.items {
65 item.encode_to(dest);
66 }
67 }
68}
69
70impl Decode for Segment {
71 #[inline]
72 fn decode<I>(input: &mut I) -> Result<Self, parity_scale_codec::Error>
73 where
74 I: Input,
75 {
76 let mut items = Vec::new();
77 loop {
78 match input.remaining_len()? {
79 Some(0) => {
80 break;
81 }
82 Some(_) => {
83 }
85 None => {
86 return Err(
87 "Source doesn't report remaining length, decoding not possible".into(),
88 );
89 }
90 }
91
92 match SegmentItem::decode(input) {
93 Ok(item) => {
94 items.push(item);
95 }
96 Err(error) => {
97 return Err(error.chain("Could not decode `Segment::items`"));
98 }
99 }
100 }
101
102 Ok(Self { items })
103 }
104}
105
106#[derive(Debug, Clone, Eq, PartialEq)]
109pub struct BlockBytes(Vec<u8>);
110
111impl Deref for BlockBytes {
112 type Target = [u8];
113
114 #[inline(always)]
115 fn deref(&self) -> &Self::Target {
116 &self.0
117 }
118}
119
120impl From<BlockBytes> for Vec<u8> {
121 #[inline(always)]
122 fn from(value: BlockBytes) -> Self {
123 value.0
124 }
125}
126
127impl Encode for BlockBytes {
128 #[inline(always)]
129 fn size_hint(&self) -> usize {
130 size_of::<u32>() + self.0.len()
131 }
132
133 #[inline]
134 fn encode_to<O>(&self, dest: &mut O)
135 where
136 O: Output + ?Sized,
137 {
138 let length = u32::try_from(self.0.len())
139 .expect("All constructors guarantee the size doesn't exceed `u32`; qed");
140
141 length.encode_to(dest);
142 dest.write(&self.0);
143 }
144}
145
146impl Decode for BlockBytes {
147 #[inline]
148 fn decode<I>(input: &mut I) -> Result<Self, parity_scale_codec::Error>
149 where
150 I: Input,
151 {
152 let length = u32::decode(input)?;
153 if length as usize > (RecordedHistorySegment::SIZE - size_of::<u32>()) {
154 return Err("Segment item size is impossibly large".into());
155 }
156 let mut bytes = vec![0; length as usize];
160 input.read(&mut bytes)?;
161 Ok(Self(bytes))
162 }
163}
164
165impl BlockBytes {
166 #[inline(always)]
167 fn truncate(&mut self, size: usize) {
168 self.0.truncate(size);
169 }
170}
171
172#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode)]
174pub enum SegmentItem {
175 #[codec(index = 0)]
177 Padding,
178 #[codec(index = 1)]
180 Block {
181 bytes: BlockBytes,
183 #[doc(hidden)]
185 #[codec(skip)]
186 block_objects: Vec<BlockObject>,
187 },
188 #[codec(index = 2)]
191 BlockStart {
192 bytes: BlockBytes,
194 #[doc(hidden)]
196 #[codec(skip)]
197 block_objects: Vec<BlockObject>,
198 },
199 #[codec(index = 3)]
201 BlockContinuation {
202 bytes: BlockBytes,
204 #[doc(hidden)]
206 #[codec(skip)]
207 block_objects: Vec<BlockObject>,
208 },
209 #[codec(index = 4)]
211 ParentSegmentHeader(SegmentHeader),
212}
213
214#[derive(Debug, Clone, Eq, PartialEq)]
217pub struct NewArchivedSegment {
218 pub segment_header: SegmentHeader,
220 pub pieces: ArchivedHistorySegment,
222}
223
224#[derive(Debug, Clone, Eq, PartialEq)]
226pub struct ArchiveBlockOutcome {
227 pub archived_segments: Vec<NewArchivedSegment>,
230
231 pub global_objects: Vec<GlobalObject>,
236}
237
238#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
240pub enum ArchiverInstantiationError {
241 #[error("Invalid last archived block, its size {0} bytes is the same as the encoded block")]
244 InvalidLastArchivedBlock(u32),
245 #[error(
247 "Invalid block, its size {block_bytes} bytes is smaller than the already archived block \
248 {archived_block_bytes} bytes"
249 )]
250 InvalidBlockSmallSize {
251 block_bytes: u32,
253 archived_block_bytes: u32,
255 },
256 #[error("Invalid block, its size {block_bytes} bytes is larger than `u32::MAX`")]
258 InvalidBlockLargeSize {
259 block_bytes: usize,
261 },
262}
263
264const MIN_DATA_ITEM_SIZE: usize = 6;
270
271#[derive(Debug, Default, Clone)]
274struct SegmentItemsBuffer {
275 items: VecDeque<SegmentItem>,
276 encoded_size: usize,
278 num_mapped_items: usize,
280 mapped_encoded_size: usize,
282}
283
284impl SegmentItemsBuffer {
285 fn len(&self) -> usize {
286 self.items.len()
287 }
288
289 fn push_back(&mut self, item: SegmentItem) {
290 self.encoded_size += item.encoded_size();
291 self.items.push_back(item);
292 }
293
294 fn push_front(&mut self, item: SegmentItem) {
295 self.encoded_size += item.encoded_size();
296 self.items.push_front(item);
297 self.num_mapped_items = 0;
300 self.mapped_encoded_size = 0;
301 }
302
303 fn pop_front(&mut self) -> Option<SegmentItem> {
304 let item = self.items.pop_front()?;
305 let item_encoded_size = item.encoded_size();
306 self.encoded_size = self
307 .encoded_size
308 .checked_sub(item_encoded_size)
309 .expect("Item size was added to the total when the item was inserted; qed");
310 if self.num_mapped_items > 0 {
311 self.num_mapped_items -= 1;
312 self.mapped_encoded_size = self
313 .mapped_encoded_size
314 .checked_sub(item_encoded_size)
315 .expect("Mapped item size was added to the mapped total; qed");
316 }
317 Some(item)
318 }
319
320 fn take_unmapped_items(&mut self) -> (usize, impl Iterator<Item = &mut SegmentItem>) {
327 let offset = self.mapped_encoded_size;
328 let first_unmapped_item = self.num_mapped_items;
329
330 self.num_mapped_items = self.items.len();
331 self.mapped_encoded_size = self.encoded_size;
332
333 (offset, self.items.range_mut(first_unmapped_item..))
334 }
335}
336
337#[derive(Debug, Clone)]
344pub struct Archiver {
345 shard_index: ShardIndex,
346 buffer: SegmentItemsBuffer,
349 erasure_coding: ErasureCoding,
351 segment_index: LocalSegmentIndex,
353 prev_segment_header_hash: Blake3Hash,
355 last_archived_block: Option<LastArchivedBlock>,
357}
358
359impl Archiver {
360 pub fn new(shard_index: ShardIndex, erasure_coding: ErasureCoding) -> Self {
362 Self {
363 shard_index,
364 buffer: SegmentItemsBuffer::default(),
365 erasure_coding,
366 segment_index: LocalSegmentIndex::ZERO,
367 prev_segment_header_hash: Blake3Hash::default(),
368 last_archived_block: None,
369 }
370 }
371
372 pub fn with_initial_state(
376 shard_index: ShardIndex,
377 erasure_coding: ErasureCoding,
378 segment_header: SegmentHeader,
379 encoded_block: &[u8],
380 mut block_objects: Vec<BlockObject>,
381 ) -> Result<Self, ArchiverInstantiationError> {
382 let mut archiver = Self::new(shard_index, erasure_coding);
383
384 archiver.segment_index = segment_header.index.as_inner() + LocalSegmentIndex::ONE;
385 archiver.prev_segment_header_hash = segment_header.hash();
386 archiver.last_archived_block = Some(segment_header.last_archived_block);
387
388 archiver
390 .buffer
391 .push_back(SegmentItem::ParentSegmentHeader(segment_header));
392
393 if let Some(archived_block_bytes) = archiver
394 .last_archived_block
395 .expect("Just inserted; qed")
396 .partial_archived()
397 {
398 let archived_block_bytes = archived_block_bytes.get();
399 let encoded_block_bytes = u32::try_from(encoded_block.len()).map_err(|_error| {
400 ArchiverInstantiationError::InvalidBlockLargeSize {
401 block_bytes: encoded_block.len(),
402 }
403 })?;
404
405 match encoded_block_bytes.cmp(&archived_block_bytes) {
406 Ordering::Less => {
407 return Err(ArchiverInstantiationError::InvalidBlockSmallSize {
408 block_bytes: encoded_block_bytes,
409 archived_block_bytes,
410 });
411 }
412 Ordering::Equal => {
413 return Err(ArchiverInstantiationError::InvalidLastArchivedBlock(
414 encoded_block_bytes,
415 ));
416 }
417 Ordering::Greater => {
418 block_objects.retain_mut(|block_object: &mut BlockObject| {
421 if block_object.offset >= archived_block_bytes {
422 block_object.offset -= archived_block_bytes;
423 true
424 } else {
425 false
426 }
427 });
428 archiver.buffer.push_back(SegmentItem::BlockContinuation {
429 bytes: BlockBytes(
430 encoded_block[(archived_block_bytes as usize)..].to_vec(),
431 ),
432 block_objects,
433 });
434 }
435 }
436 }
437
438 Ok(archiver)
439 }
440
441 pub fn last_archived_block_number(&self) -> Option<BlockNumber> {
443 self.last_archived_block
444 .map(|last_archived_block| last_archived_block.number())
445 }
446
447 pub fn add_block(
458 &mut self,
459 bytes: Vec<u8>,
460 block_objects: Vec<BlockObject>,
461 ) -> Option<ArchiveBlockOutcome> {
462 if u32::try_from(bytes.len()).is_err() {
463 return None;
464 }
465
466 self.buffer.push_back(SegmentItem::Block {
468 bytes: BlockBytes(bytes),
469 block_objects,
470 });
471
472 let mut archived_segments = Vec::new();
473 let mut object_mapping = Vec::new();
474
475 while let Some(mut segment) = self.produce_segment() {
477 object_mapping.extend(Self::produce_object_mappings(
479 Segment::default().encoded_size(),
480 segment.items.iter_mut(),
481 ));
482 archived_segments.push(self.produce_archived_segment(segment));
483 }
484
485 object_mapping.extend(self.produce_next_segment_mappings());
487
488 Some(ArchiveBlockOutcome {
489 archived_segments,
490 global_objects: object_mapping,
491 })
492 }
493
494 fn produce_segment(&mut self) -> Option<Segment> {
497 let mut segment_size = Segment::default().encoded_size();
498
499 if RecordedHistorySegment::SIZE.saturating_sub(segment_size + self.buffer.encoded_size)
501 >= MIN_DATA_ITEM_SIZE
502 {
503 return None;
504 }
505
506 let mut segment = Segment {
507 items: Vec::with_capacity(self.buffer.len()),
508 };
509
510 let mut last_archived_block = self.last_archived_block;
511
512 while RecordedHistorySegment::SIZE.saturating_sub(segment_size) >= MIN_DATA_ITEM_SIZE {
513 let segment_item = self.buffer.pop_front().expect(
514 "Buffered items are large enough to fill a segment, which is checked above; qed",
515 );
516
517 let segment_item_encoded_size = segment_item.encoded_size();
518 segment_size += segment_item_encoded_size;
519
520 let spill_over = segment_size.saturating_sub(RecordedHistorySegment::SIZE);
522
523 let segment_item = match segment_item {
524 SegmentItem::Padding => {
525 unreachable!("Buffer never contains SegmentItem::Padding; qed");
526 }
527 SegmentItem::Block {
528 mut bytes,
529 mut block_objects,
530 } => {
531 let last_archived_block =
532 if let Some(last_archived_block) = &mut last_archived_block {
533 last_archived_block
536 .number
537 .replace(last_archived_block.number() + BlockNumber::ONE);
538 last_archived_block.set_complete();
539 last_archived_block
540 } else {
541 last_archived_block.insert(LastArchivedBlock {
543 number: BlockNumber::ZERO.into(),
544 archived_progress: ArchivedBlockProgress::new_complete(),
545 })
546 };
547
548 if spill_over == 0 {
549 SegmentItem::Block {
550 bytes,
551 block_objects,
552 }
553 } else {
554 let split_point = bytes.len() - spill_over;
555
556 {
557 let continuation_bytes = bytes[split_point..].to_vec();
558 let continuation_block_objects = block_objects
559 .extract_if(.., |block_object: &mut BlockObject| {
560 if block_object.offset >= split_point as u32 {
561 block_object.offset -= split_point as u32;
562 true
563 } else {
564 false
565 }
566 })
567 .collect();
568
569 self.buffer.push_front(SegmentItem::BlockContinuation {
572 bytes: BlockBytes(continuation_bytes),
573 block_objects: continuation_block_objects,
574 });
575 }
576
577 bytes.truncate(split_point);
578 let archived_bytes = u32::try_from(split_point)
580 .ok()
581 .and_then(NonZeroU32::new)
582 .expect(
583 "The loop condition leaves space for at least one byte of the \
584 block before the split point, which is below the block size \
585 that `::add_block()` ensures fits into `u32`; qed",
586 );
587 last_archived_block.set_partial_archived(archived_bytes);
588
589 SegmentItem::BlockStart {
590 bytes,
591 block_objects,
592 }
593 }
594 }
595 #[expect(
596 clippy::rest_pattern_accessible_field,
597 reason = "Do not care about fields"
598 )]
599 SegmentItem::BlockStart { .. } => {
600 unreachable!("Buffer never contains SegmentItem::BlockStart; qed");
601 }
602 SegmentItem::BlockContinuation {
603 mut bytes,
604 mut block_objects,
605 } => {
606 let last_archived_block = last_archived_block.as_mut().expect(
607 "Block continuation implies that there are some bytes archived \
608 already; qed",
609 );
610
611 let previously_archived_bytes = last_archived_block.partial_archived().expect(
612 "Block continuation implies that there are some bytes archived \
613 already; qed",
614 );
615
616 if spill_over == 0 {
617 last_archived_block.set_complete();
618
619 SegmentItem::BlockContinuation {
620 bytes,
621 block_objects,
622 }
623 } else {
624 let split_point = bytes.len() - spill_over;
625
626 {
627 let continuation_bytes = bytes[split_point..].to_vec();
628 let continuation_block_objects = block_objects
629 .extract_if(.., |block_object: &mut BlockObject| {
630 if block_object.offset >= split_point as u32 {
631 block_object.offset -= split_point as u32;
632 true
633 } else {
634 false
635 }
636 })
637 .collect();
638 self.buffer.push_front(SegmentItem::BlockContinuation {
641 bytes: BlockBytes(continuation_bytes),
642 block_objects: continuation_block_objects,
643 });
644 }
645
646 bytes.truncate(split_point);
647 let archived_bytes = previously_archived_bytes.get()
649 + u32::try_from(split_point).expect(
650 "The split point is below the size of the block, which \
651 `::add_block()` and `::with_initial_state()` ensure fits into \
652 `u32`; qed",
653 );
654 let archived_bytes = NonZeroU32::new(archived_bytes).expect(
655 "Spillover means non-zero length of the block was archived; qed",
656 );
657 last_archived_block.set_partial_archived(archived_bytes);
658
659 SegmentItem::BlockContinuation {
660 bytes,
661 block_objects,
662 }
663 }
664 }
665 SegmentItem::ParentSegmentHeader(parent_segment_header) => {
666 SegmentItem::ParentSegmentHeader(parent_segment_header)
668 }
669 };
670
671 segment.items.push(segment_item);
672 }
673
674 self.last_archived_block = last_archived_block;
675
676 Some(segment)
677 }
678
679 fn produce_next_segment_mappings(&mut self) -> Vec<GlobalObject> {
685 let (offset, unmapped_items) = self.buffer.take_unmapped_items();
688
689 Self::produce_object_mappings(Segment::default().encoded_size() + offset, unmapped_items)
690 }
691
692 fn produce_object_mappings<'a, Items>(
697 mut base_offset_in_segment: usize,
698 items: Items,
699 ) -> Vec<GlobalObject>
700 where
701 Items: Iterator<Item = &'a mut SegmentItem>,
702 {
703 let mut corrected_object_mapping = Vec::new();
704 for segment_item in items {
705 match segment_item {
706 SegmentItem::Padding => {
707 unreachable!(
708 "Segment during archiving never contains SegmentItem::Padding; qed"
709 );
710 }
711 SegmentItem::Block {
712 bytes: _,
713 block_objects,
714 }
715 | SegmentItem::BlockStart {
716 bytes: _,
717 block_objects,
718 }
719 | SegmentItem::BlockContinuation {
720 bytes: _,
721 block_objects,
722 } => {
723 for block_object in block_objects.drain(..) {
724 let offset_in_segment = base_offset_in_segment
726 + 1
727 + u32::encoded_fixed_size().expect("Fixed size; qed")
728 + block_object.offset as usize;
729 let raw_piece_offset = (offset_in_segment % Record::SIZE)
730 .try_into()
731 .expect("Offset within piece should always fit in 32-bit integer; qed");
732 corrected_object_mapping.push(GlobalObject {
733 hash: block_object.hash,
734 piece_position: PiecePosition::from(
735 (offset_in_segment / Record::SIZE) as u8,
736 ),
737 offset: raw_piece_offset,
738 });
739 }
740 }
741 SegmentItem::ParentSegmentHeader(_) => {
742 }
744 }
745
746 base_offset_in_segment += segment_item.encoded_size();
747 }
748
749 corrected_object_mapping
750 }
751
752 fn produce_archived_segment(&mut self, segment: Segment) -> NewArchivedSegment {
754 let mut pieces = {
755 let mut pieces = ArchivedHistorySegment::default();
756
757 segment.encode_to(&mut ArchivedHistorySegmentOutput {
758 segment: &mut pieces,
759 offset: 0,
760 });
761 drop(segment);
763
764 let (source_records, parity_records) = pieces.split_records_mut();
765
766 #[cfg(not(feature = "parallel"))]
767 let extend = ErasureCoding::extend_scattered;
768 #[cfg(feature = "parallel")]
769 let extend = ErasureCoding::par_extend_scattered;
770
771 extend(
772 &self.erasure_coding,
773 source_records.map(|record| &*record),
774 parity_records,
775 )
776 .expect("Statically correct parameters; qed");
777
778 pieces
779 };
780
781 let mut record_roots = [[0; _]; ArchivedHistorySegment::NUM_PIECES];
783 {
784 let process_piece =
793 |parity_chunks: &mut Record, piece: &mut InnerPiece, record_root: &mut [u8; _]| {
794 self.erasure_coding
795 .extend(&piece.record, parity_chunks)
796 .expect(
797 "Erasure coding instance is deliberately configured to support this \
798 input; qed",
799 );
800
801 let source_chunks_root = *piece.record.source_chunks_root();
802 let parity_chunks_root = BalancedMerkleTree::compute_root_only(parity_chunks);
803
804 *record_root = BalancedMerkleTree::compute_root_only(&[
805 source_chunks_root,
806 parity_chunks_root,
807 ]);
808
809 piece.header.parity_chunks_root = RecordChunksRoot::from(parity_chunks_root);
810 };
811
812 #[cfg(not(feature = "parallel"))]
813 {
814 let mut parity_chunks = Record::new_boxed();
815 pieces
816 .iter_mut()
817 .zip(&mut record_roots)
818 .for_each(|(piece, record_root)| {
819 process_piece(&mut parity_chunks, piece, record_root);
820 });
821 }
822 #[cfg(feature = "parallel")]
823 pieces
824 .par_iter_mut()
825 .zip_eq(&mut record_roots)
826 .for_each_init(Record::new_boxed, |parity_chunks, (piece, record_root)| {
827 process_piece(parity_chunks, piece, record_root);
828 });
829 }
830
831 let segment_merkle_tree = BalancedMerkleTree::new_boxed(&record_roots);
832
833 let segment_root = SegmentRoot::from(segment_merkle_tree.root());
834
835 let segment_header = SegmentHeader {
837 index: self.segment_index.into(),
838 root: segment_root,
839 prev_segment_header_hash: self.prev_segment_header_hash,
840 last_archived_block: self
841 .last_archived_block
842 .expect("Never empty by the time segment is produced; qed"),
843 };
844
845 pieces
847 .iter_mut()
848 .zip(segment_merkle_tree.all_proofs())
849 .for_each(|(piece, record_proof)| {
850 piece.header = PieceHeader {
851 shard_index: self.shard_index.into(),
852 local_segment_index: segment_header.index,
853 segment_root: segment_header.root,
854 record_proof: RecordProof::from(record_proof),
855 ..piece.header
856 };
857 });
858
859 self.segment_index += LocalSegmentIndex::ONE;
861 self.prev_segment_header_hash = segment_header.hash();
862
863 self.buffer
866 .push_front(SegmentItem::ParentSegmentHeader(segment_header));
867
868 NewArchivedSegment {
869 segment_header,
870 pieces: pieces.to_shared(),
871 }
872 }
873}