Skip to main content

ab_core_primitives/
segments.rs

1//! Segments-related data structures
2
3#[cfg(feature = "alloc")]
4mod archival_history_segment;
5#[cfg(all(test, feature = "alloc"))]
6mod tests;
7
8use crate::block::BlockNumber;
9use crate::hashes::Blake3Hash;
10use crate::pieces::{PieceIndex, Record, SegmentProof};
11#[cfg(feature = "alloc")]
12pub use crate::segments::archival_history_segment::ArchivedHistorySegment;
13use crate::shard::ShardIndex;
14use crate::trees::SuperSegmentMerkleTree;
15use ab_blake3::{single_block_hash, single_chunk_hash};
16use ab_io_type::trivial_type::TrivialType;
17use ab_io_type::unaligned::Unaligned;
18#[cfg(feature = "alloc")]
19use alloc::boxed::Box;
20#[cfg(feature = "alloc")]
21use alloc::sync::Arc as StdArc;
22use blake3::{CHUNK_LEN, OUT_LEN};
23use core::fmt;
24use core::iter::Step;
25#[cfg(feature = "alloc")]
26use core::mem::MaybeUninit;
27use core::num::{NonZeroU32, NonZeroU64};
28use derive_more::{
29    Add, AddAssign, Deref, DerefMut, Display, Div, DivAssign, From, Into, Mul, MulAssign, Sub,
30    SubAssign,
31};
32#[cfg(feature = "scale-codec")]
33use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
34#[cfg(feature = "serde")]
35use serde::{Deserialize, Deserializer, Serialize, Serializer};
36#[cfg(feature = "serde")]
37use serde_big_array::BigArray;
38use transparent_wrapper::TransparentWrapper;
39
40/// Super segment index
41#[derive(
42    Debug,
43    Display,
44    Default,
45    Copy,
46    Clone,
47    Ord,
48    PartialOrd,
49    Eq,
50    PartialEq,
51    Hash,
52    Add,
53    AddAssign,
54    Sub,
55    SubAssign,
56    Mul,
57    MulAssign,
58    Div,
59    DivAssign,
60    TrivialType,
61)]
62#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64#[repr(C)]
65pub struct SuperSegmentIndex(u64);
66
67impl Step for SuperSegmentIndex {
68    #[inline]
69    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
70        u64::steps_between(&start.0, &end.0)
71    }
72
73    #[inline]
74    fn forward_checked(start: Self, count: usize) -> Option<Self> {
75        u64::forward_checked(start.0, count).map(Self)
76    }
77
78    #[inline(always)]
79    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
80        let (n, overflowing) = u64::forward_overflowing(start.0, count);
81        (Self(n), overflowing)
82    }
83
84    #[inline]
85    fn backward_checked(start: Self, count: usize) -> Option<Self> {
86        u64::backward_checked(start.0, count).map(Self)
87    }
88
89    #[inline(always)]
90    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
91        let (n, overflowing) = u64::backward_overflowing(start.0, count);
92        (Self(n), overflowing)
93    }
94}
95
96const impl From<u64> for SuperSegmentIndex {
97    #[inline(always)]
98    fn from(value: u64) -> Self {
99        Self(value)
100    }
101}
102
103const impl From<SuperSegmentIndex> for u64 {
104    #[inline(always)]
105    fn from(value: SuperSegmentIndex) -> Self {
106        value.0
107    }
108}
109
110impl SuperSegmentIndex {
111    /// Super segment index 0
112    pub const ZERO: Self = Self(0);
113    /// Super segment index 1
114    pub const ONE: Self = Self(1);
115
116    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if underflow occurred
117    #[inline]
118    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
119        self.0.checked_sub(rhs.0).map(Self)
120    }
121
122    /// Saturating integer subtraction. Computes `self - rhs`, returning zero if underflow
123    /// occurred
124    #[inline]
125    pub const fn saturating_sub(self, rhs: Self) -> Self {
126        Self(self.0.saturating_sub(rhs.0))
127    }
128}
129
130/// Super segment root contained within a beacon chain block
131#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
132#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
133#[repr(C)]
134pub struct SuperSegmentRoot([u8; SuperSegmentRoot::SIZE]);
135
136impl fmt::Debug for SuperSegmentRoot {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        for byte in self.0 {
139            write!(f, "{byte:02x}")?;
140        }
141        Ok(())
142    }
143}
144
145const impl Default for SuperSegmentRoot {
146    #[inline]
147    fn default() -> Self {
148        Self([0; _])
149    }
150}
151
152#[cfg(feature = "serde")]
153#[derive(Serialize, Deserialize)]
154#[serde(transparent)]
155struct SuperSegmentRootBinary(#[serde(with = "BigArray")] [u8; SuperSegmentRoot::SIZE]);
156
157#[cfg(feature = "serde")]
158#[derive(Serialize, Deserialize)]
159#[serde(transparent)]
160struct SuperSegmentRootHex(#[serde(with = "hex")] [u8; SuperSegmentRoot::SIZE]);
161
162#[cfg(feature = "serde")]
163impl Serialize for SuperSegmentRoot {
164    #[inline]
165    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
166    where
167        S: Serializer,
168    {
169        if serializer.is_human_readable() {
170            SuperSegmentRootHex(self.0).serialize(serializer)
171        } else {
172            SuperSegmentRootBinary(self.0).serialize(serializer)
173        }
174    }
175}
176
177#[cfg(feature = "serde")]
178impl<'de> Deserialize<'de> for SuperSegmentRoot {
179    #[inline]
180    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181    where
182        D: Deserializer<'de>,
183    {
184        Ok(Self(if deserializer.is_human_readable() {
185            SuperSegmentRootHex::deserialize(deserializer)?.0
186        } else {
187            SuperSegmentRootBinary::deserialize(deserializer)?.0
188        }))
189    }
190}
191
192impl AsRef<[u8]> for SuperSegmentRoot {
193    #[inline]
194    fn as_ref(&self) -> &[u8] {
195        &self.0
196    }
197}
198
199impl AsMut<[u8]> for SuperSegmentRoot {
200    #[inline]
201    fn as_mut(&mut self) -> &mut [u8] {
202        &mut self.0
203    }
204}
205
206impl SuperSegmentRoot {
207    /// Size in bytes
208    pub const SIZE: usize = 32;
209    /// The maximum number of segments in a super segment's Merkle Tree.
210    ///
211    /// `-1` to minimize the number of bits needed to represent it (exactly 20).
212    pub const MAX_SEGMENTS: u32 = 2u32.pow(20) - 1;
213}
214
215/// Segment position in a super segment
216#[derive(
217    Debug,
218    Display,
219    Default,
220    Copy,
221    Clone,
222    Ord,
223    PartialOrd,
224    Eq,
225    PartialEq,
226    Hash,
227    From,
228    Into,
229    TrivialType,
230)]
231#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233#[repr(C)]
234pub struct SegmentPosition(u32);
235
236impl From<SegmentPosition> for u64 {
237    #[inline]
238    fn from(original: SegmentPosition) -> Self {
239        Self::from(original.0)
240    }
241}
242
243impl SegmentPosition {
244    /// Zero position
245    pub const ZERO: Self = Self(0);
246}
247
248/// Shard segment root with position
249#[derive(Debug, Clone, Copy, TrivialType)]
250#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
251#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
252#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
253#[repr(C)]
254pub struct ShardSegmentRootWithPosition {
255    /// Shard index
256    pub shard_index: ShardIndex,
257    /// Position of the segment in the super segment
258    pub segment_position: SegmentPosition,
259    /// Local segment index
260    pub local_segment_index: LocalSegmentIndex,
261    /// Segment root
262    pub segment_root: SegmentRoot,
263}
264
265impl ShardSegmentRootWithPosition {
266    /// Hash for super segment creation
267    #[inline(always)]
268    pub fn hash(&self) -> [u8; OUT_LEN] {
269        single_block_hash(self.as_bytes()).expect("Less than a single block worth of bytes; qed")
270    }
271}
272
273/// Super segment header
274#[derive(Debug, Clone, Copy, Eq, PartialEq, TrivialType)]
275#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
278#[repr(C)]
279pub struct SuperSegmentHeader {
280    /// Super segment index
281    pub index: Unaligned<SuperSegmentIndex>,
282    /// Super segment root
283    pub root: SuperSegmentRoot,
284    /// Hash of the previous super segment header
285    pub prev_super_segment_header_hash: Blake3Hash,
286    /// Max index of the segment in the super segment
287    pub max_segment_index: Unaligned<SegmentIndex>,
288    /// Target beacon chain block number for the super segment
289    pub target_beacon_chain_block_number: Unaligned<BlockNumber>,
290    // TODO: New type?
291    /// Number of segments in the super segment
292    pub num_segments: u32,
293}
294
295/// Super segment
296#[cfg(feature = "alloc")]
297#[derive(Debug, Clone)]
298// TODO: Implement SCALE serialization/deserialization manually (if necessary at all)
299// #[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
300#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
301#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
302pub struct SuperSegment {
303    /// Super segment root
304    pub header: SuperSegmentHeader,
305    /// Segment roots that are included in the super segment
306    pub segment_roots: StdArc<[ShardSegmentRootWithPosition]>,
307}
308
309#[cfg(feature = "alloc")]
310impl SuperSegment {
311    /// Create a new instance and derive super segment root.
312    ///
313    /// Returns `None` if the list of segment roots is empty or there are too many of them.
314    pub fn new(
315        previous_header: &SuperSegmentHeader,
316        target_beacon_chain_block_number: BlockNumber,
317        segment_roots: StdArc<[ShardSegmentRootWithPosition]>,
318    ) -> Option<Self> {
319        let num_segments = u32::try_from(segment_roots.len()).ok()?;
320        let max_segment_index = SegmentIndex::from(
321            u64::from(previous_header.max_segment_index.as_inner()) + u64::from(num_segments),
322        );
323
324        let maybe_super_segment_root =
325            SuperSegmentMerkleTree::compute_root_only::<
326                { u64::from(SuperSegmentRoot::MAX_SEGMENTS) },
327                _,
328                _,
329            >(segment_roots.iter().map(ShardSegmentRootWithPosition::hash))?;
330
331        Some(Self {
332            header: SuperSegmentHeader {
333                index: (previous_header.index.as_inner() + SuperSegmentIndex::ONE).into(),
334                root: SuperSegmentRoot::from(maybe_super_segment_root),
335                prev_super_segment_header_hash: Blake3Hash::from(
336                    single_chunk_hash(previous_header.as_bytes())
337                        .expect("Less than a single chunk worth of bytes; qed"),
338                ),
339                max_segment_index: max_segment_index.into(),
340                target_beacon_chain_block_number: target_beacon_chain_block_number.into(),
341                num_segments,
342            },
343            segment_roots,
344        })
345    }
346
347    /// Produce a proof for a segment in the super segment at a given position
348    pub fn proof_for_segment(&self, segment_position: SegmentPosition) -> Option<SegmentProof> {
349        let mut segment_proof = [MaybeUninit::uninit(); _];
350        let (_root, proof_hashes) = SuperSegmentMerkleTree::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, padding) = segment_proof
695            .split_once(|hash| hash == &[0; _])
696            .unwrap_or((segment_proof.as_slice(), &[]));
697        // Padding must only contain zeroes, so that a proof has a single encoding
698        if padding.iter().any(|hash| hash != &[0; _]) {
699            return false;
700        }
701        SuperSegmentMerkleTree::verify(
702            super_segment_root,
703            segment_proof,
704            u64::from(segment_position),
705            shard_segment_root.hash(),
706            u64::from(num_segments),
707        )
708    }
709}
710
711/// Size of blockchain history in segments
712#[derive(
713    Debug,
714    Display,
715    Copy,
716    Clone,
717    Ord,
718    PartialOrd,
719    Eq,
720    PartialEq,
721    Hash,
722    From,
723    Into,
724    Deref,
725    DerefMut,
726    TrivialType,
727)]
728#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
729#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
730#[repr(C)]
731// Storing `SegmentIndex` to make all invariants valid
732pub struct HistorySize(SegmentIndex);
733
734impl HistorySize {
735    /// History size of one
736    pub const ONE: Self = Self(SegmentIndex::ZERO);
737
738    /// Create a new instance
739    #[inline(always)]
740    pub const fn new(value: NonZeroU64) -> Self {
741        Self(SegmentIndex::from(value.get() - 1))
742    }
743
744    /// Get internal representation
745    pub const fn as_segment_index(&self) -> SegmentIndex {
746        self.0
747    }
748
749    /// Get internal representation
750    pub const fn as_non_zero_u64(&self) -> NonZeroU64 {
751        NonZeroU64::new(u64::from(self.0).saturating_add(1)).expect("Not zero; qed")
752    }
753
754    /// Size of blockchain history in pieces
755    #[inline(always)]
756    pub const fn in_pieces(&self) -> NonZeroU64 {
757        NonZeroU64::new(
758            u64::from(self.0)
759                .saturating_add(1)
760                .saturating_mul(RecordedHistorySegment::NUM_PIECES as u64),
761        )
762        .expect("Not zero; qed")
763    }
764
765    /// Segment index that corresponds to this history size
766    #[inline(always)]
767    pub fn segment_index(&self) -> SegmentIndex {
768        self.0
769    }
770
771    /// History size at which expiration check for a sector happens.
772    ///
773    /// Returns `None` on overflow.
774    #[inline(always)]
775    pub fn sector_expiration_check(&self, min_sector_lifetime: Self) -> Option<Self> {
776        self.as_non_zero_u64()
777            .checked_add(min_sector_lifetime.as_non_zero_u64().get())
778            .map(Self::new)
779    }
780}
781
782/// Progress of an archived block.
783#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, TrivialType)]
784#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
785#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
786#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
787#[repr(C)]
788pub struct ArchivedBlockProgress {
789    /// Number of partially archived bytes of a block, `0` for a full block
790    bytes: u32,
791}
792
793impl Default for ArchivedBlockProgress {
794    /// We assume a block can always fit into the segment initially, but it is definitely possible
795    /// to be transitioned into the partial state after some overflow checking.
796    #[inline(always)]
797    fn default() -> Self {
798        Self::new_complete()
799    }
800}
801
802impl ArchivedBlockProgress {
803    /// Block is archived fully
804    #[inline(always)]
805    pub const fn new_complete() -> Self {
806        Self { bytes: 0 }
807    }
808
809    /// Block is partially archived with the provided number of bytes
810    #[inline(always)]
811    pub const fn new_partial(new_partial: NonZeroU32) -> Self {
812        Self {
813            bytes: new_partial.get(),
814        }
815    }
816
817    /// Return the number of partially archived bytes if the progress is not complete
818    #[inline(always)]
819    pub const fn partial(&self) -> Option<NonZeroU32> {
820        NonZeroU32::new(self.bytes)
821    }
822}
823
824/// Last archived block
825#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, TrivialType)]
826#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
827#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
828#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
829#[repr(C)]
830pub struct LastArchivedBlock {
831    /// Block number
832    pub number: Unaligned<BlockNumber>,
833    /// Progress of an archived block
834    pub archived_progress: ArchivedBlockProgress,
835}
836
837impl LastArchivedBlock {
838    /// Returns the number of partially archived bytes for a block
839    #[inline(always)]
840    pub fn partial_archived(&self) -> Option<NonZeroU32> {
841        self.archived_progress.partial()
842    }
843
844    /// Sets the number of partially archived bytes if block progress was archived partially
845    #[inline(always)]
846    pub fn set_partial_archived(&mut self, new_partial: NonZeroU32) {
847        self.archived_progress = ArchivedBlockProgress::new_partial(new_partial);
848    }
849
850    /// Indicate the last archived block was archived fully
851    #[inline(always)]
852    pub fn set_complete(&mut self) {
853        self.archived_progress = ArchivedBlockProgress::new_complete();
854    }
855
856    /// Get the block number (unwrap `Unaligned`)
857    pub const fn number(&self) -> BlockNumber {
858        self.number.as_inner()
859    }
860}
861
862/// Segment header for a specific segment of a shard
863#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TrivialType)]
864#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
865#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
866#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
867#[repr(C)]
868pub struct SegmentHeader {
869    /// Local segment index
870    pub index: Unaligned<LocalSegmentIndex>,
871    /// Root of roots of all records in a segment.
872    pub root: SegmentRoot,
873    /// Hash of the segment header of the previous segment
874    pub prev_segment_header_hash: Blake3Hash,
875    /// Last archived block
876    pub last_archived_block: LastArchivedBlock,
877}
878
879impl SegmentHeader {
880    /// Hash of the whole segment header
881    #[inline(always)]
882    pub fn hash(&self) -> Blake3Hash {
883        const {
884            assert!(size_of::<Self>() <= CHUNK_LEN);
885        }
886        Blake3Hash::new(
887            single_chunk_hash(self.as_bytes())
888                .expect("Less than a single chunk worth of bytes; qed"),
889        )
890    }
891}
892
893/// Recorded history segment before archiving is applied.
894///
895/// NOTE: This is a stack-allocated data structure and can cause stack overflow!
896#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut)]
897#[repr(C)]
898pub struct RecordedHistorySegment([Record; Self::NUM_RAW_RECORDS]);
899
900impl fmt::Debug for RecordedHistorySegment {
901    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
902        f.debug_struct("RecordedHistorySegment")
903            .finish_non_exhaustive()
904    }
905}
906
907impl AsRef<[u8]> for RecordedHistorySegment {
908    #[inline]
909    fn as_ref(&self) -> &[u8] {
910        Record::slice_to_repr(&self.0).as_flattened().as_flattened()
911    }
912}
913
914impl AsMut<[u8]> for RecordedHistorySegment {
915    #[inline]
916    fn as_mut(&mut self) -> &mut [u8] {
917        Record::slice_mut_to_repr(&mut self.0)
918            .as_flattened_mut()
919            .as_flattened_mut()
920    }
921}
922
923impl RecordedHistorySegment {
924    /// Number of raw records in one segment of recorded history
925    pub const NUM_RAW_RECORDS: usize = 128;
926    /// Erasure coding rate for records during the archiving process
927    pub const ERASURE_CODING_RATE: (usize, usize) = (1, 2);
928    /// Number of pieces in one segment of archived history (taking erasure coding rate into
929    /// account)
930    pub const NUM_PIECES: usize =
931        Self::NUM_RAW_RECORDS * Self::ERASURE_CODING_RATE.1 / Self::ERASURE_CODING_RATE.0;
932    /// Size of recorded history segment in bytes.
933    ///
934    /// It includes half of the records (just source records) that will later be erasure coded and
935    /// together with corresponding roots and proofs will result in
936    /// [`Self::NUM_PIECES`] `Piece`s of archival history.
937    pub const SIZE: usize = Record::SIZE * Self::NUM_RAW_RECORDS;
938
939    /// Create boxed value without hitting stack overflow
940    #[inline]
941    #[cfg(feature = "alloc")]
942    pub fn new_boxed() -> Box<Self> {
943        // TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
944        // SAFETY: Data structure filled with zeroes is a valid invariant
945        unsafe { Box::<Self>::new_zeroed().assume_init() }
946    }
947}