Skip to main content

ab_core_primitives/
segments.rs

1//! Segments-related data structures
2
3#[cfg(feature = "alloc")]
4mod archival_history_segment;
5
6use crate::block::BlockNumber;
7use crate::hashes::Blake3Hash;
8use crate::pieces::{PieceIndex, Record, SegmentProof};
9#[cfg(feature = "alloc")]
10pub use crate::segments::archival_history_segment::ArchivedHistorySegment;
11use crate::shard::ShardIndex;
12use ab_blake3::{single_block_hash, single_chunk_hash};
13use ab_io_type::trivial_type::TrivialType;
14use ab_io_type::unaligned::Unaligned;
15use ab_merkle_tree::unbalanced::UnbalancedMerkleTree;
16#[cfg(feature = "alloc")]
17use alloc::boxed::Box;
18#[cfg(feature = "alloc")]
19use alloc::sync::Arc as StdArc;
20use blake3::{CHUNK_LEN, OUT_LEN};
21use core::fmt;
22use core::iter::Step;
23#[cfg(feature = "alloc")]
24use core::mem::MaybeUninit;
25use core::num::{NonZeroU32, NonZeroU64};
26use derive_more::{
27    Add, AddAssign, Deref, DerefMut, Display, Div, DivAssign, From, Into, Mul, MulAssign, Sub,
28    SubAssign,
29};
30#[cfg(feature = "scale-codec")]
31use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
32#[cfg(feature = "serde")]
33use serde::{Deserialize, Deserializer, Serialize, Serializer};
34#[cfg(feature = "serde")]
35use serde_big_array::BigArray;
36use transparent_wrapper::TransparentWrapper;
37
38/// Super segment index
39#[derive(
40    Debug,
41    Display,
42    Default,
43    Copy,
44    Clone,
45    Ord,
46    PartialOrd,
47    Eq,
48    PartialEq,
49    Hash,
50    Add,
51    AddAssign,
52    Sub,
53    SubAssign,
54    Mul,
55    MulAssign,
56    Div,
57    DivAssign,
58    TrivialType,
59)]
60#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
61#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
62#[repr(C)]
63pub struct SuperSegmentIndex(u64);
64
65impl Step for SuperSegmentIndex {
66    #[inline]
67    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
68        u64::steps_between(&start.0, &end.0)
69    }
70
71    #[inline]
72    fn forward_checked(start: Self, count: usize) -> Option<Self> {
73        u64::forward_checked(start.0, count).map(Self)
74    }
75
76    #[inline(always)]
77    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
78        let (n, overflowing) = u64::forward_overflowing(start.0, count);
79        (Self(n), overflowing)
80    }
81
82    #[inline]
83    fn backward_checked(start: Self, count: usize) -> Option<Self> {
84        u64::backward_checked(start.0, count).map(Self)
85    }
86
87    #[inline(always)]
88    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
89        let (n, overflowing) = u64::backward_overflowing(start.0, count);
90        (Self(n), overflowing)
91    }
92}
93
94const impl From<u64> for SuperSegmentIndex {
95    #[inline(always)]
96    fn from(value: u64) -> Self {
97        Self(value)
98    }
99}
100
101const impl From<SuperSegmentIndex> for u64 {
102    #[inline(always)]
103    fn from(value: SuperSegmentIndex) -> Self {
104        value.0
105    }
106}
107
108impl SuperSegmentIndex {
109    /// Super segment index 0
110    pub const ZERO: Self = Self(0);
111    /// Super segment index 1
112    pub const ONE: Self = Self(1);
113
114    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if underflow occurred
115    #[inline]
116    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
117        self.0.checked_sub(rhs.0).map(Self)
118    }
119
120    /// Saturating integer subtraction. Computes `self - rhs`, returning zero if underflow
121    /// occurred
122    #[inline]
123    pub const fn saturating_sub(self, rhs: Self) -> Self {
124        Self(self.0.saturating_sub(rhs.0))
125    }
126}
127
128/// Super segment root contained within a beacon chain block
129#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
130#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
131#[repr(C)]
132pub struct SuperSegmentRoot([u8; SuperSegmentRoot::SIZE]);
133
134impl fmt::Debug for SuperSegmentRoot {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        for byte in self.0 {
137            write!(f, "{byte:02x}")?;
138        }
139        Ok(())
140    }
141}
142
143const impl Default for SuperSegmentRoot {
144    #[inline]
145    fn default() -> Self {
146        Self([0; _])
147    }
148}
149
150#[cfg(feature = "serde")]
151#[derive(Serialize, Deserialize)]
152#[serde(transparent)]
153struct SuperSegmentRootBinary(#[serde(with = "BigArray")] [u8; SuperSegmentRoot::SIZE]);
154
155#[cfg(feature = "serde")]
156#[derive(Serialize, Deserialize)]
157#[serde(transparent)]
158struct SuperSegmentRootHex(#[serde(with = "hex")] [u8; SuperSegmentRoot::SIZE]);
159
160#[cfg(feature = "serde")]
161impl Serialize for SuperSegmentRoot {
162    #[inline]
163    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164    where
165        S: Serializer,
166    {
167        if serializer.is_human_readable() {
168            SuperSegmentRootHex(self.0).serialize(serializer)
169        } else {
170            SuperSegmentRootBinary(self.0).serialize(serializer)
171        }
172    }
173}
174
175#[cfg(feature = "serde")]
176impl<'de> Deserialize<'de> for SuperSegmentRoot {
177    #[inline]
178    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179    where
180        D: Deserializer<'de>,
181    {
182        Ok(Self(if deserializer.is_human_readable() {
183            SuperSegmentRootHex::deserialize(deserializer)?.0
184        } else {
185            SuperSegmentRootBinary::deserialize(deserializer)?.0
186        }))
187    }
188}
189
190impl AsRef<[u8]> for SuperSegmentRoot {
191    #[inline]
192    fn as_ref(&self) -> &[u8] {
193        &self.0
194    }
195}
196
197impl AsMut<[u8]> for SuperSegmentRoot {
198    #[inline]
199    fn as_mut(&mut self) -> &mut [u8] {
200        &mut self.0
201    }
202}
203
204impl SuperSegmentRoot {
205    /// Size in bytes
206    pub const SIZE: usize = 32;
207    /// The maximum number of segments in a super segment's Merkle Tree.
208    ///
209    /// `-1` to minimize the number of bits needed to represent it (exactly 20).
210    pub const MAX_SEGMENTS: u32 = 2u32.pow(20) - 1;
211}
212
213/// Segment position in a super segment
214#[derive(
215    Debug,
216    Display,
217    Default,
218    Copy,
219    Clone,
220    Ord,
221    PartialOrd,
222    Eq,
223    PartialEq,
224    Hash,
225    From,
226    Into,
227    TrivialType,
228)]
229#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
230#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
231#[repr(C)]
232pub struct SegmentPosition(u32);
233
234impl From<SegmentPosition> for u64 {
235    #[inline]
236    fn from(original: SegmentPosition) -> Self {
237        Self::from(original.0)
238    }
239}
240
241impl SegmentPosition {
242    /// Zero position
243    pub const ZERO: Self = Self(0);
244}
245
246/// Shard segment root with position
247#[derive(Debug, Clone, Copy, TrivialType)]
248#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
249#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
250#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
251#[repr(C)]
252pub struct ShardSegmentRootWithPosition {
253    /// Shard index
254    pub shard_index: ShardIndex,
255    /// Position of the segment in the super segment
256    pub segment_position: SegmentPosition,
257    /// Local segment index
258    pub local_segment_index: LocalSegmentIndex,
259    /// Segment root
260    pub segment_root: SegmentRoot,
261}
262
263impl ShardSegmentRootWithPosition {
264    /// Hash for super segment creation
265    #[inline(always)]
266    pub fn hash(&self) -> [u8; OUT_LEN] {
267        single_block_hash(self.as_bytes()).expect("Less than a single block worth of bytes; qed")
268    }
269}
270
271/// Super segment header
272#[derive(Debug, Clone, Copy, Eq, PartialEq, TrivialType)]
273#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
274#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
275#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
276#[repr(C)]
277pub struct SuperSegmentHeader {
278    /// Super segment index
279    pub index: Unaligned<SuperSegmentIndex>,
280    /// Super segment root
281    pub root: SuperSegmentRoot,
282    /// Hash of the previous super segment header
283    pub prev_super_segment_header_hash: Blake3Hash,
284    /// Max index of the segment in the super segment
285    pub max_segment_index: Unaligned<SegmentIndex>,
286    /// Target beacon chain block number for the super segment
287    pub target_beacon_chain_block_number: Unaligned<BlockNumber>,
288    // TODO: New type?
289    /// Number of segments in the super segment
290    pub num_segments: u32,
291}
292
293/// Super segment
294#[cfg(feature = "alloc")]
295#[derive(Debug, Clone)]
296// TODO: Implement SCALE serialization/deserialization manually (if necessary at all)
297// #[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
298#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
299#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
300pub struct SuperSegment {
301    /// Super segment root
302    pub header: SuperSegmentHeader,
303    /// Segment roots that are included in the super segment
304    pub segment_roots: StdArc<[ShardSegmentRootWithPosition]>,
305}
306
307#[cfg(feature = "alloc")]
308impl SuperSegment {
309    /// Create a new instance and derive super segment root.
310    ///
311    /// Returns `None` if the list of segment roots is empty or there are too many of them.
312    pub fn new(
313        previous_header: &SuperSegmentHeader,
314        target_beacon_chain_block_number: BlockNumber,
315        segment_roots: StdArc<[ShardSegmentRootWithPosition]>,
316    ) -> Option<Self> {
317        let num_segments = u32::try_from(segment_roots.len()).ok()?;
318        let max_segment_index = SegmentIndex::from(
319            u64::from(previous_header.max_segment_index.as_inner()) + u64::from(num_segments),
320        );
321
322        // TODO: Keyed hash
323        let maybe_super_segment_root =
324            UnbalancedMerkleTree::compute_root_only::<
325                { u64::from(SuperSegmentRoot::MAX_SEGMENTS) },
326                _,
327                _,
328            >(segment_roots.iter().map(ShardSegmentRootWithPosition::hash))?;
329
330        Some(Self {
331            header: SuperSegmentHeader {
332                index: (previous_header.index.as_inner() + SuperSegmentIndex::ONE).into(),
333                root: SuperSegmentRoot::from(maybe_super_segment_root),
334                prev_super_segment_header_hash: Blake3Hash::from(
335                    single_chunk_hash(previous_header.as_bytes())
336                        .expect("Less than a single chunk worth of bytes; qed"),
337                ),
338                max_segment_index: max_segment_index.into(),
339                target_beacon_chain_block_number: target_beacon_chain_block_number.into(),
340                num_segments,
341            },
342            segment_roots,
343        })
344    }
345
346    /// Produce a proof for a segment in the super segment at a given position
347    pub fn proof_for_segment(&self, segment_position: SegmentPosition) -> Option<SegmentProof> {
348        // TODO: Keyed hash
349        let mut segment_proof = [MaybeUninit::uninit(); _];
350        let (_root, proof_hashes) = UnbalancedMerkleTree::compute_root_and_proof_in::<
351            { u64::from(SuperSegmentRoot::MAX_SEGMENTS) },
352            _,
353            _,
354        >(
355            self.segment_roots.iter().map(|shard_segment_root| {
356                single_block_hash(shard_segment_root.as_bytes())
357                    .expect("Less than a single block worth of bytes; qed")
358            }),
359            u32::from(segment_position) as usize,
360            &mut segment_proof,
361        )?;
362        let proof_length = proof_hashes.len();
363
364        // Unused hashes must be zeroed
365        segment_proof
366            .get_mut(proof_length..)
367            .expect("Proof is a prefix of the provided memory; qed")
368            .write_filled([0; OUT_LEN]);
369        // SAFETY: The first `proof_length` hashes were initialized by proof generation above, the
370        // rest were just zeroed
371        let segment_proof = unsafe { MaybeUninit::from(segment_proof).assume_init() };
372
373        Some(SegmentProof::from(segment_proof))
374    }
375}
376
377/// Local segment index of a shard
378#[derive(
379    Debug,
380    Display,
381    Default,
382    Copy,
383    Clone,
384    Ord,
385    PartialOrd,
386    Eq,
387    PartialEq,
388    Hash,
389    Add,
390    AddAssign,
391    Sub,
392    SubAssign,
393    Mul,
394    MulAssign,
395    Div,
396    DivAssign,
397    TrivialType,
398)]
399#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
400#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
401#[repr(C)]
402pub struct LocalSegmentIndex(u64);
403
404impl Step for LocalSegmentIndex {
405    #[inline]
406    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
407        u64::steps_between(&start.0, &end.0)
408    }
409
410    #[inline]
411    fn forward_checked(start: Self, count: usize) -> Option<Self> {
412        u64::forward_checked(start.0, count).map(Self)
413    }
414
415    #[inline(always)]
416    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
417        let (n, overflowing) = u64::forward_overflowing(start.0, count);
418        (Self(n), overflowing)
419    }
420
421    #[inline]
422    fn backward_checked(start: Self, count: usize) -> Option<Self> {
423        u64::backward_checked(start.0, count).map(Self)
424    }
425
426    #[inline(always)]
427    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
428        let (n, overflowing) = u64::backward_overflowing(start.0, count);
429        (Self(n), overflowing)
430    }
431}
432
433const impl From<u64> for LocalSegmentIndex {
434    #[inline(always)]
435    fn from(value: u64) -> Self {
436        Self(value)
437    }
438}
439
440const impl From<LocalSegmentIndex> for u64 {
441    #[inline(always)]
442    fn from(value: LocalSegmentIndex) -> Self {
443        value.0
444    }
445}
446
447impl LocalSegmentIndex {
448    /// Local segment index 0
449    pub const ZERO: Self = Self(0);
450    /// Local segment index 1
451    pub const ONE: Self = Self(1);
452
453    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if underflow occurred
454    #[inline]
455    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
456        self.0.checked_sub(rhs.0).map(Self)
457    }
458
459    /// Saturating integer subtraction. Computes `self - rhs`, returning zero if underflow
460    /// occurred
461    #[inline]
462    pub const fn saturating_sub(self, rhs: Self) -> Self {
463        Self(self.0.saturating_sub(rhs.0))
464    }
465}
466
467/// Segment index
468#[derive(
469    Debug,
470    Display,
471    Default,
472    Copy,
473    Clone,
474    Ord,
475    PartialOrd,
476    Eq,
477    PartialEq,
478    Hash,
479    Add,
480    AddAssign,
481    Sub,
482    SubAssign,
483    Mul,
484    MulAssign,
485    Div,
486    DivAssign,
487    TrivialType,
488)]
489#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
490#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
491#[repr(C)]
492pub struct SegmentIndex(u64);
493
494impl Step for SegmentIndex {
495    #[inline]
496    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
497        u64::steps_between(&start.0, &end.0)
498    }
499
500    #[inline]
501    fn forward_checked(start: Self, count: usize) -> Option<Self> {
502        u64::forward_checked(start.0, count).map(Self)
503    }
504
505    #[inline(always)]
506    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
507        let (n, overflowing) = u64::forward_overflowing(start.0, count);
508        (Self(n), overflowing)
509    }
510
511    #[inline]
512    fn backward_checked(start: Self, count: usize) -> Option<Self> {
513        u64::backward_checked(start.0, count).map(Self)
514    }
515
516    #[inline(always)]
517    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
518        let (n, overflowing) = u64::backward_overflowing(start.0, count);
519        (Self(n), overflowing)
520    }
521}
522
523const impl From<u64> for SegmentIndex {
524    #[inline(always)]
525    fn from(value: u64) -> Self {
526        Self(value)
527    }
528}
529
530const impl From<SegmentIndex> for u64 {
531    #[inline(always)]
532    fn from(value: SegmentIndex) -> Self {
533        value.0
534    }
535}
536
537impl SegmentIndex {
538    /// Segment index 0
539    pub const ZERO: Self = Self(0);
540    /// Segment index 1
541    pub const ONE: Self = Self(1);
542
543    /// Get the first piece index in this segment
544    #[inline]
545    pub const fn first_piece_index(&self) -> PieceIndex {
546        PieceIndex::from(self.0 * RecordedHistorySegment::NUM_PIECES as u64)
547    }
548
549    /// Get the last piece index in this segment
550    #[inline]
551    pub const fn last_piece_index(&self) -> PieceIndex {
552        PieceIndex::from((self.0 + 1) * RecordedHistorySegment::NUM_PIECES as u64 - 1)
553    }
554
555    /// List of piece indexes that belong to this segment
556    #[inline]
557    pub fn segment_piece_indexes(&self) -> [PieceIndex; RecordedHistorySegment::NUM_PIECES] {
558        let mut piece_indices = [PieceIndex::ZERO; RecordedHistorySegment::NUM_PIECES];
559        (self.first_piece_index()..=self.last_piece_index())
560            .zip(&mut piece_indices)
561            .for_each(|(input, output)| {
562                *output = input;
563            });
564
565        piece_indices
566    }
567
568    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if underflow occurred
569    #[inline]
570    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
571        self.0.checked_sub(rhs.0).map(Self)
572    }
573
574    /// Saturating integer subtraction. Computes `self - rhs`, returning zero if underflow
575    /// occurred
576    #[inline]
577    pub const fn saturating_sub(self, rhs: Self) -> Self {
578        Self(self.0.saturating_sub(rhs.0))
579    }
580}
581
582/// Segment root contained within a segment
583#[derive(
584    Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType, TransparentWrapper,
585)]
586#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
587#[repr(C)]
588pub struct SegmentRoot([u8; SegmentRoot::SIZE]);
589
590impl fmt::Debug for SegmentRoot {
591    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592        for byte in self.0 {
593            write!(f, "{byte:02x}")?;
594        }
595        Ok(())
596    }
597}
598
599#[cfg(feature = "serde")]
600#[derive(Serialize, Deserialize)]
601#[serde(transparent)]
602struct SegmentRootBinary(#[serde(with = "BigArray")] [u8; SegmentRoot::SIZE]);
603
604#[cfg(feature = "serde")]
605#[derive(Serialize, Deserialize)]
606#[serde(transparent)]
607struct SegmentRootHex(#[serde(with = "hex")] [u8; SegmentRoot::SIZE]);
608
609#[cfg(feature = "serde")]
610impl Serialize for SegmentRoot {
611    #[inline]
612    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
613    where
614        S: Serializer,
615    {
616        if serializer.is_human_readable() {
617            SegmentRootHex(self.0).serialize(serializer)
618        } else {
619            SegmentRootBinary(self.0).serialize(serializer)
620        }
621    }
622}
623
624#[cfg(feature = "serde")]
625impl<'de> Deserialize<'de> for SegmentRoot {
626    #[inline]
627    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
628    where
629        D: Deserializer<'de>,
630    {
631        Ok(Self(if deserializer.is_human_readable() {
632            SegmentRootHex::deserialize(deserializer)?.0
633        } else {
634            SegmentRootBinary::deserialize(deserializer)?.0
635        }))
636    }
637}
638
639impl Default for SegmentRoot {
640    #[inline(always)]
641    fn default() -> Self {
642        Self([0; _])
643    }
644}
645
646impl AsRef<[u8]> for SegmentRoot {
647    #[inline(always)]
648    fn as_ref(&self) -> &[u8] {
649        &self.0
650    }
651}
652
653impl AsMut<[u8]> for SegmentRoot {
654    #[inline(always)]
655    fn as_mut(&mut self) -> &mut [u8] {
656        &mut self.0
657    }
658}
659
660impl SegmentRoot {
661    /// Size in bytes
662    pub const SIZE: usize = 32;
663
664    /// Convenient conversion from a slice of underlying representation for efficiency purposes
665    #[inline(always)]
666    pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
667        Self::wrap_slice(value)
668    }
669
670    /// Convenient conversion to a slice of underlying representation for efficiency purposes
671    #[inline(always)]
672    pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
673        Self::peel_slice(value)
674    }
675
676    /// Check whether a segment root is a part of the super segment
677    pub fn is_valid(
678        &self,
679        shard_index: ShardIndex,
680        local_segment_index: LocalSegmentIndex,
681        segment_position: SegmentPosition,
682        segment_proof: &SegmentProof,
683        num_segments: u32,
684        super_segment_root: &SuperSegmentRoot,
685    ) -> bool {
686        let shard_segment_root = ShardSegmentRootWithPosition {
687            shard_index,
688            segment_position,
689            local_segment_index,
690            segment_root: *self,
691        };
692        // The proof is fixed size and contains zero padding elements, which must be skipped for
693        // verification purposes
694        let segment_proof = segment_proof
695            .split_once(|hash| hash == &[0; _])
696            .map_or(segment_proof.as_slice(), |(before, _after)| before);
697        UnbalancedMerkleTree::verify(
698            super_segment_root,
699            segment_proof,
700            u64::from(segment_position),
701            shard_segment_root.hash(),
702            u64::from(num_segments),
703        )
704    }
705}
706
707/// Size of blockchain history in segments
708#[derive(
709    Debug,
710    Display,
711    Copy,
712    Clone,
713    Ord,
714    PartialOrd,
715    Eq,
716    PartialEq,
717    Hash,
718    From,
719    Into,
720    Deref,
721    DerefMut,
722    TrivialType,
723)]
724#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
725#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
726#[repr(C)]
727// Storing `SegmentIndex` to make all invariants valid
728pub struct HistorySize(SegmentIndex);
729
730impl HistorySize {
731    /// History size of one
732    pub const ONE: Self = Self(SegmentIndex::ZERO);
733
734    /// Create a new instance
735    #[inline(always)]
736    pub const fn new(value: NonZeroU64) -> Self {
737        Self(SegmentIndex::from(value.get() - 1))
738    }
739
740    /// Get internal representation
741    pub const fn as_segment_index(&self) -> SegmentIndex {
742        self.0
743    }
744
745    /// Get internal representation
746    pub const fn as_non_zero_u64(&self) -> NonZeroU64 {
747        NonZeroU64::new(u64::from(self.0).saturating_add(1)).expect("Not zero; qed")
748    }
749
750    /// Size of blockchain history in pieces
751    #[inline(always)]
752    pub const fn in_pieces(&self) -> NonZeroU64 {
753        NonZeroU64::new(
754            u64::from(self.0)
755                .saturating_add(1)
756                .saturating_mul(RecordedHistorySegment::NUM_PIECES as u64),
757        )
758        .expect("Not zero; qed")
759    }
760
761    /// Segment index that corresponds to this history size
762    #[inline(always)]
763    pub fn segment_index(&self) -> SegmentIndex {
764        self.0
765    }
766
767    /// History size at which expiration check for a sector happens.
768    ///
769    /// Returns `None` on overflow.
770    #[inline(always)]
771    pub fn sector_expiration_check(&self, min_sector_lifetime: Self) -> Option<Self> {
772        self.as_non_zero_u64()
773            .checked_add(min_sector_lifetime.as_non_zero_u64().get())
774            .map(Self::new)
775    }
776}
777
778/// Progress of an archived block.
779#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, TrivialType)]
780#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
781#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
782#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
783#[repr(C)]
784pub struct ArchivedBlockProgress {
785    /// Number of partially archived bytes of a block, `0` for a full block
786    bytes: u32,
787}
788
789impl Default for ArchivedBlockProgress {
790    /// We assume a block can always fit into the segment initially, but it is definitely possible
791    /// to be transitioned into the partial state after some overflow checking.
792    #[inline(always)]
793    fn default() -> Self {
794        Self::new_complete()
795    }
796}
797
798impl ArchivedBlockProgress {
799    /// Block is archived fully
800    #[inline(always)]
801    pub const fn new_complete() -> Self {
802        Self { bytes: 0 }
803    }
804
805    /// Block is partially archived with the provided number of bytes
806    #[inline(always)]
807    pub const fn new_partial(new_partial: NonZeroU32) -> Self {
808        Self {
809            bytes: new_partial.get(),
810        }
811    }
812
813    /// Return the number of partially archived bytes if the progress is not complete
814    #[inline(always)]
815    pub const fn partial(&self) -> Option<NonZeroU32> {
816        NonZeroU32::new(self.bytes)
817    }
818}
819
820/// Last archived block
821#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, TrivialType)]
822#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
823#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
824#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
825#[repr(C)]
826pub struct LastArchivedBlock {
827    /// Block number
828    pub number: Unaligned<BlockNumber>,
829    /// Progress of an archived block
830    pub archived_progress: ArchivedBlockProgress,
831}
832
833impl LastArchivedBlock {
834    /// Returns the number of partially archived bytes for a block
835    #[inline(always)]
836    pub fn partial_archived(&self) -> Option<NonZeroU32> {
837        self.archived_progress.partial()
838    }
839
840    /// Sets the number of partially archived bytes if block progress was archived partially
841    #[inline(always)]
842    pub fn set_partial_archived(&mut self, new_partial: NonZeroU32) {
843        self.archived_progress = ArchivedBlockProgress::new_partial(new_partial);
844    }
845
846    /// Indicate the last archived block was archived fully
847    #[inline(always)]
848    pub fn set_complete(&mut self) {
849        self.archived_progress = ArchivedBlockProgress::new_complete();
850    }
851
852    /// Get the block number (unwrap `Unaligned`)
853    pub const fn number(&self) -> BlockNumber {
854        self.number.as_inner()
855    }
856}
857
858/// Segment header for a specific segment of a shard
859#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TrivialType)]
860#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
861#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
862#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
863#[repr(C)]
864pub struct SegmentHeader {
865    /// Local segment index
866    pub index: Unaligned<LocalSegmentIndex>,
867    /// Root of roots of all records in a segment.
868    pub root: SegmentRoot,
869    /// Hash of the segment header of the previous segment
870    pub prev_segment_header_hash: Blake3Hash,
871    /// Last archived block
872    pub last_archived_block: LastArchivedBlock,
873}
874
875impl SegmentHeader {
876    /// Hash of the whole segment header
877    #[inline(always)]
878    pub fn hash(&self) -> Blake3Hash {
879        const {
880            assert!(size_of::<Self>() <= CHUNK_LEN);
881        }
882        Blake3Hash::new(
883            single_chunk_hash(self.as_bytes())
884                .expect("Less than a single chunk worth of bytes; qed"),
885        )
886    }
887}
888
889/// Recorded history segment before archiving is applied.
890///
891/// NOTE: This is a stack-allocated data structure and can cause stack overflow!
892#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut)]
893#[repr(C)]
894pub struct RecordedHistorySegment([Record; Self::NUM_RAW_RECORDS]);
895
896impl fmt::Debug for RecordedHistorySegment {
897    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
898        f.debug_struct("RecordedHistorySegment")
899            .finish_non_exhaustive()
900    }
901}
902
903impl AsRef<[u8]> for RecordedHistorySegment {
904    #[inline]
905    fn as_ref(&self) -> &[u8] {
906        Record::slice_to_repr(&self.0).as_flattened().as_flattened()
907    }
908}
909
910impl AsMut<[u8]> for RecordedHistorySegment {
911    #[inline]
912    fn as_mut(&mut self) -> &mut [u8] {
913        Record::slice_mut_to_repr(&mut self.0)
914            .as_flattened_mut()
915            .as_flattened_mut()
916    }
917}
918
919impl RecordedHistorySegment {
920    /// Number of raw records in one segment of recorded history
921    pub const NUM_RAW_RECORDS: usize = 128;
922    /// Erasure coding rate for records during the archiving process
923    pub const ERASURE_CODING_RATE: (usize, usize) = (1, 2);
924    /// Number of pieces in one segment of archived history (taking erasure coding rate into
925    /// account)
926    pub const NUM_PIECES: usize =
927        Self::NUM_RAW_RECORDS * Self::ERASURE_CODING_RATE.1 / Self::ERASURE_CODING_RATE.0;
928    /// Size of recorded history segment in bytes.
929    ///
930    /// It includes half of the records (just source records) that will later be erasure coded and
931    /// together with corresponding roots and proofs will result in
932    /// [`Self::NUM_PIECES`] `Piece`s of archival history.
933    pub const SIZE: usize = Record::SIZE * Self::NUM_RAW_RECORDS;
934
935    /// Create boxed value without hitting stack overflow
936    #[inline]
937    #[cfg(feature = "alloc")]
938    pub fn new_boxed() -> Box<Self> {
939        // TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
940        // SAFETY: Data structure filled with zeroes is a valid invariant
941        unsafe { Box::<Self>::new_zeroed().assume_init() }
942    }
943}