Skip to main content

ab_core_primitives/
pieces.rs

1//! Pieces-related data structures.
2
3#[cfg(feature = "alloc")]
4mod cow_bytes;
5#[cfg(feature = "alloc")]
6mod flat_pieces;
7#[cfg(feature = "alloc")]
8mod piece;
9
10#[cfg(feature = "alloc")]
11pub use crate::pieces::flat_pieces::FlatPieces;
12#[cfg(feature = "alloc")]
13pub use crate::pieces::piece::Piece;
14use crate::segments::{
15    LocalSegmentIndex, RecordedHistorySegment, SegmentIndex, SegmentPosition, SegmentRoot,
16    SuperSegmentIndex, SuperSegmentRoot,
17};
18use crate::shard::ShardIndex;
19#[cfg(feature = "serde")]
20use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
21use ab_io_type::trivial_type::TrivialType;
22use ab_io_type::unaligned::Unaligned;
23use ab_merkle_tree::balanced::BalancedMerkleTree;
24#[cfg(feature = "alloc")]
25use alloc::boxed::Box;
26#[cfg(feature = "alloc")]
27use alloc::vec::Vec;
28use blake3::OUT_LEN;
29use core::array::TryFromSliceError;
30use core::hash::Hash;
31use core::iter::Step;
32use core::mem::MaybeUninit;
33#[cfg(feature = "alloc")]
34use core::slice;
35use core::{fmt, mem};
36use derive_more::{
37    Add, AddAssign, AsMut, AsRef, Deref, DerefMut, Display, Div, DivAssign, From, Into, Mul,
38    MulAssign, Sub, SubAssign,
39};
40#[cfg(feature = "scale-codec")]
41use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
42#[cfg(feature = "serde")]
43use serde_big_array::BigArray;
44
45/// Piece index
46#[derive(
47    Debug,
48    Display,
49    Default,
50    Copy,
51    Clone,
52    Ord,
53    PartialOrd,
54    Eq,
55    PartialEq,
56    Hash,
57    Add,
58    AddAssign,
59    Sub,
60    SubAssign,
61    Mul,
62    MulAssign,
63    Div,
64    DivAssign,
65)]
66#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
67#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
68#[repr(C)]
69pub struct PieceIndex(u64);
70
71impl Step for PieceIndex {
72    #[inline]
73    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
74        u64::steps_between(&start.0, &end.0)
75    }
76
77    #[inline]
78    fn forward_checked(start: Self, count: usize) -> Option<Self> {
79        u64::forward_checked(start.0, count).map(Self)
80    }
81
82    #[inline(always)]
83    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
84        let (n, overflowing) = u64::forward_overflowing(start.0, count);
85        (Self(n), overflowing)
86    }
87
88    #[inline]
89    fn backward_checked(start: Self, count: usize) -> Option<Self> {
90        u64::backward_checked(start.0, count).map(Self)
91    }
92
93    #[inline(always)]
94    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
95        let (n, overflowing) = u64::backward_overflowing(start.0, count);
96        (Self(n), overflowing)
97    }
98}
99
100const impl From<u64> for PieceIndex {
101    #[inline(always)]
102    fn from(value: u64) -> Self {
103        Self(value)
104    }
105}
106
107const impl From<PieceIndex> for u64 {
108    #[inline(always)]
109    fn from(value: PieceIndex) -> Self {
110        value.0
111    }
112}
113
114impl PieceIndex {
115    /// Size in bytes.
116    pub const SIZE: usize = size_of::<u64>();
117    /// Piece index 0.
118    pub const ZERO: PieceIndex = PieceIndex(0);
119    /// Piece index 1.
120    pub const ONE: PieceIndex = PieceIndex(1);
121
122    /// Create a piece index from bytes.
123    #[inline]
124    pub const fn from_bytes(bytes: [u8; Self::SIZE]) -> Self {
125        Self(u64::from_le_bytes(bytes))
126    }
127
128    /// Convert a piece index to bytes.
129    #[inline]
130    pub const fn to_bytes(self) -> [u8; Self::SIZE] {
131        self.0.to_le_bytes()
132    }
133
134    /// Segment index piece index corresponds to
135    #[inline]
136    pub const fn segment_index(&self) -> SegmentIndex {
137        SegmentIndex::from(self.0 / RecordedHistorySegment::NUM_PIECES as u64)
138    }
139
140    /// Position of a piece in a segment
141    #[inline]
142    pub fn position(&self) -> PiecePosition {
143        PiecePosition::from((self.0 % RecordedHistorySegment::NUM_PIECES as u64) as u8)
144    }
145}
146
147const {
148    // Assert that `u8` represents `PiecePosition` perfectly
149    assert!(RecordedHistorySegment::NUM_PIECES == usize::from(u8::MAX) + 1);
150}
151
152/// Piece position in a segment
153#[derive(
154    Debug,
155    Display,
156    Default,
157    Copy,
158    Clone,
159    Ord,
160    PartialOrd,
161    Eq,
162    PartialEq,
163    Hash,
164    From,
165    Into,
166    TrivialType,
167)]
168#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
170#[repr(C)]
171pub struct PiecePosition(u8);
172
173impl Step for PiecePosition {
174    #[inline]
175    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
176        u8::steps_between(&start.0, &end.0)
177    }
178
179    #[inline]
180    fn forward_checked(start: Self, count: usize) -> Option<Self> {
181        u8::forward_checked(start.0, count).map(Self)
182    }
183
184    #[inline(always)]
185    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
186        let (n, overflowing) = u8::forward_overflowing(start.0, count);
187        (Self(n), overflowing)
188    }
189
190    #[inline]
191    fn backward_checked(start: Self, count: usize) -> Option<Self> {
192        u8::backward_checked(start.0, count).map(Self)
193    }
194
195    #[inline(always)]
196    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
197        let (n, overflowing) = u8::backward_overflowing(start.0, count);
198        (Self(n), overflowing)
199    }
200}
201
202impl From<PiecePosition> for u16 {
203    #[inline]
204    fn from(original: PiecePosition) -> Self {
205        Self::from(original.0)
206    }
207}
208
209impl From<PiecePosition> for u32 {
210    #[inline]
211    fn from(original: PiecePosition) -> Self {
212        Self::from(original.0)
213    }
214}
215
216impl From<PiecePosition> for u64 {
217    #[inline]
218    fn from(original: PiecePosition) -> Self {
219        Self::from(original.0)
220    }
221}
222
223impl From<PiecePosition> for usize {
224    #[inline]
225    fn from(original: PiecePosition) -> Self {
226        usize::from(original.0)
227    }
228}
229
230/// Piece offset in a sector
231#[derive(
232    Debug,
233    Display,
234    Default,
235    Copy,
236    Clone,
237    Ord,
238    PartialOrd,
239    Eq,
240    PartialEq,
241    Hash,
242    From,
243    Into,
244    Add,
245    AddAssign,
246    Sub,
247    SubAssign,
248    Mul,
249    MulAssign,
250    Div,
251    DivAssign,
252    TrivialType,
253)]
254#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
255#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
256#[repr(C)]
257pub struct PieceOffset(u16);
258
259impl Step for PieceOffset {
260    #[inline]
261    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
262        u16::steps_between(&start.0, &end.0)
263    }
264
265    #[inline]
266    fn forward_checked(start: Self, count: usize) -> Option<Self> {
267        u16::forward_checked(start.0, count).map(Self)
268    }
269
270    #[inline(always)]
271    fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
272        let (n, overflowing) = u16::forward_overflowing(start.0, count);
273        (Self(n), overflowing)
274    }
275
276    #[inline]
277    fn backward_checked(start: Self, count: usize) -> Option<Self> {
278        u16::backward_checked(start.0, count).map(Self)
279    }
280
281    #[inline(always)]
282    fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
283        let (n, overflowing) = u16::backward_overflowing(start.0, count);
284        (Self(n), overflowing)
285    }
286}
287
288impl From<PieceOffset> for u32 {
289    #[inline]
290    fn from(original: PieceOffset) -> Self {
291        Self::from(original.0)
292    }
293}
294
295impl From<PieceOffset> for u64 {
296    #[inline]
297    fn from(original: PieceOffset) -> Self {
298        Self::from(original.0)
299    }
300}
301
302impl From<PieceOffset> for usize {
303    #[inline]
304    fn from(original: PieceOffset) -> Self {
305        usize::from(original.0)
306    }
307}
308
309impl PieceOffset {
310    /// Piece index 0
311    pub const ZERO: Self = Self(0);
312    /// Piece index 1
313    pub const ONE: Self = Self(1);
314    /// Size in bytes
315    pub const SIZE: usize = size_of::<u16>();
316
317    /// Convert piece offset to bytes
318    #[inline]
319    pub const fn to_bytes(self) -> [u8; Self::SIZE] {
320        self.0.to_le_bytes()
321    }
322}
323
324/// Chunk contained in a record
325#[derive(
326    Default,
327    Copy,
328    Clone,
329    Eq,
330    PartialEq,
331    Ord,
332    PartialOrd,
333    Hash,
334    From,
335    Into,
336    AsRef,
337    AsMut,
338    Deref,
339    DerefMut,
340    TrivialType,
341)]
342#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
343#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
344#[cfg_attr(feature = "serde", serde(transparent))]
345#[repr(C)]
346pub struct RecordChunk([u8; RecordChunk::SIZE]);
347
348impl fmt::Debug for RecordChunk {
349    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350        for byte in self.0 {
351            write!(f, "{byte:02x}")?;
352        }
353        Ok(())
354    }
355}
356
357impl RecordChunk {
358    /// Size of the chunk in bytes
359    pub const SIZE: usize = 32;
360
361    /// Convenient conversion from slice to underlying representation for efficiency purposes
362    #[inline]
363    pub fn slice_to_repr(value: &[Self]) -> &[[u8; Self::SIZE]] {
364        // SAFETY: `RecordChunk` is `#[repr(C)]` and guaranteed to have the same memory layout
365        unsafe { mem::transmute(value) }
366    }
367
368    /// Convenient conversion from slice of underlying representation for efficiency purposes
369    #[inline]
370    pub fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
371        // SAFETY: `RecordChunk` is `#[repr(C)]` and guaranteed to have the same memory layout
372        unsafe { mem::transmute(value) }
373    }
374
375    /// Convenient conversion from mutable slice to underlying representation for efficiency
376    /// purposes
377    #[inline]
378    pub fn slice_mut_to_repr(value: &mut [Self]) -> &mut [[u8; Self::SIZE]] {
379        // SAFETY: `RecordChunk` is `#[repr(C)]` and guaranteed to have the same memory layout
380        unsafe { mem::transmute(value) }
381    }
382
383    /// Convenient conversion from mutable slice of underlying representation for efficiency
384    /// purposes
385    #[inline]
386    pub fn slice_mut_from_repr(value: &mut [[u8; Self::SIZE]]) -> &mut [Self] {
387        // SAFETY: `RecordChunk` is `#[repr(C)]` and guaranteed to have the same memory layout
388        unsafe { mem::transmute(value) }
389    }
390}
391
392/// Record contained within a piece.
393///
394/// NOTE: This is a stack-allocated data structure and can cause stack overflow!
395#[derive(Copy, Clone, Eq, PartialEq, Deref, DerefMut, TrivialType)]
396#[repr(C)]
397pub struct Record([[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]);
398
399impl fmt::Debug for Record {
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        for byte in self.0.as_flattened() {
402            write!(f, "{byte:02x}")?;
403        }
404        Ok(())
405    }
406}
407
408impl AsRef<[u8]> for Record {
409    #[inline]
410    fn as_ref(&self) -> &[u8] {
411        self.0.as_flattened()
412    }
413}
414
415impl AsRef<[u8; Record::SIZE]> for Record {
416    #[inline(always)]
417    fn as_ref(&self) -> &[u8; Record::SIZE] {
418        self.as_bytes()
419    }
420}
421
422impl AsMut<[u8; Record::SIZE]> for Record {
423    #[inline(always)]
424    fn as_mut(&mut self) -> &mut [u8; Record::SIZE] {
425        // SAFETY: `Record` is a plain byte array, any bit pattern is valid for it
426        unsafe { self.as_bytes_mut() }
427    }
428}
429
430impl AsMut<[u8]> for Record {
431    #[inline]
432    fn as_mut(&mut self) -> &mut [u8] {
433        self.0.as_flattened_mut()
434    }
435}
436
437impl From<&Record> for &[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS] {
438    #[inline]
439    fn from(value: &Record) -> Self {
440        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
441        unsafe { mem::transmute(value) }
442    }
443}
444
445impl From<&[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]> for &Record {
446    #[inline]
447    fn from(value: &[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]) -> Self {
448        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
449        unsafe { mem::transmute(value) }
450    }
451}
452
453impl From<&mut Record> for &mut [[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS] {
454    #[inline]
455    fn from(value: &mut Record) -> Self {
456        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
457        unsafe { mem::transmute(value) }
458    }
459}
460
461impl From<&mut [[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]> for &mut Record {
462    #[inline]
463    fn from(value: &mut [[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]) -> Self {
464        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
465        unsafe { mem::transmute(value) }
466    }
467}
468
469impl Record {
470    /// Number of chunks within one record.
471    pub const NUM_CHUNKS: usize = 2usize.pow(15);
472    /// Number of s-buckets contained within one sector record.
473    ///
474    /// Essentially we chunk records and erasure code them.
475    pub const NUM_S_BUCKETS: usize = Record::NUM_CHUNKS
476        * RecordedHistorySegment::ERASURE_CODING_RATE.1
477        / RecordedHistorySegment::ERASURE_CODING_RATE.0;
478    /// Size of a segment record, it is guaranteed to be a multiple of [`RecordChunk::SIZE`]
479    pub const SIZE: usize = RecordChunk::SIZE * Record::NUM_CHUNKS;
480
481    /// Create boxed value without hitting stack overflow
482    #[inline]
483    #[cfg(feature = "alloc")]
484    pub fn new_boxed() -> Box<Self> {
485        // TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
486        // SAFETY: Data structure filled with zeroes is a valid invariant
487        unsafe { Box::new_zeroed().assume_init() }
488    }
489
490    /// Create vector filled with zeroed records without hitting stack overflow
491    #[inline]
492    #[cfg(feature = "alloc")]
493    pub fn new_zero_vec(length: usize) -> Vec<Self> {
494        // TODO: Should have been just `vec![Self::default(); length]`, but
495        //  https://github.com/rust-lang/rust/issues/53827
496        let mut records = Vec::with_capacity(length);
497        {
498            let slice = records.spare_capacity_mut();
499            // SAFETY: Same memory layout due to `#[repr(C)]` on `Record` and
500            // `MaybeUninit<[[T; M]; N]>` is guaranteed to have the same layout as
501            // `[[MaybeUninit<T>; M]; N]`
502            let slice = unsafe {
503                slice::from_raw_parts_mut(
504                    slice
505                        .as_mut_ptr()
506                        .cast::<[[MaybeUninit<u8>; RecordChunk::SIZE]; Record::NUM_CHUNKS]>(),
507                    length,
508                )
509            };
510            for byte in slice.as_flattened_mut().as_flattened_mut() {
511                byte.write(0);
512            }
513        }
514        // SAFETY: All values are initialized above.
515        unsafe {
516            records.set_len(records.capacity());
517        }
518
519        records
520    }
521
522    /// Convenient conversion from slice of record to underlying representation for efficiency
523    /// purposes.
524    #[inline(always)]
525    pub fn slice_to_repr(value: &[Self]) -> &[[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]] {
526        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
527        unsafe { mem::transmute(value) }
528    }
529
530    /// Convenient conversion from slice of underlying representation to record for efficiency
531    /// purposes.
532    #[inline(always)]
533    pub fn slice_from_repr(value: &[[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]]) -> &[Self] {
534        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
535        unsafe { mem::transmute(value) }
536    }
537
538    /// Convenient conversion from mutable slice of record to underlying representation for
539    /// efficiency purposes.
540    #[inline(always)]
541    pub fn slice_mut_to_repr(
542        value: &mut [Self],
543    ) -> &mut [[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]] {
544        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
545        unsafe { mem::transmute(value) }
546    }
547
548    /// Convenient conversion from mutable slice of underlying representation to record for
549    /// efficiency purposes.
550    #[inline(always)]
551    pub fn slice_mut_from_repr(
552        value: &mut [[[u8; RecordChunk::SIZE]; Record::NUM_CHUNKS]],
553    ) -> &mut [Self] {
554        // SAFETY: `Record` is `#[repr(C)]` and guaranteed to have the same memory layout
555        unsafe { mem::transmute(value) }
556    }
557
558    /// Derive source chunks root on-demand
559    #[inline(always)]
560    pub fn source_chunks_root(&self) -> RecordChunksRoot {
561        RecordChunksRoot(BalancedMerkleTree::compute_root_only(self))
562    }
563}
564
565/// Root of the record contained within a piece.
566///
567/// This is a Merkle Tree root of the roots of source and parity record chunks.
568#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
569#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
570#[repr(C)]
571pub struct RecordRoot([u8; RecordRoot::SIZE]);
572
573impl fmt::Debug for RecordRoot {
574    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575        for byte in self.0 {
576            write!(f, "{byte:02x}")?;
577        }
578        Ok(())
579    }
580}
581
582#[cfg(feature = "serde")]
583#[derive(Serialize, Deserialize)]
584#[serde(transparent)]
585struct RecordRootBinary(#[serde(with = "BigArray")] [u8; RecordRoot::SIZE]);
586
587#[cfg(feature = "serde")]
588#[derive(Serialize, Deserialize)]
589#[serde(transparent)]
590struct RecordRootHex(#[serde(with = "hex")] [u8; RecordRoot::SIZE]);
591
592#[cfg(feature = "serde")]
593impl Serialize for RecordRoot {
594    #[inline]
595    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
596    where
597        S: Serializer,
598    {
599        if serializer.is_human_readable() {
600            RecordRootHex(self.0).serialize(serializer)
601        } else {
602            RecordRootBinary(self.0).serialize(serializer)
603        }
604    }
605}
606
607#[cfg(feature = "serde")]
608impl<'de> Deserialize<'de> for RecordRoot {
609    #[inline]
610    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
611    where
612        D: Deserializer<'de>,
613    {
614        Ok(Self(if deserializer.is_human_readable() {
615            RecordRootHex::deserialize(deserializer)?.0
616        } else {
617            RecordRootBinary::deserialize(deserializer)?.0
618        }))
619    }
620}
621
622impl Default for RecordRoot {
623    #[inline]
624    fn default() -> Self {
625        Self([0; _])
626    }
627}
628
629impl TryFrom<&[u8]> for RecordRoot {
630    type Error = TryFromSliceError;
631
632    #[inline]
633    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
634        <[u8; Self::SIZE]>::try_from(slice).map(Self)
635    }
636}
637
638impl AsRef<[u8]> for RecordRoot {
639    #[inline]
640    fn as_ref(&self) -> &[u8] {
641        &self.0
642    }
643}
644
645impl AsMut<[u8]> for RecordRoot {
646    #[inline]
647    fn as_mut(&mut self) -> &mut [u8] {
648        &mut self.0
649    }
650}
651
652impl From<&RecordRoot> for &[u8; RecordRoot::SIZE] {
653    #[inline]
654    fn from(value: &RecordRoot) -> Self {
655        // SAFETY: `RecordRoot` is `#[repr(C)]` and guaranteed to have the same
656        // memory layout
657        unsafe { mem::transmute(value) }
658    }
659}
660
661impl From<&[u8; RecordRoot::SIZE]> for &RecordRoot {
662    #[inline]
663    fn from(value: &[u8; RecordRoot::SIZE]) -> Self {
664        // SAFETY: `RecordRoot` is `#[repr(C)]` and guaranteed to have the same
665        // memory layout
666        unsafe { mem::transmute(value) }
667    }
668}
669
670impl From<&mut RecordRoot> for &mut [u8; RecordRoot::SIZE] {
671    #[inline]
672    fn from(value: &mut RecordRoot) -> Self {
673        // SAFETY: `RecordRoot` is `#[repr(C)]` and guaranteed to have the same
674        // memory layout
675        unsafe { mem::transmute(value) }
676    }
677}
678
679impl From<&mut [u8; RecordRoot::SIZE]> for &mut RecordRoot {
680    #[inline]
681    fn from(value: &mut [u8; RecordRoot::SIZE]) -> Self {
682        // SAFETY: `RecordRoot` is `#[repr(C)]` and guaranteed to have the same
683        // memory layout
684        unsafe { mem::transmute(value) }
685    }
686}
687
688impl RecordRoot {
689    /// Size of record root in bytes.
690    pub const SIZE: usize = 32;
691
692    /// Validate record root hash produced by the archiver
693    pub fn is_valid(
694        &self,
695        segment_root: &SegmentRoot,
696        record_proof: &RecordProof,
697        position: PiecePosition,
698    ) -> bool {
699        BalancedMerkleTree::<{ RecordedHistorySegment::NUM_PIECES }>::verify(
700            segment_root,
701            record_proof,
702            usize::from(position),
703            self.0,
704        )
705    }
706}
707
708/// Root of source or parity record chunks
709#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
710#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
711#[repr(C)]
712pub struct RecordChunksRoot([u8; RecordChunksRoot::SIZE]);
713
714impl fmt::Debug for RecordChunksRoot {
715    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716        for byte in self.0 {
717            write!(f, "{byte:02x}")?;
718        }
719        Ok(())
720    }
721}
722
723#[cfg(feature = "serde")]
724#[derive(Serialize, Deserialize)]
725#[serde(transparent)]
726struct RecordChunksRootBinary(#[serde(with = "BigArray")] [u8; RecordChunksRoot::SIZE]);
727
728#[cfg(feature = "serde")]
729#[derive(Serialize, Deserialize)]
730#[serde(transparent)]
731struct RecordChunksRootHex(#[serde(with = "hex")] [u8; RecordChunksRoot::SIZE]);
732
733#[cfg(feature = "serde")]
734impl Serialize for RecordChunksRoot {
735    #[inline]
736    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
737    where
738        S: Serializer,
739    {
740        if serializer.is_human_readable() {
741            RecordChunksRootHex(self.0).serialize(serializer)
742        } else {
743            RecordChunksRootBinary(self.0).serialize(serializer)
744        }
745    }
746}
747
748#[cfg(feature = "serde")]
749impl<'de> Deserialize<'de> for RecordChunksRoot {
750    #[inline]
751    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
752    where
753        D: Deserializer<'de>,
754    {
755        Ok(Self(if deserializer.is_human_readable() {
756            RecordChunksRootHex::deserialize(deserializer)?.0
757        } else {
758            RecordChunksRootBinary::deserialize(deserializer)?.0
759        }))
760    }
761}
762
763impl Default for RecordChunksRoot {
764    #[inline]
765    fn default() -> Self {
766        Self([0; _])
767    }
768}
769
770impl TryFrom<&[u8]> for RecordChunksRoot {
771    type Error = TryFromSliceError;
772
773    #[inline]
774    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
775        <[u8; Self::SIZE]>::try_from(slice).map(Self)
776    }
777}
778
779impl AsRef<[u8]> for RecordChunksRoot {
780    #[inline]
781    fn as_ref(&self) -> &[u8] {
782        &self.0
783    }
784}
785
786impl AsMut<[u8]> for RecordChunksRoot {
787    #[inline]
788    fn as_mut(&mut self) -> &mut [u8] {
789        &mut self.0
790    }
791}
792
793impl From<&RecordChunksRoot> for &[u8; RecordChunksRoot::SIZE] {
794    #[inline]
795    fn from(value: &RecordChunksRoot) -> Self {
796        // SAFETY: `RecordChunksRoot` is `#[repr(C)]` and guaranteed to have the same
797        // memory layout
798        unsafe { mem::transmute(value) }
799    }
800}
801
802impl From<&[u8; RecordChunksRoot::SIZE]> for &RecordChunksRoot {
803    #[inline]
804    fn from(value: &[u8; RecordChunksRoot::SIZE]) -> Self {
805        // SAFETY: `RecordChunksRoot` is `#[repr(C)]` and guaranteed to have the same
806        // memory layout
807        unsafe { mem::transmute(value) }
808    }
809}
810
811impl From<&mut RecordChunksRoot> for &mut [u8; RecordChunksRoot::SIZE] {
812    #[inline]
813    fn from(value: &mut RecordChunksRoot) -> Self {
814        // SAFETY: `RecordChunksRoot` is `#[repr(C)]` and guaranteed to have the same
815        // memory layout
816        unsafe { mem::transmute(value) }
817    }
818}
819
820impl From<&mut [u8; RecordChunksRoot::SIZE]> for &mut RecordChunksRoot {
821    #[inline]
822    fn from(value: &mut [u8; RecordChunksRoot::SIZE]) -> Self {
823        // SAFETY: `RecordChunksRoot` is `#[repr(C)]` and guaranteed to have the same
824        // memory layout
825        unsafe { mem::transmute(value) }
826    }
827}
828
829impl RecordChunksRoot {
830    /// Size of record chunks root in bytes.
831    pub const SIZE: usize = 32;
832}
833
834/// Proof that the record (root) belongs to a segment
835#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
836#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
837#[repr(C)]
838pub struct RecordProof([[u8; OUT_LEN]; RecordProof::NUM_HASHES]);
839
840impl fmt::Debug for RecordProof {
841    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
842        write!(f, "[")?;
843        for hash in self.0 {
844            for byte in hash {
845                write!(f, "{byte:02x}")?;
846            }
847            write!(f, ", ")?;
848        }
849        write!(f, "]")?;
850        Ok(())
851    }
852}
853
854#[cfg(feature = "serde")]
855#[derive(Serialize, Deserialize)]
856#[serde(transparent)]
857struct RecordProofBinary([[u8; OUT_LEN]; RecordProof::NUM_HASHES]);
858
859#[cfg(feature = "serde")]
860#[derive(Serialize, Deserialize)]
861#[serde(transparent)]
862struct RecordProofHexHash(#[serde(with = "hex")] [u8; OUT_LEN]);
863
864#[cfg(feature = "serde")]
865#[derive(Serialize, Deserialize)]
866#[serde(transparent)]
867struct RecordProofHex([RecordProofHexHash; RecordProof::NUM_HASHES]);
868
869#[cfg(feature = "serde")]
870impl Serialize for RecordProof {
871    #[inline]
872    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
873    where
874        S: Serializer,
875    {
876        if serializer.is_human_readable() {
877            // SAFETY: `RecordProofHexHash` is `#[repr(C)]` and guaranteed to have the
878            // same memory layout
879            RecordProofHex(unsafe {
880                mem::transmute::<
881                    [[u8; OUT_LEN]; Self::NUM_HASHES],
882                    [RecordProofHexHash; Self::NUM_HASHES],
883                >(self.0)
884            })
885            .serialize(serializer)
886        } else {
887            RecordProofBinary(self.0).serialize(serializer)
888        }
889    }
890}
891
892#[cfg(feature = "serde")]
893impl<'de> Deserialize<'de> for RecordProof {
894    #[inline]
895    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
896    where
897        D: Deserializer<'de>,
898    {
899        Ok(Self(if deserializer.is_human_readable() {
900            // SAFETY: `RecordProofHexHash` is `#[repr(C)]` and guaranteed to have the
901            // same memory layout
902            unsafe {
903                mem::transmute::<
904                    [RecordProofHexHash; Self::NUM_HASHES],
905                    [[u8; OUT_LEN]; Self::NUM_HASHES],
906                >(RecordProofHex::deserialize(deserializer)?.0)
907            }
908        } else {
909            RecordProofBinary::deserialize(deserializer)?.0
910        }))
911    }
912}
913
914impl Default for RecordProof {
915    #[inline]
916    fn default() -> Self {
917        Self([[0; OUT_LEN]; _])
918    }
919}
920
921impl AsRef<[u8]> for RecordProof {
922    #[inline]
923    fn as_ref(&self) -> &[u8] {
924        self.0.as_flattened()
925    }
926}
927
928impl AsMut<[u8]> for RecordProof {
929    #[inline]
930    fn as_mut(&mut self) -> &mut [u8] {
931        self.0.as_flattened_mut()
932    }
933}
934
935impl From<&RecordProof> for &[u8; RecordProof::SIZE] {
936    #[inline]
937    fn from(value: &RecordProof) -> Self {
938        // SAFETY: `RecordProof` is `#[repr(C)]` and guaranteed to have the same
939        // memory layout
940        unsafe { mem::transmute(value) }
941    }
942}
943
944impl From<&[u8; RecordProof::SIZE]> for &RecordProof {
945    #[inline]
946    fn from(value: &[u8; RecordProof::SIZE]) -> Self {
947        // SAFETY: `RecordProof` is `#[repr(C)]` and guaranteed to have the same
948        // memory layout
949        unsafe { mem::transmute(value) }
950    }
951}
952
953impl From<&mut RecordProof> for &mut [u8; RecordProof::SIZE] {
954    #[inline]
955    fn from(value: &mut RecordProof) -> Self {
956        // SAFETY: `RecordProof` is `#[repr(C)]` and guaranteed to have the same
957        // memory layout
958        unsafe { mem::transmute(value) }
959    }
960}
961
962impl From<&mut [u8; RecordProof::SIZE]> for &mut RecordProof {
963    #[inline]
964    fn from(value: &mut [u8; RecordProof::SIZE]) -> Self {
965        // SAFETY: `RecordProof` is `#[repr(C)]` and guaranteed to have the same
966        // memory layout
967        unsafe { mem::transmute(value) }
968    }
969}
970
971impl RecordProof {
972    /// Size of record proof in bytes
973    pub const SIZE: usize = OUT_LEN * Self::NUM_HASHES;
974    const NUM_HASHES: usize = RecordedHistorySegment::NUM_PIECES.ilog2() as usize;
975}
976
977/// Proof that the segment belongs to a super segment
978#[derive(Copy, Clone, Eq, PartialEq, Hash, Deref, DerefMut, From, Into, TrivialType)]
979#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
980#[repr(C)]
981pub struct SegmentProof([[u8; OUT_LEN]; SegmentProof::NUM_HASHES]);
982
983impl fmt::Debug for SegmentProof {
984    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
985        write!(f, "[")?;
986        for hash in self.0 {
987            for byte in hash {
988                write!(f, "{byte:02x}")?;
989            }
990            write!(f, ", ")?;
991        }
992        write!(f, "]")?;
993        Ok(())
994    }
995}
996
997#[cfg(feature = "serde")]
998#[derive(Serialize, Deserialize)]
999#[serde(transparent)]
1000struct SegmentProofBinary([[u8; OUT_LEN]; SegmentProof::NUM_HASHES]);
1001
1002#[cfg(feature = "serde")]
1003#[derive(Serialize, Deserialize)]
1004#[serde(transparent)]
1005struct SegmentProofHexHash(#[serde(with = "hex")] [u8; OUT_LEN]);
1006
1007#[cfg(feature = "serde")]
1008#[derive(Serialize, Deserialize)]
1009#[serde(transparent)]
1010struct SegmentProofHex([SegmentProofHexHash; SegmentProof::NUM_HASHES]);
1011
1012#[cfg(feature = "serde")]
1013impl Serialize for SegmentProof {
1014    #[inline]
1015    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1016    where
1017        S: Serializer,
1018    {
1019        if serializer.is_human_readable() {
1020            // SAFETY: `SegmentProofHexHash` is `#[repr(C)]` and guaranteed to have the
1021            // same memory layout
1022            SegmentProofHex(unsafe {
1023                mem::transmute::<
1024                    [[u8; OUT_LEN]; Self::NUM_HASHES],
1025                    [SegmentProofHexHash; Self::NUM_HASHES],
1026                >(self.0)
1027            })
1028            .serialize(serializer)
1029        } else {
1030            SegmentProofBinary(self.0).serialize(serializer)
1031        }
1032    }
1033}
1034
1035#[cfg(feature = "serde")]
1036impl<'de> Deserialize<'de> for SegmentProof {
1037    #[inline]
1038    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1039    where
1040        D: Deserializer<'de>,
1041    {
1042        Ok(Self(if deserializer.is_human_readable() {
1043            // SAFETY: `SegmentProofHexHash` is `#[repr(C)]` and guaranteed to have the
1044            // same memory layout
1045            unsafe {
1046                mem::transmute::<
1047                    [SegmentProofHexHash; Self::NUM_HASHES],
1048                    [[u8; OUT_LEN]; Self::NUM_HASHES],
1049                >(SegmentProofHex::deserialize(deserializer)?.0)
1050            }
1051        } else {
1052            SegmentProofBinary::deserialize(deserializer)?.0
1053        }))
1054    }
1055}
1056
1057impl Default for SegmentProof {
1058    #[inline]
1059    fn default() -> Self {
1060        Self([[0; OUT_LEN]; _])
1061    }
1062}
1063
1064impl AsRef<[u8]> for SegmentProof {
1065    #[inline]
1066    fn as_ref(&self) -> &[u8] {
1067        self.0.as_flattened()
1068    }
1069}
1070
1071impl AsMut<[u8]> for SegmentProof {
1072    #[inline]
1073    fn as_mut(&mut self) -> &mut [u8] {
1074        self.0.as_flattened_mut()
1075    }
1076}
1077
1078impl From<&SegmentProof> for &[u8; SegmentProof::SIZE] {
1079    #[inline]
1080    fn from(value: &SegmentProof) -> Self {
1081        // SAFETY: `SegmentProof` is `#[repr(C)]` and guaranteed to have the same
1082        // memory layout
1083        unsafe { mem::transmute(value) }
1084    }
1085}
1086
1087impl From<&[u8; SegmentProof::SIZE]> for &SegmentProof {
1088    #[inline]
1089    fn from(value: &[u8; SegmentProof::SIZE]) -> Self {
1090        // SAFETY: `SegmentProof` is `#[repr(C)]` and guaranteed to have the same
1091        // memory layout
1092        unsafe { mem::transmute(value) }
1093    }
1094}
1095
1096impl From<&mut SegmentProof> for &mut [u8; SegmentProof::SIZE] {
1097    #[inline]
1098    fn from(value: &mut SegmentProof) -> Self {
1099        // SAFETY: `SegmentProof` is `#[repr(C)]` and guaranteed to have the same
1100        // memory layout
1101        unsafe { mem::transmute(value) }
1102    }
1103}
1104
1105impl From<&mut [u8; SegmentProof::SIZE]> for &mut SegmentProof {
1106    #[inline]
1107    fn from(value: &mut [u8; SegmentProof::SIZE]) -> Self {
1108        // SAFETY: `SegmentProof` is `#[repr(C)]` and guaranteed to have the same
1109        // memory layout
1110        unsafe { mem::transmute(value) }
1111    }
1112}
1113
1114impl SegmentProof {
1115    /// Size of segment proof in bytes
1116    pub const SIZE: usize = OUT_LEN * Self::NUM_HASHES;
1117    const NUM_HASHES: usize = SuperSegmentRoot::MAX_SEGMENTS.next_power_of_two().ilog2() as usize;
1118
1119    /// Returns a mutable reference to an internal array as uninitialized memory.
1120    ///
1121    /// This is a convenience method for proof generation.
1122    pub fn as_uninit_repr(
1123        &mut self,
1124    ) -> &mut [MaybeUninit<[u8; OUT_LEN]>; SegmentProof::NUM_HASHES] {
1125        // SAFETY: Casting initialized memory into uninitialized memory of the same size is safe
1126        unsafe { mem::transmute(&mut self.0) }
1127    }
1128}
1129
1130/// Header for a piece of archival history.
1131///
1132/// Primarily contains information needed for piece verification.
1133#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, TrivialType)]
1134#[cfg_attr(feature = "scale-codec", derive(Encode, Decode))]
1135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1136#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1137#[repr(C)]
1138pub struct PieceHeader {
1139    /// Shard index
1140    pub shard_index: Unaligned<ShardIndex>,
1141    /// Local segment index
1142    pub local_segment_index: Unaligned<LocalSegmentIndex>,
1143    /// Super segment index
1144    pub super_segment_index: Unaligned<SuperSegmentIndex>,
1145    /// Position of the segment in the super segment
1146    pub segment_position: Unaligned<SegmentPosition>,
1147    /// Segment root
1148    pub segment_root: SegmentRoot,
1149    /// Segment proof
1150    pub segment_proof: SegmentProof,
1151    /// Root of parity record chunks.
1152    ///
1153    /// Technically redundant, but helps to avoid repeating erasure coding during verification.
1154    pub parity_chunks_root: RecordChunksRoot,
1155    /// Proof that the record (root) belongs to a segment
1156    pub record_proof: RecordProof,
1157}
1158
1159const {
1160    // Must have minimal alignment for various conversions to/from bytes
1161    assert!(align_of::<PieceHeader>() == 1);
1162}
1163
1164/// A piece of archival history.
1165///
1166/// This version is allocated on the stack, for a heap-allocated piece that can be moved around
1167/// efficiently, see [`Piece`].
1168///
1169/// Internally, a piece contains a record, supplementary record chunk root, and a proof proving this
1170/// piece belongs to can be used to verify that a piece belongs to the actual archival history of
1171/// the blockchain.
1172#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
1173#[repr(C)]
1174pub struct InnerPiece {
1175    /// Piece header
1176    pub header: PieceHeader,
1177    /// Record contained within a piece
1178    pub record: Record,
1179}
1180
1181const {
1182    // Must have minimal alignment for various conversions to/from bytes
1183    assert!(align_of::<InnerPiece>() == 1);
1184}
1185
1186impl InnerPiece {
1187    /// Size of a piece (in bytes)
1188    pub const SIZE: usize = size_of::<Self>();
1189
1190    /// Create boxed value without hitting stack overflow
1191    #[inline]
1192    #[cfg(feature = "alloc")]
1193    pub fn new_boxed() -> Box<Self> {
1194        // TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
1195        // SAFETY: Data structure filled with zeroes is a valid invariant
1196        unsafe { Box::<Self>::new_zeroed().assume_init() }
1197    }
1198
1199    /// Check whether the piece is valid against the matching super segment root
1200    pub fn is_valid(
1201        &self,
1202        super_segment_root: &SuperSegmentRoot,
1203        num_segments: u32,
1204        position: PiecePosition,
1205    ) -> bool {
1206        if !self.header.segment_root.is_valid(
1207            self.header.shard_index.as_inner(),
1208            self.header.local_segment_index.as_inner(),
1209            self.header.segment_position.as_inner(),
1210            &self.header.segment_proof,
1211            num_segments,
1212            super_segment_root,
1213        ) {
1214            return false;
1215        }
1216        self.record_root().is_valid(
1217            &self.header.segment_root,
1218            &self.header.record_proof,
1219            position,
1220        )
1221    }
1222
1223    /// Root of the record contained within a piece.
1224    ///
1225    /// It is re-derived on every call of this function.
1226    #[inline]
1227    pub fn record_root(&self) -> RecordRoot {
1228        let record_merkle_tree_root = BalancedMerkleTree::compute_root_only(&[
1229            *self.record.source_chunks_root(),
1230            *self.header.parity_chunks_root,
1231        ]);
1232
1233        RecordRoot::from(record_merkle_tree_root)
1234    }
1235
1236    /// Convenient conversion from slice of piece array to underlying representation for efficiency
1237    /// purposes.
1238    #[inline]
1239    pub fn slice_to_repr(value: &[Self]) -> &[[u8; Self::SIZE]] {
1240        // SAFETY: `PieceArray` is `#[repr(C)]` and guaranteed to have the same memory
1241        // layout
1242        unsafe { mem::transmute(value) }
1243    }
1244
1245    /// Convenient conversion from slice of underlying representation to piece array for efficiency
1246    /// purposes.
1247    #[inline]
1248    pub fn slice_from_repr(value: &[[u8; Self::SIZE]]) -> &[Self] {
1249        // SAFETY: `PieceArray` is `#[repr(C)]` and guaranteed to have the same memory
1250        // layout
1251        unsafe { mem::transmute(value) }
1252    }
1253
1254    /// Convenient conversion from mutable slice of piece array to underlying representation for
1255    /// efficiency purposes.
1256    #[inline]
1257    pub fn slice_mut_to_repr(value: &mut [Self]) -> &mut [[u8; Self::SIZE]] {
1258        // SAFETY: `PieceArray` is `#[repr(C)]` and guaranteed to have the same memory
1259        // layout
1260        unsafe { mem::transmute(value) }
1261    }
1262
1263    /// Convenient conversion from mutable slice of underlying representation to piece array for
1264    /// efficiency purposes.
1265    #[inline]
1266    pub fn slice_mut_from_repr(value: &mut [[u8; Self::SIZE]]) -> &mut [Self] {
1267        // SAFETY: `PieceArray` is `#[repr(C)]` and guaranteed to have the same memory
1268        // layout
1269        unsafe { mem::transmute(value) }
1270    }
1271}