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