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};
9#[cfg(feature = "alloc")]
10pub use crate::segments::archival_history_segment::ArchivedHistorySegment;
11use ab_io_type::trivial_type::TrivialType;
12use ab_io_type::unaligned::Unaligned;
13#[cfg(feature = "alloc")]
14use alloc::boxed::Box;
15use core::iter::Step;
16use core::num::{NonZeroU32, NonZeroU64};
17use core::{fmt, mem};
18use derive_more::{
19    Add, AddAssign, Deref, DerefMut, Display, Div, DivAssign, From, Into, Mul, MulAssign, Sub,
20    SubAssign,
21};
22#[cfg(feature = "scale-codec")]
23use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
24#[cfg(feature = "scale-codec")]
25use scale_info::TypeInfo;
26#[cfg(feature = "serde")]
27use serde::{Deserialize, Deserializer, Serialize, Serializer};
28#[cfg(feature = "serde")]
29use serde_big_array::BigArray;
30
31/// Super segment root contained within beacon chain block
32#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
33#[cfg_attr(
34    feature = "scale-codec",
35    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
36)]
37#[repr(C)]
38pub struct SuperSegmentRoot([u8; SuperSegmentRoot::SIZE]);
39
40impl fmt::Debug for SuperSegmentRoot {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        for byte in self.0 {
43            write!(f, "{byte:02x}")?;
44        }
45        Ok(())
46    }
47}
48
49#[cfg(feature = "serde")]
50#[derive(Serialize, Deserialize)]
51#[serde(transparent)]
52struct SuperSegmentRootBinary(#[serde(with = "BigArray")] [u8; SuperSegmentRoot::SIZE]);
53
54#[cfg(feature = "serde")]
55#[derive(Serialize, Deserialize)]
56#[serde(transparent)]
57struct SuperSegmentRootHex(#[serde(with = "hex")] [u8; SuperSegmentRoot::SIZE]);
58
59#[cfg(feature = "serde")]
60impl Serialize for SuperSegmentRoot {
61    #[inline]
62    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63    where
64        S: Serializer,
65    {
66        if serializer.is_human_readable() {
67            SuperSegmentRootHex(self.0).serialize(serializer)
68        } else {
69            SuperSegmentRootBinary(self.0).serialize(serializer)
70        }
71    }
72}
73
74#[cfg(feature = "serde")]
75impl<'de> Deserialize<'de> for SuperSegmentRoot {
76    #[inline]
77    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78    where
79        D: Deserializer<'de>,
80    {
81        Ok(Self(if deserializer.is_human_readable() {
82            SuperSegmentRootHex::deserialize(deserializer)?.0
83        } else {
84            SuperSegmentRootBinary::deserialize(deserializer)?.0
85        }))
86    }
87}
88
89impl Default for SuperSegmentRoot {
90    #[inline]
91    fn default() -> Self {
92        Self([0; Self::SIZE])
93    }
94}
95
96impl AsRef<[u8]> for SuperSegmentRoot {
97    #[inline]
98    fn as_ref(&self) -> &[u8] {
99        &self.0
100    }
101}
102
103impl AsMut<[u8]> for SuperSegmentRoot {
104    #[inline]
105    fn as_mut(&mut self) -> &mut [u8] {
106        &mut self.0
107    }
108}
109
110impl SuperSegmentRoot {
111    /// Size in bytes
112    pub const SIZE: usize = 32;
113}
114
115/// Segment index type.
116#[derive(
117    Debug,
118    Display,
119    Default,
120    Copy,
121    Clone,
122    Ord,
123    PartialOrd,
124    Eq,
125    PartialEq,
126    Hash,
127    From,
128    Into,
129    Add,
130    AddAssign,
131    Sub,
132    SubAssign,
133    Mul,
134    MulAssign,
135    Div,
136    DivAssign,
137    TrivialType,
138)]
139#[cfg_attr(
140    feature = "scale-codec",
141    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
142)]
143#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
144#[repr(C)]
145pub struct SegmentIndex(u64);
146
147impl Step for SegmentIndex {
148    #[inline]
149    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
150        u64::steps_between(&start.0, &end.0)
151    }
152
153    #[inline]
154    fn forward_checked(start: Self, count: usize) -> Option<Self> {
155        u64::forward_checked(start.0, count).map(Self)
156    }
157
158    #[inline]
159    fn backward_checked(start: Self, count: usize) -> Option<Self> {
160        u64::backward_checked(start.0, count).map(Self)
161    }
162}
163
164impl SegmentIndex {
165    /// Segment index 0.
166    pub const ZERO: SegmentIndex = SegmentIndex(0);
167    /// Segment index 1.
168    pub const ONE: SegmentIndex = SegmentIndex(1);
169
170    /// Create new instance
171    #[inline]
172    pub const fn new(n: u64) -> Self {
173        Self(n)
174    }
175
176    /// Get internal representation
177    #[inline(always)]
178    pub const fn as_u64(self) -> u64 {
179        self.0
180    }
181
182    /// Get the first piece index in this segment.
183    #[inline]
184    pub const fn first_piece_index(&self) -> PieceIndex {
185        PieceIndex::new(self.0 * RecordedHistorySegment::NUM_PIECES as u64)
186    }
187
188    /// Get the last piece index in this segment.
189    #[inline]
190    pub const fn last_piece_index(&self) -> PieceIndex {
191        PieceIndex::new((self.0 + 1) * RecordedHistorySegment::NUM_PIECES as u64 - 1)
192    }
193
194    /// List of piece indexes that belong to this segment.
195    #[inline]
196    pub fn segment_piece_indexes(&self) -> [PieceIndex; RecordedHistorySegment::NUM_PIECES] {
197        let mut piece_indices = [PieceIndex::ZERO; RecordedHistorySegment::NUM_PIECES];
198        (self.first_piece_index()..=self.last_piece_index())
199            .zip(&mut piece_indices)
200            .for_each(|(input, output)| {
201                *output = input;
202            });
203
204        piece_indices
205    }
206
207    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
208    #[inline]
209    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
210        self.0.checked_sub(rhs.0).map(Self)
211    }
212}
213
214/// Segment root contained within segment header.
215#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
216#[cfg_attr(
217    feature = "scale-codec",
218    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
219)]
220#[repr(C)]
221pub struct SegmentRoot([u8; SegmentRoot::SIZE]);
222
223impl fmt::Debug for SegmentRoot {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        for byte in self.0 {
226            write!(f, "{byte:02x}")?;
227        }
228        Ok(())
229    }
230}
231
232#[cfg(feature = "serde")]
233#[derive(Serialize, Deserialize)]
234#[serde(transparent)]
235struct SegmentRootBinary(#[serde(with = "BigArray")] [u8; SegmentRoot::SIZE]);
236
237#[cfg(feature = "serde")]
238#[derive(Serialize, Deserialize)]
239#[serde(transparent)]
240struct SegmentRootHex(#[serde(with = "hex")] [u8; SegmentRoot::SIZE]);
241
242#[cfg(feature = "serde")]
243impl Serialize for SegmentRoot {
244    #[inline]
245    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
246    where
247        S: Serializer,
248    {
249        if serializer.is_human_readable() {
250            SegmentRootHex(self.0).serialize(serializer)
251        } else {
252            SegmentRootBinary(self.0).serialize(serializer)
253        }
254    }
255}
256
257#[cfg(feature = "serde")]
258impl<'de> Deserialize<'de> for SegmentRoot {
259    #[inline]
260    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261    where
262        D: Deserializer<'de>,
263    {
264        Ok(Self(if deserializer.is_human_readable() {
265            SegmentRootHex::deserialize(deserializer)?.0
266        } else {
267            SegmentRootBinary::deserialize(deserializer)?.0
268        }))
269    }
270}
271
272impl Default for SegmentRoot {
273    #[inline(always)]
274    fn default() -> Self {
275        Self([0; Self::SIZE])
276    }
277}
278
279impl AsRef<[u8]> for SegmentRoot {
280    #[inline(always)]
281    fn as_ref(&self) -> &[u8] {
282        &self.0
283    }
284}
285
286impl AsMut<[u8]> for SegmentRoot {
287    #[inline(always)]
288    fn as_mut(&mut self) -> &mut [u8] {
289        &mut self.0
290    }
291}
292
293impl SegmentRoot {
294    /// Size in bytes
295    pub const SIZE: usize = 32;
296
297    /// Convenient conversion from slice of underlying representation for efficiency purposes
298    #[inline(always)]
299    pub const fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
300        // SAFETY: `SegmentRoot` is `#[repr(C)]` and guaranteed to have the same memory layout
301        unsafe { mem::transmute(value) }
302    }
303
304    /// Convenient conversion to slice of underlying representation for efficiency purposes
305    #[inline(always)]
306    pub const fn repr_from_slice(value: &[Self]) -> &[[u8; Self::SIZE]] {
307        // SAFETY: `SegmentRoot` is `#[repr(C)]` and guaranteed to have the same memory layout
308        unsafe { mem::transmute(value) }
309    }
310}
311
312/// Size of blockchain history in segments.
313#[derive(
314    Debug,
315    Display,
316    Copy,
317    Clone,
318    Ord,
319    PartialOrd,
320    Eq,
321    PartialEq,
322    Hash,
323    From,
324    Into,
325    Deref,
326    DerefMut,
327    TrivialType,
328)]
329#[cfg_attr(
330    feature = "scale-codec",
331    derive(Encode, Decode, TypeInfo, MaxEncodedLen)
332)]
333#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
334#[repr(C)]
335// Storing `SegmentIndex` to make all invariants valid
336pub struct HistorySize(SegmentIndex);
337
338impl HistorySize {
339    /// History size of one
340    pub const ONE: Self = Self(SegmentIndex::ZERO);
341
342    /// Create new instance
343    #[inline(always)]
344    pub const fn new(value: NonZeroU64) -> Self {
345        Self(SegmentIndex::new(value.get() - 1))
346    }
347
348    /// Get internal representation
349    pub const fn as_segment_index(&self) -> SegmentIndex {
350        self.0
351    }
352
353    /// Get internal representation
354    pub const fn as_non_zero_u64(&self) -> NonZeroU64 {
355        NonZeroU64::new(self.0.as_u64().saturating_add(1)).expect("Not zero; qed")
356    }
357
358    /// Size of blockchain history in pieces.
359    #[inline(always)]
360    pub const fn in_pieces(&self) -> NonZeroU64 {
361        NonZeroU64::new(
362            self.0
363                .as_u64()
364                .saturating_add(1)
365                .saturating_mul(RecordedHistorySegment::NUM_PIECES as u64),
366        )
367        .expect("Not zero; qed")
368    }
369
370    /// Segment index that corresponds to this history size.
371    #[inline(always)]
372    pub fn segment_index(&self) -> SegmentIndex {
373        self.0
374    }
375
376    /// History size at which expiration check for sector happens.
377    ///
378    /// Returns `None` on overflow.
379    #[inline(always)]
380    pub fn sector_expiration_check(&self, min_sector_lifetime: Self) -> Option<Self> {
381        self.as_non_zero_u64()
382            .checked_add(min_sector_lifetime.as_non_zero_u64().get())
383            .map(Self::new)
384    }
385}
386
387/// Progress of an archived block.
388#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, TrivialType)]
389#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, TypeInfo))]
390#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
391#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
392#[repr(C)]
393pub struct ArchivedBlockProgress {
394    /// Number of partially archived bytes of a block, `0` for full block
395    bytes: u32,
396}
397
398impl Default for ArchivedBlockProgress {
399    /// We assume a block can always fit into the segment initially, but it is definitely possible
400    /// to be transitioned into the partial state after some overflow checking.
401    #[inline(always)]
402    fn default() -> Self {
403        Self::new_complete()
404    }
405}
406
407impl ArchivedBlockProgress {
408    /// Block is archived fully
409    #[inline(always)]
410    pub const fn new_complete() -> Self {
411        Self { bytes: 0 }
412    }
413
414    /// Block is partially archived with provided number of bytes
415    #[inline(always)]
416    pub const fn new_partial(new_partial: NonZeroU32) -> Self {
417        Self {
418            bytes: new_partial.get(),
419        }
420    }
421
422    /// Return the number of partially archived bytes if the progress is not complete
423    #[inline(always)]
424    pub const fn partial(&self) -> Option<NonZeroU32> {
425        NonZeroU32::new(self.bytes)
426    }
427}
428
429/// Last archived block
430#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, TrivialType)]
431#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, TypeInfo))]
432#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
433#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
434#[repr(C)]
435pub struct LastArchivedBlock {
436    /// Block number
437    pub number: Unaligned<BlockNumber>,
438    /// Progress of an archived block.
439    pub archived_progress: ArchivedBlockProgress,
440}
441
442impl LastArchivedBlock {
443    /// Returns the number of partially archived bytes for a block.
444    #[inline(always)]
445    pub fn partial_archived(&self) -> Option<NonZeroU32> {
446        self.archived_progress.partial()
447    }
448
449    /// Sets the number of partially archived bytes if block progress was archived partially
450    #[inline(always)]
451    pub fn set_partial_archived(&mut self, new_partial: NonZeroU32) {
452        self.archived_progress = ArchivedBlockProgress::new_partial(new_partial);
453    }
454
455    /// Indicate last archived block was archived fully
456    #[inline(always)]
457    pub fn set_complete(&mut self) {
458        self.archived_progress = ArchivedBlockProgress::new_complete();
459    }
460
461    /// Get block number (unwrap `Unaligned`)
462    pub const fn number(&self) -> BlockNumber {
463        self.number.as_inner()
464    }
465}
466
467/// Segment header for a specific segment.
468///
469/// Each segment will have corresponding [`SegmentHeader`] included as the first item in the next
470/// segment. Each `SegmentHeader` includes hash of the previous one and all together form a chain of
471/// segment headers that is used for quick and efficient verification that some `Piece`
472/// corresponds to the actual archival history of the blockchain.
473#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, TrivialType)]
474#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, TypeInfo))]
475#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
476#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
477#[repr(C)]
478pub struct SegmentHeader {
479    /// Segment index
480    pub segment_index: Unaligned<SegmentIndex>,
481    /// Root of roots of all records in a segment.
482    pub segment_root: SegmentRoot,
483    /// Hash of the segment header of the previous segment
484    pub prev_segment_header_hash: Blake3Hash,
485    /// Last archived block
486    pub last_archived_block: LastArchivedBlock,
487}
488
489impl SegmentHeader {
490    /// Hash of the whole segment header
491    #[inline(always)]
492    pub fn hash(&self) -> Blake3Hash {
493        blake3::hash(self.as_bytes()).into()
494    }
495
496    /// Get segment index (unwrap `Unaligned`)
497    #[inline(always)]
498    pub const fn segment_index(&self) -> SegmentIndex {
499        self.segment_index.as_inner()
500    }
501}
502
503/// Recorded history segment before archiving is applied.
504///
505/// NOTE: This is a stack-allocated data structure and can cause stack overflow!
506#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut)]
507#[repr(C)]
508pub struct RecordedHistorySegment([Record; Self::NUM_RAW_RECORDS]);
509
510impl fmt::Debug for RecordedHistorySegment {
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        f.debug_struct("RecordedHistorySegment")
513            .finish_non_exhaustive()
514    }
515}
516
517impl Default for RecordedHistorySegment {
518    #[inline]
519    fn default() -> Self {
520        Self([Record::default(); Self::NUM_RAW_RECORDS])
521    }
522}
523
524impl AsRef<[u8]> for RecordedHistorySegment {
525    #[inline]
526    fn as_ref(&self) -> &[u8] {
527        Record::slice_to_repr(&self.0).as_flattened().as_flattened()
528    }
529}
530
531impl AsMut<[u8]> for RecordedHistorySegment {
532    #[inline]
533    fn as_mut(&mut self) -> &mut [u8] {
534        Record::slice_mut_to_repr(&mut self.0)
535            .as_flattened_mut()
536            .as_flattened_mut()
537    }
538}
539
540impl RecordedHistorySegment {
541    /// Number of raw records in one segment of recorded history.
542    pub const NUM_RAW_RECORDS: usize = 128;
543    /// Erasure coding rate for records during archiving process.
544    pub const ERASURE_CODING_RATE: (usize, usize) = (1, 2);
545    /// Number of pieces in one segment of archived history (taking erasure coding rate into
546    /// account)
547    pub const NUM_PIECES: usize =
548        Self::NUM_RAW_RECORDS * Self::ERASURE_CODING_RATE.1 / Self::ERASURE_CODING_RATE.0;
549    /// Size of recorded history segment in bytes.
550    ///
551    /// It includes half of the records (just source records) that will later be erasure coded and
552    /// together with corresponding roots and proofs will result in
553    /// [`Self::NUM_PIECES`] `Piece`s of archival history.
554    pub const SIZE: usize = Record::SIZE * Self::NUM_RAW_RECORDS;
555
556    /// Create boxed value without hitting stack overflow
557    #[inline]
558    #[cfg(feature = "alloc")]
559    pub fn new_boxed() -> Box<Self> {
560        // TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
561        // SAFETY: Data structure filled with zeroes is a valid invariant
562        unsafe { Box::<Self>::new_zeroed().assume_init() }
563    }
564}