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