Skip to main content

ab_archiving/
archiver.rs

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/// The segment represents a collection of items stored in archival history
47#[derive(Debug, Default, Clone, Eq, PartialEq)]
48pub struct Segment {
49    /// Segment items
50    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                    // Processing continues below
84                }
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/// Similar to `Vec<u8>`, but when encoded with SCALE codec uses fixed size length encoding (as
107/// little-endian `u32`)
108#[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        // TODO: It is inefficient to zero it, but there is no API for it right now and actually
157        //  implementation in `parity-scale-codec` itself is unsound:
158        //  https://github.com/paritytech/parity-scale-codec/pull/605#discussion_r2076151291
159        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/// Kinds of items that are contained within a segment
173#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode)]
174pub enum SegmentItem {
175    /// Special dummy enum variant only used as an implementation detail for padding purposes
176    #[codec(index = 0)]
177    Padding,
178    /// Contains a full block inside
179    #[codec(index = 1)]
180    Block {
181        /// Block bytes
182        bytes: BlockBytes,
183        /// This is a convenience implementation detail and will not be available on decoding
184        #[doc(hidden)]
185        #[codec(skip)]
186        block_objects: Vec<BlockObject>,
187    },
188    /// Contains the beginning of the block inside, the remainder will be found in the following
189    /// segments
190    #[codec(index = 2)]
191    BlockStart {
192        /// Block bytes
193        bytes: BlockBytes,
194        /// This is a convenience implementation detail and will not be available on decoding
195        #[doc(hidden)]
196        #[codec(skip)]
197        block_objects: Vec<BlockObject>,
198    },
199    /// Continuation of the partial block spilled over into the next segment
200    #[codec(index = 3)]
201    BlockContinuation {
202        /// Block bytes
203        bytes: BlockBytes,
204        /// This is a convenience implementation detail and will not be available on decoding
205        #[doc(hidden)]
206        #[codec(skip)]
207        block_objects: Vec<BlockObject>,
208    },
209    /// Segment header of the parent
210    #[codec(index = 4)]
211    ParentSegmentHeader(SegmentHeader),
212}
213
214/// Newly archived segment as a combination of a segment header and corresponding archived history
215/// segment containing pieces
216#[derive(Debug, Clone, Eq, PartialEq)]
217pub struct NewArchivedSegment {
218    /// Segment header
219    pub segment_header: SegmentHeader,
220    /// Segment of archived history containing pieces
221    pub pieces: ArchivedHistorySegment,
222}
223
224/// The outcome of adding a block to the archiver.
225#[derive(Debug, Clone, Eq, PartialEq)]
226pub struct ArchiveBlockOutcome {
227    /// The new segments archived after adding the block.
228    /// There can be zero or more segments created after each block.
229    pub archived_segments: Vec<NewArchivedSegment>,
230
231    // TODO: The API for global objects needs to change and be associated with segments rather than
232    //  blocks
233    /// The new object mappings for those segments.
234    /// There can be zero or more mappings created after each block.
235    pub global_objects: Vec<GlobalObject>,
236}
237
238/// Archiver instantiation error
239#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
240pub enum ArchiverInstantiationError {
241    /// Invalid last archived block, its size is the same as the encoded block
242    /// (so it should have been completely archived, not partially archived)
243    #[error("Invalid last archived block, its size {0} bytes is the same as the encoded block")]
244    InvalidLastArchivedBlock(u32),
245    /// Invalid block, its size is smaller than the already archived number of bytes
246    #[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        /// Full block size
252        block_bytes: u32,
253        /// Already archived portion of the block
254        archived_block_bytes: u32,
255    },
256    /// Invalid block, its size is larger than [`u32::MAX`] bytes
257    #[error("Invalid block, its size {block_bytes} bytes is larger than `u32::MAX`")]
258    InvalidBlockLargeSize {
259        /// Full block size
260        block_bytes: usize,
261    },
262}
263
264/// Size of the smallest segment item with data: 1 byte for enum variant, 4 bytes for length and 1
265/// for the actual data.
266///
267/// A segment is complete when less than this is left in it. Segment header item is never the last
268/// one, so a block that is split always has some of its data before the split point.
269const MIN_DATA_ITEM_SIZE: usize = 6;
270
271/// Buffer of segment items that keeps track of their total encoded size and which items had their
272/// object mappings produced already
273#[derive(Debug, Default, Clone)]
274struct SegmentItemsBuffer {
275    items: VecDeque<SegmentItem>,
276    /// Total encoded size of all items
277    encoded_size: usize,
278    /// Number of items at the beginning of the buffer that had their object mappings produced
279    num_mapped_items: usize,
280    /// Total encoded size of items that had their object mappings produced
281    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        // New item may have object mappings and shifts offsets of all other items, so all items are
298        // considered unmapped (items that were mapped before have no block objects left anyway)
299        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    /// Returns items that didn't have their object mappings produced yet along with the offset of
321    /// the first of them from the beginning of the buffer.
322    ///
323    /// Returned items are considered mapped afterwards, so object mappings must be produced for
324    /// them. Only fields that are not encoded (like block objects) may be modified, otherwise the
325    /// total encoded size will be wrong.
326    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/// Block archiver.
338///
339/// It takes new confirmed (at `K` depth) blocks and concatenates them into a buffer, buffer is
340/// sliced into segments of [`RecordedHistorySegment::SIZE`] size, segments are sliced into source
341/// records of [`Record::SIZE`], records are erasure coded, committed to, then roots with proofs are
342/// appended and records become pieces that are returned alongside the corresponding segment header.
343#[derive(Debug, Clone)]
344pub struct Archiver {
345    shard_index: ShardIndex,
346    /// Buffer containing blocks and other buffered items that are pending to be included into the
347    /// next segment
348    buffer: SegmentItemsBuffer,
349    /// Erasure coding data structure
350    erasure_coding: ErasureCoding,
351    /// An index of the current segment
352    segment_index: LocalSegmentIndex,
353    /// Hash of the segment header of the previous segment
354    prev_segment_header_hash: Blake3Hash,
355    /// Last archived block
356    last_archived_block: Option<LastArchivedBlock>,
357}
358
359impl Archiver {
360    /// Create a new instance
361    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    /// Create a new instance of the archiver with the initial state in case of restart.
373    ///
374    /// `block` corresponds to `last_archived_block` and will be processed according to its state.
375    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        // The first thing in the buffer should be segment header
389        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                    // Take part of the encoded block that wasn't archived yet and push to the
419                    // buffer as a block continuation
420                    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    /// Get the last archived block if there was any
442    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    /// Adds a new block to the internal buffer, potentially producing pieces, segment headers, and
448    /// object mappings.
449    ///
450    /// NOTE:
451    /// * pieces inside [`NewArchivedSegment`] are shared initially for efficient memory reuse when
452    ///   working with pieces
453    /// * pieces do not have a segment position and a segment proof filled in because it is not yet
454    ///   known at the time of local segment creation
455    ///
456    /// Returns `None` if the block is larger than [`u32::MAX`] bytes.
457    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        // Append new block to the buffer
467        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        // Add completed segments and their mappings for this block
476        while let Some(mut segment) = self.produce_segment() {
477            // Produce any segment mappings that haven't already been produced.
478            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        // Produce any next segment buffer mappings that haven't already been produced.
486        object_mapping.extend(self.produce_next_segment_mappings());
487
488        Some(ArchiveBlockOutcome {
489            archived_segments,
490            global_objects: object_mapping,
491        })
492    }
493
494    /// Try to slice buffer contents into segments if there is enough data, producing one segment at
495    /// a time
496    fn produce_segment(&mut self) -> Option<Segment> {
497        let mut segment_size = Segment::default().encoded_size();
498
499        // Not enough data for a segment yet
500        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            // Check if there is an excess of data that should be spilled over into the next segment
521            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                            // Increase the archived block number and assume the whole block was
534                            // archived (spill over checked below)
535                            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                            // Genesis block
542                            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                            // Push a continuation element back into the buffer where the removed
570                            // segment item was
571                            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                        // Update last archived block to include partial archiving info
579                        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                            // Push a continuation element back into the buffer where the removed
639                            // segment item was
640                            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                        // Update last archived block to include partial archiving info
648                        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                    // We are not interested in segment header here
667                    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    /// Produce object mappings for the buffered items for the next segment. Then remove the
680    /// mappings in those items.
681    ///
682    /// Must only be called after all complete segments for a block have been produced. Before
683    /// that, the buffer can contain a `BlockContinuation` which spans multiple segments.
684    fn produce_next_segment_mappings(&mut self) -> Vec<GlobalObject> {
685        // Usually this is just the block that was just added, avoiding iteration over the whole
686        // buffer is important for performance when there are many small blocks
687        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    /// Produce object mappings for `items` in `segment_index`, where the first item starts at
693    /// `base_offset_in_segment`. Then remove the mappings from those items.
694    ///
695    /// This method can be called on a `Segment`’s items, or on the `Archiver`'s internal buffer.
696    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                        // `+1` corresponds to `SegmentItem::X {}` enum variant encoding
725                        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                    // Ignore, no object mappings here
743                }
744            }
745
746            base_offset_in_segment += segment_item.encoded_size();
747        }
748
749        corrected_object_mapping
750    }
751
752    /// Take a segment as an input, apply necessary transformations, and produce an archived segment
753    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            // Segment is quite big and no longer necessary
762            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        // Collect hashes to roots from all records
782        let mut record_roots = [[0; _]; ArchivedHistorySegment::NUM_PIECES];
783        {
784            // Here we build a tree of record chunks, with the first half being source chunks as
785            // they are originally and the second half being parity chunks. While we build tree
786            // threes here (for source chunks, parity chunks and combined for the whole record), it
787            // could have been a single tree, and it would end up with the same root. Building them
788            // separately requires less RAM and allows capturing parity chunks root more easily.
789            //
790            // `parity_chunks` is only used for deriving a root and is fully overwritten by erasure
791            // coding, so it is reused between pieces.
792            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        // Now produce segment header
836        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        // Create proof for every record and write it to corresponding piece.
846        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        // Update state
860        self.segment_index += LocalSegmentIndex::ONE;
861        self.prev_segment_header_hash = segment_header.hash();
862
863        // Add segment header to the beginning of the buffer to be the first thing included in the
864        // next segment
865        self.buffer
866            .push_front(SegmentItem::ParentSegmentHeader(segment_header));
867
868        NewArchivedSegment {
869            segment_header,
870            pieces: pieces.to_shared(),
871        }
872    }
873}