Skip to main content

ab_core_primitives/block/
body.rs

1//! Block body primitives
2
3#[cfg(feature = "alloc")]
4pub mod owned;
5
6#[cfg(feature = "alloc")]
7use crate::block::body::owned::{
8    GenericOwnedBlockBody, OwnedBeaconChainBody, OwnedBlockBody, OwnedIntermediateShardBody,
9    OwnedLeafShardBody,
10};
11use crate::block::header::{IntermediateShardHeader, LeafShardHeader};
12use crate::block::{BlockNumber, align_to_and_ensure_zero_padding};
13use crate::hashes::Blake3Hash;
14use crate::pot::PotCheckpoints;
15use crate::segments::{LocalSegmentIndex, SegmentRoot};
16use crate::shard::{RealShardKind, ShardIndex};
17use crate::transaction::Transaction;
18use ab_blake3::{BLOCK_LEN, OUT_LEN, single_block_hash};
19use ab_io_type::trivial_type::TrivialType;
20use ab_io_type::unaligned::Unaligned;
21use ab_merkle_tree::balanced::BalancedMerkleTree;
22use ab_merkle_tree::unbalanced::UnbalancedMerkleTree;
23use core::iter::TrustedLen;
24use core::{array, cmp, fmt, iter, slice};
25use derive_more::From;
26use yoke::Yokeable;
27
28/// Generic block body
29pub trait GenericBlockBody<'a>
30where
31    Self: Copy + fmt::Debug + Into<BlockBody<'a>> + Send + Sync,
32{
33    /// Shard kind
34    const SHARD_KIND: RealShardKind;
35
36    /// Owned block body
37    #[cfg(feature = "alloc")]
38    type Owned: GenericOwnedBlockBody<Body<'a> = Self>
39    where
40        Self: 'a;
41
42    /// Turn into an owned version
43    #[cfg(feature = "alloc")]
44    fn to_owned(self) -> Self::Owned;
45
46    /// Compute block body root
47    fn root(&self) -> Blake3Hash;
48}
49
50/// Calculates a Merkle Tree root for a provided list of segment roots
51#[inline]
52pub fn compute_segments_root<'a, const MAX_SEGMENTS: u64, Iter>(segment_roots: Iter) -> Blake3Hash
53where
54    [(); MAX_SEGMENTS.next_power_of_two().ilog2() as usize + 1]:,
55    Iter: IntoIterator<Item = &'a SegmentRoot>,
56{
57    // TODO: Keyed hash
58    let root = UnbalancedMerkleTree::compute_root_only::<MAX_SEGMENTS, _, _>(
59        segment_roots.into_iter().map(|segment_root| {
60            // Hash the root again so we can prove it, otherwise root of segments is
61            // indistinguishable from individual segment roots and can be used to confuse verifier
62            single_block_hash(segment_root.as_ref())
63                .expect("Less than a single block worth of bytes; qed")
64        }),
65    );
66
67    Blake3Hash::new(root.unwrap_or_default())
68}
69
70/// Own segments produced by a shard
71#[derive(Debug, Copy, Clone)]
72pub struct OwnSegments<'a> {
73    /// Local segment index of the first own segment root
74    pub first_local_segment_index: LocalSegmentIndex,
75    /// Segment roots produced by a shard
76    pub segment_roots: &'a [SegmentRoot],
77}
78
79impl OwnSegments<'_> {
80    /// Compute the root of own segments
81    #[inline]
82    pub fn root(&self) -> Blake3Hash {
83        // TODO: Keyed hash
84        let root = BalancedMerkleTree::compute_root_only(&[
85            single_block_hash(self.first_local_segment_index.as_bytes())
86                .expect("Less than a single block worth of bytes; qed"),
87            *compute_segments_root::<{ u8::MAX as u64 }, _>(self.segment_roots),
88        ]);
89
90        Blake3Hash::new(root)
91    }
92
93    /// Compute the root of own segments while mixing in shard index.
94    ///
95    /// This method is useful for deriving roots on intermediate shard that can be verified by the
96    /// beacon chain later (which will have just shard index available rather than the whole leaf
97    /// shard block header).
98    #[inline]
99    pub fn root_with_shard_index(&self, shard_index: ShardIndex) -> Blake3Hash {
100        // TODO: Keyed hash
101        let root = BalancedMerkleTree::compute_root_only(&[
102            {
103                const {
104                    assert!((ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize <= BLOCK_LEN);
105                }
106                let mut pair = [0u8; (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize];
107                pair[..ShardIndex::SIZE as usize].copy_from_slice(shard_index.as_bytes());
108                pair[ShardIndex::SIZE as usize..]
109                    .copy_from_slice(self.first_local_segment_index.as_bytes());
110
111                single_block_hash(&pair).expect("Less than a single block worth of bytes; qed")
112            },
113            *compute_segments_root::<{ u8::MAX as u64 }, _>(self.segment_roots),
114        ]);
115
116        Blake3Hash::new(root)
117    }
118}
119
120/// Information about intermediate shard block
121#[derive(Debug, Clone)]
122pub struct IntermediateShardBlockInfo<'a> {
123    /// A block header that corresponds to an intermediate shard
124    pub header: IntermediateShardHeader<'a>,
125    /// Segments proof if either own or child segments are present
126    pub segments_proof: Option<&'a [u8; OUT_LEN]>,
127    /// Segments in the corresponding block
128    pub own_segments: Option<OwnSegments<'a>>,
129    num_leaf_shard_blocks_with_segments: u8,
130    leaf_shards_segments_bytes: &'a [u8],
131}
132
133impl<'a> IntermediateShardBlockInfo<'a> {
134    /// Segments of leaf shards in the corresponding intermediate shard block
135    #[inline]
136    pub fn leaf_shards_segments(
137        &self,
138    ) -> impl ExactSizeIterator<Item = (ShardIndex, OwnSegments<'a>)> + TrustedLen + use<'a> {
139        // SAFETY: Checked in constructor
140        let (counts, mut remainder) = unsafe {
141            self.leaf_shards_segments_bytes.split_at_unchecked(
142                usize::from(self.num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
143            )
144        };
145        counts.iter().map(move |&num_own_segment_roots| {
146            let num_own_segment_roots = usize::from(num_own_segment_roots);
147
148            let shard_index;
149            // SAFETY: Checked in constructor
150            (shard_index, remainder) =
151                unsafe { remainder.split_at_unchecked(ShardIndex::SIZE as usize) };
152            // SAFETY: Correct size and no alignment requirements
153            let shard_index =
154                unsafe { Unaligned::<ShardIndex>::from_bytes_unchecked(shard_index) }.as_inner();
155
156            let first_local_segment_index;
157            // SAFETY: Checked in constructor
158            (first_local_segment_index, remainder) =
159                unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
160            // SAFETY: Correct size and no alignment requirements
161            let first_local_segment_index = unsafe {
162                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
163            }
164            .as_inner();
165
166            let own_segment_roots;
167            // SAFETY: Checked in constructor
168            (own_segment_roots, remainder) =
169                unsafe { remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE) };
170            // SAFETY: Valid pointer and size, no alignment requirements
171            let own_segment_roots = unsafe {
172                slice::from_raw_parts(
173                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
174                    num_own_segment_roots,
175                )
176            };
177            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
178
179            (
180                shard_index,
181                OwnSegments {
182                    first_local_segment_index,
183                    segment_roots: own_segment_roots,
184                },
185            )
186        })
187    }
188}
189
190/// Information about a collection of intermediate shard blocks
191#[derive(Debug, Copy, Clone)]
192pub struct IntermediateShardBlocksInfo<'a> {
193    num_blocks: usize,
194    bytes: &'a [u8],
195}
196
197impl<'a> IntermediateShardBlocksInfo<'a> {
198    /// Create an instance from provided bytes.
199    ///
200    /// `bytes` do not need to be aligned.
201    ///
202    /// Returns an instance and remaining bytes on success.
203    #[inline]
204    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
205        // The layout here is as follows:
206        // * number of blocks: u16 as unaligned little-endian bytes
207        // * for each block:
208        //   * number of own segment roots: u8
209        //   * number of leaf shard blocks with segments: u8
210        // * padding to 8-bytes boundary with zeroes
211        // * for each block:
212        //   * block header: IntermediateShardHeader
213        //   * segment roots proof (if either own or child segments are present)
214        //   * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
215        //   * concatenated own segment roots
216        //   * for each leaf shard block with segments:
217        //     * number of own segment roots: u8
218        //   * for each leaf shard block with segments:
219        //     * shard index: u32 as unaligned little-endian bytes
220        //     * local segment index of the first segment root: unaligned `LocalSegmentIndex`
221        //     * concatenated own segment roots
222        //   * padding to 8-bytes boundary with zeroes
223
224        let num_blocks = bytes.split_off(..size_of::<u16>())?;
225        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
226
227        let bytes_start = bytes;
228
229        let mut counts = bytes.split_off(..num_blocks * (size_of::<u8>() * 2))?;
230
231        let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
232
233        for _ in 0..num_blocks {
234            let num_own_segment_roots = usize::from(counts[0]);
235            let num_leaf_shard_blocks_with_segments = usize::from(counts[1]);
236            counts = &counts[2..];
237
238            (_, remainder) = IntermediateShardHeader::try_from_bytes(remainder)?;
239
240            if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
241                let _segments_proof = remainder.split_off(..SegmentRoot::SIZE)?;
242            }
243
244            if num_own_segment_roots > 0 {
245                let _first_local_segment_index =
246                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
247                let _own_segment_roots =
248                    remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
249            }
250
251            let leaf_shard_segment_roots_counts =
252                remainder.split_off(..num_leaf_shard_blocks_with_segments * size_of::<u8>())?;
253
254            for &num_own_segment_roots in leaf_shard_segment_roots_counts {
255                let _shard_index = remainder.split_off(..ShardIndex::SIZE as usize)?;
256                let _first_local_segment_index =
257                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
258                let _own_segment_roots = remainder
259                    .split_off(..usize::from(num_own_segment_roots) * SegmentRoot::SIZE)?;
260            }
261
262            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
263        }
264
265        let info = Self {
266            num_blocks,
267            bytes: &bytes_start[..bytes_start.len() - remainder.len()],
268        };
269
270        if !info.is_internally_consistent() {
271            return None;
272        }
273
274        Some((info, remainder))
275    }
276
277    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
278    /// checks
279    #[inline]
280    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
281        // The layout here is as follows:
282        // * number of blocks: u16 as unaligned little-endian bytes
283        // * for each block:
284        //   * number of own segment roots: u8
285        //   * number of leaf shard blocks with segments: u8
286        // * padding to 8-bytes boundary with zeroes
287        // * for each block:
288        //   * block header: IntermediateShardHeader
289        //   * segment roots proof (if either own or child segments are present)
290        //   * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
291        //   * concatenated own segment roots
292        //   * for each leaf shard block with segments:
293        //     * number of own segment roots: u8
294        //   * for each leaf shard block with segments:
295        //     * shard index: u32 as unaligned little-endian bytes
296        //     * local segment index of the first segment root: unaligned `LocalSegmentIndex`
297        //     * concatenated own segment roots
298        //   * padding to 8-bytes boundary with zeroes
299
300        let num_blocks = bytes.split_off(..size_of::<u16>())?;
301        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
302
303        let bytes_start = bytes;
304
305        let mut counts = bytes.split_off(..num_blocks * (size_of::<u8>() * 2))?;
306
307        let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
308
309        for _ in 0..num_blocks {
310            let num_own_segment_roots = usize::from(counts[0]);
311            let num_leaf_shard_blocks_with_segments = usize::from(counts[1]);
312            counts = &counts[2..];
313
314            (_, remainder) = IntermediateShardHeader::try_from_bytes(remainder)?;
315
316            if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
317                let _segments_proof = remainder.split_off(..OUT_LEN)?;
318            }
319
320            if num_own_segment_roots > 0 {
321                let _first_local_segment_index =
322                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
323                let _own_segment_roots =
324                    remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
325            }
326
327            let leaf_shard_segment_roots_counts =
328                remainder.split_off(..num_leaf_shard_blocks_with_segments * size_of::<u8>())?;
329
330            for &num_own_segment_roots in leaf_shard_segment_roots_counts {
331                let _shard_index = remainder.split_off(..ShardIndex::SIZE as usize)?;
332                let _first_local_segment_index =
333                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
334                let _own_segment_roots = remainder
335                    .split_off(..usize::from(num_own_segment_roots) * SegmentRoot::SIZE)?;
336            }
337
338            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
339        }
340
341        Some((
342            Self {
343                num_blocks,
344                bytes: &bytes_start[..bytes_start.len() - remainder.len()],
345            },
346            remainder,
347        ))
348    }
349
350    /// Check intermediate shard info's internal consistency.
351    ///
352    /// This is usually not necessary to be called explicitly since internal consistency is checked
353    /// by [`Self::try_from_bytes()`] internally.
354    #[inline]
355    pub fn is_internally_consistent(&self) -> bool {
356        let mut last_intermediate_shard_info =
357            None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
358
359        self.iter().all(|intermediate_shard_block| {
360            let shard_index = intermediate_shard_block.header.prefix.shard_index;
361
362            // Ensure increasing order of shard indices, block numbers and local segment indices
363            if let Some((
364                last_intermediate_shard_index,
365                last_intermediate_shard_block_number,
366                last_intermediate_shard_first_local_segment_index,
367            )) = last_intermediate_shard_info
368            {
369                match last_intermediate_shard_index.cmp(&shard_index) {
370                    cmp::Ordering::Less => {
371                        last_intermediate_shard_info.replace((
372                            shard_index,
373                            intermediate_shard_block.header.prefix.number,
374                            intermediate_shard_block
375                                .own_segments
376                                .as_ref()
377                                .map(|own_segments| own_segments.first_local_segment_index),
378                        ));
379                    }
380                    cmp::Ordering::Equal => {
381                        if last_intermediate_shard_block_number
382                            >= intermediate_shard_block.header.prefix.number
383                        {
384                            return false;
385                        }
386                        if let Some(own_segments) = &intermediate_shard_block.own_segments {
387                            if let Some(last_intermediate_shard_first_local_segment_index) =
388                                last_intermediate_shard_first_local_segment_index
389                                && last_intermediate_shard_first_local_segment_index
390                                    >= own_segments.first_local_segment_index
391                            {
392                                return false;
393                            }
394
395                            last_intermediate_shard_info.replace((
396                                shard_index,
397                                intermediate_shard_block.header.prefix.number,
398                                Some(own_segments.first_local_segment_index),
399                            ));
400                        } else {
401                            last_intermediate_shard_info.replace((
402                                shard_index,
403                                intermediate_shard_block.header.prefix.number,
404                                last_intermediate_shard_first_local_segment_index,
405                            ));
406                        }
407                    }
408                    cmp::Ordering::Greater => {
409                        return false;
410                    }
411                }
412            } else {
413                last_intermediate_shard_info.replace((
414                    shard_index,
415                    intermediate_shard_block.header.prefix.number,
416                    intermediate_shard_block
417                        .own_segments
418                        .as_ref()
419                        .map(|own_segments| own_segments.first_local_segment_index),
420                ));
421            }
422
423            let mut last_leaf_shard_info = None::<(ShardIndex, LocalSegmentIndex)>;
424
425            // Ensure increasing order of shard indices and local segment indices
426            if !intermediate_shard_block.leaf_shards_segments().all(
427                |(leaf_shard_index, own_segments)| {
428                    if !leaf_shard_index.is_child_of(shard_index) {
429                        return false;
430                    }
431
432                    if let Some((
433                        last_leaf_shard_index,
434                        last_leaf_shard_first_local_segment_index,
435                    )) = last_leaf_shard_info
436                    {
437                        match last_leaf_shard_index.cmp(&leaf_shard_index) {
438                            cmp::Ordering::Less => {
439                                // Expected
440                            }
441                            cmp::Ordering::Equal => {
442                                if last_leaf_shard_first_local_segment_index
443                                    >= own_segments.first_local_segment_index
444                                {
445                                    return false;
446                                }
447                            }
448                            cmp::Ordering::Greater => {
449                                return false;
450                            }
451                        }
452                    }
453                    last_leaf_shard_info
454                        .replace((leaf_shard_index, own_segments.first_local_segment_index));
455
456                    true
457                },
458            ) {
459                return false;
460            }
461
462            if intermediate_shard_block.own_segments.is_none()
463                && intermediate_shard_block
464                    .leaf_shards_segments()
465                    .size_hint()
466                    .0
467                    == 0
468            {
469                return intermediate_shard_block.segments_proof.is_none();
470            }
471
472            let segments_proof = intermediate_shard_block.segments_proof.unwrap_or(&[0; _]);
473
474            // TODO: This is a workaround for https://github.com/rust-lang/rust/issues/139866 that allows
475            //  the code to compile. Constant 65535 is hardcoded here and below for compilation
476            // to  succeed.
477            #[expect(clippy::assertions_on_constants, reason = "Intentional documentation")]
478            #[expect(clippy::eq_op, reason = "Intentional documentation")]
479            const {
480                assert!(u16::MAX == 65535);
481            }
482            let leaf_shards_root = UnbalancedMerkleTree::compute_root_only::<65535, _, _>(
483                intermediate_shard_block.leaf_shards_segments().map(
484                    |(shard_index, own_segments)| own_segments.root_with_shard_index(shard_index),
485                ),
486            )
487            .unwrap_or_default();
488
489            BalancedMerkleTree::<2>::verify(
490                &intermediate_shard_block.header.result.body_root,
491                array::from_ref(segments_proof),
492                0,
493                BalancedMerkleTree::compute_root_only(&[
494                    *intermediate_shard_block
495                        .own_segments
496                        .as_ref()
497                        .map(OwnSegments::root)
498                        .unwrap_or_default(),
499                    leaf_shards_root,
500                ]),
501            )
502        })
503    }
504
505    /// Iterator over intermediate shard blocks in a collection
506    #[inline]
507    pub fn iter(
508        &self,
509    ) -> impl ExactSizeIterator<Item = IntermediateShardBlockInfo<'a>> + TrustedLen + use<'a> {
510        // SAFETY: Checked in constructor
511        let (mut counts, mut remainder) = unsafe {
512            self.bytes
513                .split_at_unchecked(self.num_blocks * (size_of::<u8>() + size_of::<u16>()))
514        };
515
516        iter::repeat_with(move || {
517            let num_own_segment_roots = usize::from(counts[0]);
518            let num_leaf_shard_blocks_with_segments = counts[1];
519            counts = &counts[2..];
520
521            // TODO: Unchecked method would have been helpful here
522            let header;
523            (header, remainder) = IntermediateShardHeader::try_from_bytes(remainder)
524                .expect("Already checked in constructor; qed");
525
526            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)
527                .expect("Already checked in constructor; qed");
528
529            let segments_proof =
530                if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
531                    let segments_proof;
532                    // SAFETY: Checked in constructor
533                    (segments_proof, remainder) = unsafe { remainder.split_at_unchecked(OUT_LEN) };
534                    // SAFETY: Valid pointer and size, no alignment requirements
535                    Some(unsafe {
536                        segments_proof
537                            .as_ptr()
538                            .cast::<[u8; OUT_LEN]>()
539                            .as_ref_unchecked()
540                    })
541                } else {
542                    None
543                };
544
545            let own_segments = if num_own_segment_roots > 0 {
546                let first_local_segment_index;
547                // SAFETY: Checked in constructor
548                (first_local_segment_index, remainder) =
549                    unsafe { remainder.split_at_unchecked(size_of::<LocalSegmentIndex>()) };
550                // SAFETY: Correct alignment and size
551                let first_local_segment_index =
552                    *unsafe { LocalSegmentIndex::from_bytes_unchecked(first_local_segment_index) };
553
554                let own_segment_roots;
555                // SAFETY: Checked in constructor
556                (own_segment_roots, remainder) = unsafe {
557                    remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE)
558                };
559                // SAFETY: Valid pointer and size, no alignment requirements
560                let own_segment_roots = unsafe {
561                    slice::from_raw_parts(
562                        own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
563                        num_own_segment_roots,
564                    )
565                };
566                let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
567
568                Some(OwnSegments {
569                    first_local_segment_index,
570                    segment_roots: own_segment_roots,
571                })
572            } else {
573                None
574            };
575
576            // SAFETY: Checked in constructor
577            let leaf_shard_segment_roots_counts = unsafe {
578                remainder.get_unchecked(
579                    ..usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
580                )
581            };
582
583            let leaf_shards_segments_bytes;
584            // SAFETY: Checked in constructor
585            (leaf_shards_segments_bytes, remainder) = unsafe {
586                remainder.split_at_unchecked(
587                    usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>()
588                        + usize::from(num_leaf_shard_blocks_with_segments)
589                            * (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize
590                        + leaf_shard_segment_roots_counts.iter().fold(
591                            0usize,
592                            |acc, &num_own_segment_roots| {
593                                acc + usize::from(num_own_segment_roots) * SegmentRoot::SIZE
594                            },
595                        ),
596                )
597            };
598
599            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)
600                .expect("Already checked in constructor; qed");
601
602            IntermediateShardBlockInfo {
603                header,
604                segments_proof,
605                own_segments,
606                num_leaf_shard_blocks_with_segments,
607                leaf_shards_segments_bytes,
608            }
609        })
610        .take(self.num_blocks)
611    }
612
613    /// Number of intermediate shard blocks
614    #[inline(always)]
615    pub const fn len(&self) -> usize {
616        self.num_blocks
617    }
618
619    /// Returns `true` if there are no intermediate shard blocks
620    #[inline(always)]
621    pub const fn is_empty(&self) -> bool {
622        self.num_blocks == 0
623    }
624
625    /// Compute the root of segments of the intermediate shard blocks info.
626    ///
627    /// Returns the default value for an empty collection of segment roots.
628    #[inline]
629    pub fn segments_root(&self) -> Blake3Hash {
630        let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
631            self.iter().map(|shard_block_info| {
632                // TODO: This is a workaround for https://github.com/rust-lang/rust/issues/139866
633                //  that allows the code to compile. Constant 4_294_967_295 is hardcoded here and
634                //  below for compilation to succeed.
635                #[expect(clippy::assertions_on_constants, reason = "Intentional documentation")]
636                #[expect(clippy::eq_op, reason = "Intentional documentation")]
637                const {
638                    assert!(u32::MAX == 4_294_967_295);
639                }
640                UnbalancedMerkleTree::compute_root_only::<4_294_967_295, _, _>(
641                    shard_block_info
642                        .own_segments
643                        .as_ref()
644                        .map(OwnSegments::root)
645                        .into_iter()
646                        .chain(shard_block_info.leaf_shards_segments().map(
647                            |(shard_index, own_segments)| {
648                                own_segments.root_with_shard_index(shard_index)
649                            },
650                        )),
651                )
652                .unwrap_or_default()
653            }),
654        )
655        .unwrap_or_default();
656
657        Blake3Hash::new(root)
658    }
659
660    /// Compute the root of headers of the intermediate shard blocks info.
661    ///
662    /// Returns the default value for an empty collection of shard blocks.
663    #[inline]
664    pub fn headers_root(&self) -> Blake3Hash {
665        let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
666            // TODO: Keyed hash
667            self.iter().map(|shard_block_info| {
668                // Hash the root again so we can prove it, otherwise the root of headers is
669                // indistinguishable from individual block roots and can be used to confuse
670                // verifier
671                single_block_hash(shard_block_info.header.root().as_ref())
672                    .expect("Less than a single block worth of bytes; qed")
673            }),
674        )
675        .unwrap_or_default();
676
677        Blake3Hash::new(root)
678    }
679}
680
681/// Block body that corresponds to the beacon chain
682#[derive(Debug, Copy, Clone, Yokeable)]
683// Prevent creation of potentially broken invariants externally
684#[non_exhaustive]
685pub struct BeaconChainBody<'a> {
686    /// Segments produced by this shard
687    own_segments: Option<OwnSegments<'a>>,
688    /// Intermediate shard blocks
689    intermediate_shard_blocks: IntermediateShardBlocksInfo<'a>,
690    /// Proof of time checkpoints from after future proof of time of the parent block to the
691    /// current block's future proof of time (inclusive)
692    pot_checkpoints: &'a [PotCheckpoints],
693}
694
695impl<'a> GenericBlockBody<'a> for BeaconChainBody<'a> {
696    const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
697
698    #[cfg(feature = "alloc")]
699    type Owned = OwnedBeaconChainBody;
700
701    #[cfg(feature = "alloc")]
702    #[inline(always)]
703    fn to_owned(self) -> Self::Owned {
704        self.to_owned()
705    }
706
707    #[inline(always)]
708    fn root(&self) -> Blake3Hash {
709        self.root()
710    }
711}
712
713impl<'a> BeaconChainBody<'a> {
714    /// Create an instance from provided correctly aligned bytes.
715    ///
716    /// `bytes` should be 4-bytes aligned.
717    ///
718    /// Returns an instance and remaining bytes on success.
719    #[inline]
720    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
721        // The layout here is as follows:
722        // * number of PoT checkpoints: u32 as aligned little-endian bytes
723        // * number of own segment roots: u8
724        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
725        // * concatenated own segment roots
726        // * intermediate shard blocks: IntermediateShardBlocksInfo
727        // * concatenated PoT checkpoints
728
729        let num_pot_checkpoints = bytes.split_off(..size_of::<u32>())?;
730        // SAFETY: All bit patterns are valid
731        let num_pot_checkpoints =
732            *unsafe { <u32 as TrivialType>::from_bytes(num_pot_checkpoints) }? as usize;
733
734        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
735        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
736
737        let own_segments = if num_own_segment_roots > 0 {
738            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
739            // SAFETY: Unaligned and correct size
740            let first_local_segment_index = unsafe {
741                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
742            }
743            .as_inner();
744
745            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
746            // SAFETY: Valid pointer and size, no alignment requirements
747            let own_segment_roots = unsafe {
748                slice::from_raw_parts(
749                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
750                    num_own_segment_roots,
751                )
752            };
753            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
754
755            Some(OwnSegments {
756                first_local_segment_index,
757                segment_roots: own_segment_roots,
758            })
759        } else {
760            None
761        };
762
763        let (intermediate_shard_blocks, mut remainder) =
764            IntermediateShardBlocksInfo::try_from_bytes(bytes)?;
765
766        let pot_checkpoints = remainder.split_off(..num_pot_checkpoints * PotCheckpoints::SIZE)?;
767        // SAFETY: Valid pointer and size, no alignment requirements
768        let pot_checkpoints = unsafe {
769            slice::from_raw_parts(
770                pot_checkpoints
771                    .as_ptr()
772                    .cast::<[u8; PotCheckpoints::SIZE]>(),
773                num_pot_checkpoints,
774            )
775        };
776        let pot_checkpoints = PotCheckpoints::slice_from_bytes(pot_checkpoints);
777
778        let body = Self {
779            own_segments,
780            intermediate_shard_blocks,
781            pot_checkpoints,
782        };
783
784        if !body.is_internally_consistent() {
785            return None;
786        }
787
788        Some((body, remainder))
789    }
790
791    /// Check block body's internal consistency.
792    ///
793    /// This is usually not necessary to be called explicitly since internal consistency is checked
794    /// by [`Self::try_from_bytes()`] internally.
795    #[inline]
796    pub fn is_internally_consistent(&self) -> bool {
797        // Nothing to check here
798        true
799    }
800
801    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
802    /// checks
803    #[inline]
804    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
805        // The layout here is as follows:
806        // * number of PoT checkpoints: u32 as aligned little-endian bytes
807        // * number of own segment roots: u8
808        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
809        // * concatenated own segment roots
810        // * intermediate shard blocks: IntermediateShardBlocksInfo
811        // * concatenated PoT checkpoints
812
813        let num_pot_checkpoints = bytes.split_off(..size_of::<u32>())?;
814        // SAFETY: All bit patterns are valid
815        let num_pot_checkpoints =
816            *unsafe { <u32 as TrivialType>::from_bytes(num_pot_checkpoints) }? as usize;
817
818        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
819        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
820
821        let own_segments = if num_own_segment_roots > 0 {
822            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
823            // SAFETY: Unaligned and correct size
824            let first_local_segment_index = unsafe {
825                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
826            }
827            .as_inner();
828
829            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
830            // SAFETY: Valid pointer and size, no alignment requirements
831            let own_segment_roots = unsafe {
832                slice::from_raw_parts(
833                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
834                    num_own_segment_roots,
835                )
836            };
837            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
838
839            Some(OwnSegments {
840                first_local_segment_index,
841                segment_roots: own_segment_roots,
842            })
843        } else {
844            None
845        };
846
847        let (intermediate_shard_blocks, mut remainder) =
848            IntermediateShardBlocksInfo::try_from_bytes_unchecked(bytes)?;
849
850        let pot_checkpoints = remainder.split_off(..num_pot_checkpoints * PotCheckpoints::SIZE)?;
851        // SAFETY: Valid pointer and size, no alignment requirements
852        let pot_checkpoints = unsafe {
853            slice::from_raw_parts(
854                pot_checkpoints
855                    .as_ptr()
856                    .cast::<[u8; PotCheckpoints::SIZE]>(),
857                num_pot_checkpoints,
858            )
859        };
860        let pot_checkpoints = PotCheckpoints::slice_from_bytes(pot_checkpoints);
861
862        Some((
863            Self {
864                own_segments,
865                intermediate_shard_blocks,
866                pot_checkpoints,
867            },
868            remainder,
869        ))
870    }
871
872    /// Create an owned version of this body
873    #[cfg(feature = "alloc")]
874    #[inline(always)]
875    pub fn to_owned(self) -> OwnedBeaconChainBody {
876        if let Some(own_segments) = self.own_segments {
877            let first_local_segment_index = own_segments.first_local_segment_index;
878
879            OwnedBeaconChainBody::new(
880                own_segments.segment_roots.iter().copied().enumerate().map(
881                    |(index, segment_root)| {
882                        (
883                            first_local_segment_index + LocalSegmentIndex::from(index as u64),
884                            segment_root,
885                        )
886                    },
887                ),
888                self.intermediate_shard_blocks.iter(),
889                self.pot_checkpoints,
890            )
891            .expect("`self` is always a valid invariant; qed")
892        } else {
893            OwnedBeaconChainBody::new(
894                iter::empty(),
895                self.intermediate_shard_blocks.iter(),
896                self.pot_checkpoints,
897            )
898            .expect("`self` is always a valid invariant; qed")
899        }
900    }
901
902    /// Segment roots produced by this shard
903    #[inline(always)]
904    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
905        self.own_segments
906    }
907
908    /// Intermediate shard blocks
909    #[inline(always)]
910    pub fn intermediate_shard_blocks(&self) -> &IntermediateShardBlocksInfo<'a> {
911        &self.intermediate_shard_blocks
912    }
913
914    /// Proof of time checkpoints from after future proof of time of the parent block to the current
915    /// block's future proof of time (inclusive)
916    #[inline(always)]
917    pub fn pot_checkpoints(&self) -> &'a [PotCheckpoints] {
918        self.pot_checkpoints
919    }
920
921    /// Compute block body root
922    #[inline]
923    pub fn root(&self) -> Blake3Hash {
924        // TODO: Keyed hash
925        let root = BalancedMerkleTree::compute_root_only(&[
926            *self
927                .own_segments
928                .as_ref()
929                .map(OwnSegments::root)
930                .unwrap_or_default(),
931            *self.intermediate_shard_blocks.segments_root(),
932            *self.intermediate_shard_blocks.headers_root(),
933            blake3::hash(PotCheckpoints::bytes_from_slice(self.pot_checkpoints).as_flattened())
934                .into(),
935        ]);
936
937        Blake3Hash::new(root)
938    }
939}
940
941/// Information about leaf shard segments
942#[derive(Debug, Copy, Clone)]
943pub struct LeafShardOwnSegments<'a> {
944    /// Segment roots proof
945    pub segment_roots_proof: &'a [u8; OUT_LEN],
946    /// Segments produced by this shard
947    pub own_segments: OwnSegments<'a>,
948}
949
950/// Information about the leaf shard block container inside the intermediate shard block body
951#[derive(Debug, Clone)]
952pub struct LeafShardBlockInfo<'a> {
953    /// A block header that corresponds to a leaf shard
954    pub header: LeafShardHeader<'a>,
955    /// Segments in the corresponding block
956    pub segments: Option<LeafShardOwnSegments<'a>>,
957}
958
959/// Information about a collection of leaf shard blocks
960#[derive(Debug, Copy, Clone)]
961pub struct LeafShardBlocksInfo<'a> {
962    num_blocks: usize,
963    bytes: &'a [u8],
964}
965
966impl<'a> LeafShardBlocksInfo<'a> {
967    /// Create an instance from provided bytes.
968    ///
969    /// `bytes` do not need to be aligned.
970    ///
971    /// Returns an instance and remaining bytes on success.
972    #[inline]
973    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
974        // The layout here is as follows:
975        // * number of blocks: u16 as unaligned little-endian bytes
976        // * for each block:
977        //   * number of own segment roots: u8
978        // * padding to 8-bytes boundary with zeroes
979        // * for each block:
980        //   * block header: LeafShardHeader
981        //   * padding to 8-bytes boundary with zeroes
982        //   * local segment index of the first segment root (if any): `LocalSegmentIndex`
983        //   * segment roots proof (if there is at least one segment root)
984        //   * concatenated own segment roots
985
986        let num_blocks = bytes.split_off(..size_of::<u16>())?;
987        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
988
989        let bytes_start = bytes;
990
991        let mut counts = bytes.split_off(..num_blocks * size_of::<u8>())?;
992
993        let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
994
995        for _ in 0..num_blocks {
996            let num_own_segment_roots = usize::from(counts[0]);
997            counts = &counts[1..];
998
999            (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
1000
1001            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
1002
1003            if num_own_segment_roots > 0 {
1004                let _first_local_segment_index =
1005                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
1006                let _segment_roots_proof = remainder.split_off(..OUT_LEN)?;
1007                let _own_segment_roots =
1008                    remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1009            }
1010        }
1011
1012        let info = Self {
1013            num_blocks,
1014            bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1015        };
1016
1017        if !info.is_internally_consistent() {
1018            return None;
1019        }
1020
1021        Some((info, remainder))
1022    }
1023
1024    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1025    /// checks
1026    #[inline]
1027    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1028        // The layout here is as follows:
1029        // * number of blocks: u16 as unaligned little-endian bytes
1030        // * for each block:
1031        //   * number of own segment roots: u8
1032        // * padding to 8-bytes boundary with zeroes
1033        // * for each block:
1034        //   * block header: LeafShardHeader
1035        //   * padding to 8-bytes boundary with zeroes
1036        //   * local segment index of the first segment root (if any): `LocalSegmentIndex`
1037        //   * segment roots proof (if there is at least one segment root)
1038        //   * concatenated own segment roots
1039
1040        let num_blocks = bytes.split_off(..size_of::<u16>())?;
1041        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
1042
1043        let bytes_start = bytes;
1044
1045        let mut counts = bytes.split_off(..num_blocks * size_of::<u8>())?;
1046
1047        let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
1048
1049        for _ in 0..num_blocks {
1050            let num_own_segment_roots = usize::from(counts[0]);
1051            counts = &counts[1..];
1052
1053            (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
1054
1055            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
1056
1057            if num_own_segment_roots > 0 {
1058                let _first_local_segment_index =
1059                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
1060                let _segment_roots_proof = remainder.split_off(..OUT_LEN)?;
1061                let _own_segment_roots =
1062                    remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1063            }
1064        }
1065
1066        Some((
1067            Self {
1068                num_blocks,
1069                bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1070            },
1071            remainder,
1072        ))
1073    }
1074
1075    /// Check leaf shard info's internal consistency.
1076    ///
1077    /// This is usually not necessary to be called explicitly since internal consistency is checked
1078    /// by [`Self::try_from_bytes()`] internally.
1079    #[inline]
1080    pub fn is_internally_consistent(&self) -> bool {
1081        let mut last_leaf_shard_info = None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
1082
1083        self.iter().all(|leaf_shard_block| {
1084            let shard_index = leaf_shard_block.header.prefix.shard_index;
1085
1086            // Ensure increasing order of shard indices, block numbers and local segment indices
1087            if let Some((
1088                last_leaf_shard_index,
1089                last_leaf_shard_block_number,
1090                last_leaf_shard_first_local_segment_index,
1091            )) = last_leaf_shard_info
1092            {
1093                match last_leaf_shard_index.cmp(&shard_index) {
1094                    cmp::Ordering::Less => {
1095                        last_leaf_shard_info.replace((
1096                            shard_index,
1097                            leaf_shard_block.header.prefix.number,
1098                            leaf_shard_block
1099                                .segments
1100                                .as_ref()
1101                                .map(|segments| segments.own_segments.first_local_segment_index),
1102                        ));
1103                    }
1104                    cmp::Ordering::Equal => {
1105                        if last_leaf_shard_block_number >= leaf_shard_block.header.prefix.number {
1106                            return false;
1107                        }
1108                        if let Some(leaf_shard_segments) = &leaf_shard_block.segments {
1109                            if let Some(last_leaf_shard_first_local_segment_index) =
1110                                last_leaf_shard_first_local_segment_index
1111                                && last_leaf_shard_first_local_segment_index
1112                                    >= leaf_shard_segments.own_segments.first_local_segment_index
1113                            {
1114                                return false;
1115                            }
1116
1117                            last_leaf_shard_info.replace((
1118                                shard_index,
1119                                leaf_shard_block.header.prefix.number,
1120                                Some(leaf_shard_segments.own_segments.first_local_segment_index),
1121                            ));
1122                        } else {
1123                            last_leaf_shard_info.replace((
1124                                shard_index,
1125                                leaf_shard_block.header.prefix.number,
1126                                last_leaf_shard_first_local_segment_index,
1127                            ));
1128                        }
1129                    }
1130                    cmp::Ordering::Greater => {
1131                        return false;
1132                    }
1133                }
1134            } else {
1135                last_leaf_shard_info.replace((
1136                    shard_index,
1137                    leaf_shard_block.header.prefix.number,
1138                    leaf_shard_block
1139                        .segments
1140                        .as_ref()
1141                        .map(|segments| segments.own_segments.first_local_segment_index),
1142                ));
1143            }
1144
1145            let Some(segments) = leaf_shard_block.segments else {
1146                return true;
1147            };
1148
1149            BalancedMerkleTree::<2>::verify(
1150                &leaf_shard_block.header.result.body_root,
1151                array::from_ref(segments.segment_roots_proof),
1152                0,
1153                *segments.own_segments.root(),
1154            )
1155        })
1156    }
1157
1158    /// Iterator over leaf shard blocks in a collection
1159    #[inline]
1160    pub fn iter(
1161        &self,
1162    ) -> impl ExactSizeIterator<Item = LeafShardBlockInfo<'a>> + TrustedLen + use<'a> {
1163        // SAFETY: Checked in constructor
1164        let (mut counts, mut remainder) = unsafe {
1165            self.bytes
1166                .split_at_unchecked(self.num_blocks * size_of::<u8>())
1167        };
1168
1169        iter::repeat_with(move || {
1170            let num_own_segment_roots = usize::from(counts[0]);
1171            counts = &counts[1..];
1172
1173            // TODO: Unchecked method would have been helpful here
1174            let header;
1175            (header, remainder) = LeafShardHeader::try_from_bytes(remainder)
1176                .expect("Already checked in constructor; qed");
1177
1178            remainder = align_to_and_ensure_zero_padding::<u64>(remainder)
1179                .expect("Already checked in constructor; qed");
1180
1181            let segments = if num_own_segment_roots > 0 {
1182                let first_local_segment_index;
1183                // SAFETY: Checked in constructor
1184                (first_local_segment_index, remainder) =
1185                    unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
1186                // SAFETY: Correct alignment and size
1187                let first_local_segment_index =
1188                    *unsafe { LocalSegmentIndex::from_bytes_unchecked(first_local_segment_index) };
1189
1190                let segment_roots_proof;
1191                // SAFETY: Checked in constructor
1192                (segment_roots_proof, remainder) = unsafe { remainder.split_at_unchecked(OUT_LEN) };
1193                // SAFETY: Valid pointer and size, no alignment requirements
1194                let segment_roots_proof = unsafe {
1195                    segment_roots_proof
1196                        .as_ptr()
1197                        .cast::<[u8; OUT_LEN]>()
1198                        .as_ref_unchecked()
1199                };
1200
1201                let own_segment_roots;
1202                // SAFETY: Checked in constructor
1203                (own_segment_roots, remainder) = unsafe {
1204                    remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE)
1205                };
1206                // SAFETY: Valid pointer and size, no alignment requirements
1207                let own_segment_roots = unsafe {
1208                    slice::from_raw_parts(
1209                        own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1210                        num_own_segment_roots,
1211                    )
1212                };
1213                let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1214
1215                Some(LeafShardOwnSegments {
1216                    segment_roots_proof,
1217                    own_segments: OwnSegments {
1218                        first_local_segment_index,
1219                        segment_roots: own_segment_roots,
1220                    },
1221                })
1222            } else {
1223                None
1224            };
1225
1226            LeafShardBlockInfo { header, segments }
1227        })
1228        .take(self.num_blocks)
1229    }
1230
1231    /// Number of leaf shard blocks
1232    #[inline(always)]
1233    pub const fn len(&self) -> usize {
1234        self.num_blocks
1235    }
1236
1237    /// Returns `true` if there are no leaf shard blocks
1238    #[inline(always)]
1239    pub const fn is_empty(&self) -> bool {
1240        self.num_blocks == 0
1241    }
1242
1243    /// Compute the root of segments of the leaf shard blocks info.
1244    ///
1245    /// Returns the default value for an empty collection of segment roots.
1246    #[inline]
1247    pub fn segments_root(&self) -> Blake3Hash {
1248        let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
1249            self.iter().map(|shard_block_info| {
1250                shard_block_info
1251                    .segments
1252                    .map(|own_segments| {
1253                        own_segments
1254                            .own_segments
1255                            .root_with_shard_index(shard_block_info.header.prefix.shard_index)
1256                    })
1257                    .unwrap_or_default()
1258            }),
1259        )
1260        .unwrap_or_default();
1261
1262        Blake3Hash::new(root)
1263    }
1264
1265    /// Compute the root of headers of the leaf shard blocks info.
1266    ///
1267    /// Returns the default value for an empty collection of shard blocks.
1268    #[inline]
1269    pub fn headers_root(&self) -> Blake3Hash {
1270        let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
1271            self.iter().map(|shard_block_info| {
1272                // Hash the root again so we can prove it, otherwise root of headers is
1273                // indistinguishable from individual block roots and can be used to confuse
1274                // verifier
1275                single_block_hash(shard_block_info.header.root().as_ref())
1276                    .expect("Less than a single block worth of bytes; qed")
1277            }),
1278        )
1279        .unwrap_or_default();
1280
1281        Blake3Hash::new(root)
1282    }
1283}
1284
1285/// Block body that corresponds to an intermediate shard
1286#[derive(Debug, Copy, Clone, Yokeable)]
1287// Prevent creation of potentially broken invariants externally
1288#[non_exhaustive]
1289pub struct IntermediateShardBody<'a> {
1290    /// Segments produced by this shard
1291    own_segments: Option<OwnSegments<'a>>,
1292    /// Leaf shard blocks
1293    leaf_shard_blocks: LeafShardBlocksInfo<'a>,
1294}
1295
1296impl<'a> GenericBlockBody<'a> for IntermediateShardBody<'a> {
1297    const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
1298
1299    #[cfg(feature = "alloc")]
1300    type Owned = OwnedIntermediateShardBody;
1301
1302    #[cfg(feature = "alloc")]
1303    #[inline(always)]
1304    fn to_owned(self) -> Self::Owned {
1305        self.to_owned()
1306    }
1307
1308    #[inline(always)]
1309    fn root(&self) -> Blake3Hash {
1310        self.root()
1311    }
1312}
1313
1314impl<'a> IntermediateShardBody<'a> {
1315    /// Create an instance from provided bytes.
1316    ///
1317    /// `bytes` do not need to be aligned.
1318    ///
1319    /// Returns an instance and remaining bytes on success.
1320    #[inline]
1321    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1322        // The layout here is as follows:
1323        // * number of own segment roots: u8
1324        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1325        // * concatenated own segment roots
1326        // * leaf shard blocks: LeafShardBlocksInfo
1327
1328        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1329        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1330
1331        let own_segments = if num_own_segment_roots > 0 {
1332            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1333            // SAFETY: Unaligned and correct size
1334            let first_local_segment_index = unsafe {
1335                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1336            }
1337            .as_inner();
1338
1339            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1340            // SAFETY: Valid pointer and size, no alignment requirements
1341            let own_segment_roots = unsafe {
1342                slice::from_raw_parts(
1343                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1344                    num_own_segment_roots,
1345                )
1346            };
1347            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1348
1349            Some(OwnSegments {
1350                first_local_segment_index,
1351                segment_roots: own_segment_roots,
1352            })
1353        } else {
1354            None
1355        };
1356
1357        let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes(bytes)?;
1358
1359        let body = Self {
1360            own_segments,
1361            leaf_shard_blocks,
1362        };
1363
1364        if !body.is_internally_consistent() {
1365            return None;
1366        }
1367
1368        Some((body, remainder))
1369    }
1370
1371    /// Check block body's internal consistency.
1372    ///
1373    /// This is usually not necessary to be called explicitly since internal consistency is checked
1374    /// by [`Self::try_from_bytes()`] internally.
1375    #[inline]
1376    pub fn is_internally_consistent(&self) -> bool {
1377        // Nothing to check here
1378        true
1379    }
1380
1381    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1382    /// checks
1383    #[inline]
1384    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1385        // The layout here is as follows:
1386        // * number of own segment roots: u8
1387        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1388        // * concatenated own segment roots
1389        // * leaf shard blocks: LeafShardBlocksInfo
1390
1391        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1392        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1393
1394        let own_segments = if num_own_segment_roots > 0 {
1395            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1396            // SAFETY: Unaligned and correct size
1397            let first_local_segment_index = unsafe {
1398                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1399            }
1400            .as_inner();
1401
1402            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1403            // SAFETY: Valid pointer and size, no alignment requirements
1404            let own_segment_roots = unsafe {
1405                slice::from_raw_parts(
1406                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1407                    num_own_segment_roots,
1408                )
1409            };
1410            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1411
1412            Some(OwnSegments {
1413                first_local_segment_index,
1414                segment_roots: own_segment_roots,
1415            })
1416        } else {
1417            None
1418        };
1419
1420        let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes_unchecked(bytes)?;
1421
1422        Some((
1423            Self {
1424                own_segments,
1425                leaf_shard_blocks,
1426            },
1427            remainder,
1428        ))
1429    }
1430
1431    /// Segment roots produced by this shard
1432    #[inline(always)]
1433    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1434        self.own_segments
1435    }
1436
1437    /// Leaf shard blocks
1438    #[inline(always)]
1439    pub fn leaf_shard_blocks(&self) -> &LeafShardBlocksInfo<'a> {
1440        &self.leaf_shard_blocks
1441    }
1442
1443    /// Create an owned version of this body
1444    #[cfg(feature = "alloc")]
1445    #[inline(always)]
1446    pub fn to_owned(self) -> OwnedIntermediateShardBody {
1447        if let Some(own_segments) = self.own_segments {
1448            let first_local_segment_index = own_segments.first_local_segment_index;
1449
1450            OwnedIntermediateShardBody::new(
1451                own_segments.segment_roots.iter().copied().enumerate().map(
1452                    |(index, segment_root)| {
1453                        (
1454                            first_local_segment_index + LocalSegmentIndex::from(index as u64),
1455                            segment_root,
1456                        )
1457                    },
1458                ),
1459                self.leaf_shard_blocks.iter(),
1460            )
1461            .expect("`self` is always a valid invariant; qed")
1462        } else {
1463            OwnedIntermediateShardBody::new(iter::empty(), self.leaf_shard_blocks.iter())
1464                .expect("`self` is always a valid invariant; qed")
1465        }
1466    }
1467
1468    /// Compute block body root
1469    #[inline]
1470    pub fn root(&self) -> Blake3Hash {
1471        // Explicit nested trees to emphasize that the proof size for segments is just one hash
1472        let root = BalancedMerkleTree::compute_root_only(&[
1473            BalancedMerkleTree::compute_root_only(&[
1474                *self
1475                    .own_segments
1476                    .as_ref()
1477                    .map(OwnSegments::root)
1478                    .unwrap_or_default(),
1479                *self.leaf_shard_blocks.segments_root(),
1480            ]),
1481            *self.leaf_shard_blocks.headers_root(),
1482        ]);
1483
1484        Blake3Hash::new(root)
1485    }
1486}
1487
1488/// Collection of transactions
1489#[derive(Debug, Copy, Clone)]
1490pub struct Transactions<'a> {
1491    num_transactions: usize,
1492    bytes: &'a [u8],
1493}
1494
1495impl<'a> Transactions<'a> {
1496    /// Create an instance from provided bytes.
1497    ///
1498    /// `bytes` do not need to be aligned.
1499    ///
1500    /// Returns an instance and remaining bytes on success.
1501    #[inline]
1502    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1503        // The layout here is as follows:
1504        // * number of transactions: u32 as unaligned little-endian bytes
1505        // * padding to 16-bytes boundary with zeroes
1506        // * for each transaction
1507        //   * transaction: Transaction
1508        //   * padding to 16-bytes boundary with zeroes
1509
1510        let num_transactions = bytes.split_off(..size_of::<u32>())?;
1511        let num_transactions = u32::from_le_bytes([
1512            num_transactions[0],
1513            num_transactions[1],
1514            num_transactions[2],
1515            num_transactions[3],
1516        ]) as usize;
1517
1518        let mut remainder = align_to_and_ensure_zero_padding::<u128>(bytes)?;
1519        let bytes_start = remainder;
1520
1521        for _ in 0..num_transactions {
1522            (_, remainder) = Transaction::try_from_bytes(bytes)?;
1523            remainder = align_to_and_ensure_zero_padding::<u128>(remainder)?;
1524        }
1525
1526        Some((
1527            Self {
1528                num_transactions,
1529                bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1530            },
1531            remainder,
1532        ))
1533    }
1534
1535    /// Iterator over transactions in a collection
1536    #[inline]
1537    pub fn iter(&self) -> impl ExactSizeIterator<Item = Transaction<'a>> + TrustedLen + use<'a> {
1538        let mut remainder = self.bytes;
1539
1540        iter::repeat_with(move || {
1541            // SAFETY: Checked in constructor
1542            let transaction = unsafe { Transaction::from_bytes_unchecked(remainder) };
1543
1544            remainder = &remainder[transaction.encoded_size()..];
1545            remainder = align_to_and_ensure_zero_padding::<u128>(remainder)
1546                .expect("Already checked in constructor; qed");
1547
1548            transaction
1549        })
1550        .take(self.num_transactions)
1551    }
1552
1553    /// Number of transactions
1554    #[inline(always)]
1555    pub const fn len(&self) -> usize {
1556        self.num_transactions
1557    }
1558
1559    /// Returns `true` if there are no transactions
1560    #[inline(always)]
1561    pub const fn is_empty(&self) -> bool {
1562        self.num_transactions == 0
1563    }
1564
1565    /// Compute the root of transactions.
1566    ///
1567    /// Returns the default value for an empty collection of transactions.
1568    #[inline]
1569    pub fn root(&self) -> Blake3Hash {
1570        let root = UnbalancedMerkleTree::compute_root_only::<{ u32::MAX as u64 }, _, _>(
1571            self.iter().map(|transaction| {
1572                // Hash the hash again so we can prove it, otherwise root of transactions is
1573                // indistinguishable from individual transaction roots and can be used to
1574                // confuse verifier
1575                single_block_hash(transaction.hash().as_ref())
1576                    .expect("Less than a single block worth of bytes; qed")
1577            }),
1578        )
1579        .unwrap_or_default();
1580
1581        Blake3Hash::new(root)
1582    }
1583}
1584
1585/// Block body that corresponds to a leaf shard
1586#[derive(Debug, Copy, Clone, Yokeable)]
1587// Prevent creation of potentially broken invariants externally
1588#[non_exhaustive]
1589pub struct LeafShardBody<'a> {
1590    /// Segments produced by this shard
1591    own_segments: Option<OwnSegments<'a>>,
1592    /// User transactions
1593    transactions: Transactions<'a>,
1594}
1595
1596impl<'a> GenericBlockBody<'a> for LeafShardBody<'a> {
1597    const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
1598
1599    #[cfg(feature = "alloc")]
1600    type Owned = OwnedLeafShardBody;
1601
1602    #[cfg(feature = "alloc")]
1603    #[inline(always)]
1604    fn to_owned(self) -> Self::Owned {
1605        self.to_owned()
1606    }
1607
1608    #[inline(always)]
1609    fn root(&self) -> Blake3Hash {
1610        self.root()
1611    }
1612}
1613
1614impl<'a> LeafShardBody<'a> {
1615    /// Create an instance from provided bytes.
1616    ///
1617    /// `bytes` do not need to be aligned.
1618    ///
1619    /// Returns an instance and remaining bytes on success.
1620    #[inline]
1621    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1622        // The layout here is as follows:
1623        // * number of own segment roots: u8
1624        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1625        // * concatenated own segment roots
1626        // * transactions: Transactions
1627
1628        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1629        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1630
1631        let own_segments = if num_own_segment_roots > 0 {
1632            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1633            // SAFETY: Unaligned and correct size
1634            let first_local_segment_index = unsafe {
1635                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1636            }
1637            .as_inner();
1638
1639            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1640            // SAFETY: Valid pointer and size, no alignment requirements
1641            let own_segment_roots = unsafe {
1642                slice::from_raw_parts(
1643                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1644                    num_own_segment_roots,
1645                )
1646            };
1647            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1648
1649            Some(OwnSegments {
1650                first_local_segment_index,
1651                segment_roots: own_segment_roots,
1652            })
1653        } else {
1654            None
1655        };
1656
1657        let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1658
1659        let body = Self {
1660            own_segments,
1661            transactions,
1662        };
1663
1664        if !body.is_internally_consistent() {
1665            return None;
1666        }
1667
1668        Some((body, remainder))
1669    }
1670
1671    /// Check block body's internal consistency.
1672    ///
1673    /// This is usually not necessary to be called explicitly since internal consistency is checked
1674    /// by [`Self::try_from_bytes()`] internally.
1675    #[inline]
1676    pub fn is_internally_consistent(&self) -> bool {
1677        // Nothing to check here
1678        true
1679    }
1680
1681    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1682    /// checks
1683    #[inline]
1684    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1685        // The layout here is as follows:
1686        // * number of own segment roots: u8
1687        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1688        // * concatenated own segment roots
1689        // * transactions: Transactions
1690
1691        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1692        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1693
1694        let own_segments = if num_own_segment_roots > 0 {
1695            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1696            // SAFETY: Unaligned and correct size
1697            let first_local_segment_index = unsafe {
1698                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1699            }
1700            .as_inner();
1701
1702            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1703            // SAFETY: Valid pointer and size, no alignment requirements
1704            let own_segment_roots = unsafe {
1705                slice::from_raw_parts(
1706                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1707                    num_own_segment_roots,
1708                )
1709            };
1710            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1711
1712            Some(OwnSegments {
1713                first_local_segment_index,
1714                segment_roots: own_segment_roots,
1715            })
1716        } else {
1717            None
1718        };
1719
1720        let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1721
1722        Some((
1723            Self {
1724                own_segments,
1725                transactions,
1726            },
1727            remainder,
1728        ))
1729    }
1730
1731    /// Segment roots produced by this shard
1732    #[inline(always)]
1733    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1734        self.own_segments
1735    }
1736
1737    /// User transactions
1738    #[inline(always)]
1739    pub fn transactions(&self) -> &Transactions<'a> {
1740        &self.transactions
1741    }
1742
1743    /// Create an owned version of this body
1744    #[cfg(feature = "alloc")]
1745    #[inline(always)]
1746    pub fn to_owned(self) -> OwnedLeafShardBody {
1747        let mut builder = if let Some(own_segments) = self.own_segments {
1748            let first_local_segment_index = own_segments.first_local_segment_index;
1749
1750            OwnedLeafShardBody::init(own_segments.segment_roots.iter().copied().enumerate().map(
1751                |(index, segment_root)| {
1752                    (
1753                        first_local_segment_index + LocalSegmentIndex::from(index as u64),
1754                        segment_root,
1755                    )
1756                },
1757            ))
1758            .expect("`self` is always a valid invariant; qed")
1759        } else {
1760            OwnedLeafShardBody::init(iter::empty())
1761                .expect("`self` is always a valid invariant; qed")
1762        };
1763        for transaction in self.transactions.iter() {
1764            builder
1765                .add_transaction(transaction)
1766                .expect("`self` is always a valid invariant; qed");
1767        }
1768
1769        builder.finish()
1770    }
1771
1772    /// Compute block body root
1773    #[inline]
1774    pub fn root(&self) -> Blake3Hash {
1775        let root = BalancedMerkleTree::compute_root_only(&[
1776            *self
1777                .own_segments
1778                .as_ref()
1779                .map(OwnSegments::root)
1780                .unwrap_or_default(),
1781            *self.transactions.root(),
1782        ]);
1783
1784        Blake3Hash::new(root)
1785    }
1786}
1787
1788/// Block body that together with [`BlockHeader`] form a [`Block`]
1789///
1790/// [`BlockHeader`]: crate::block::header::BlockHeader
1791/// [`Block`]: crate::block::Block
1792#[derive(Debug, Copy, Clone, From)]
1793pub enum BlockBody<'a> {
1794    /// Block body corresponds to the beacon chain
1795    BeaconChain(BeaconChainBody<'a>),
1796    /// Block body corresponds to an intermediate shard
1797    IntermediateShard(IntermediateShardBody<'a>),
1798    /// Block body corresponds to a leaf shard
1799    LeafShard(LeafShardBody<'a>),
1800}
1801
1802impl<'a> BlockBody<'a> {
1803    /// Try to create a new instance from provided bytes for the provided shard index.
1804    ///
1805    /// `bytes` do not need to be aligned.
1806    ///
1807    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
1808    /// bytes are not properly aligned or input is otherwise invalid.
1809    #[inline]
1810    pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
1811        match shard_kind {
1812            RealShardKind::BeaconChain => {
1813                let (body, remainder) = BeaconChainBody::try_from_bytes(bytes)?;
1814                Some((Self::BeaconChain(body), remainder))
1815            }
1816            RealShardKind::IntermediateShard => {
1817                let (body, remainder) = IntermediateShardBody::try_from_bytes(bytes)?;
1818                Some((Self::IntermediateShard(body), remainder))
1819            }
1820            RealShardKind::LeafShard => {
1821                let (body, remainder) = LeafShardBody::try_from_bytes(bytes)?;
1822                Some((Self::LeafShard(body), remainder))
1823            }
1824        }
1825    }
1826
1827    /// Check block body's internal consistency.
1828    ///
1829    /// This is usually not necessary to be called explicitly since internal consistency is checked
1830    /// by [`Self::try_from_bytes()`] internally.
1831    #[inline]
1832    pub fn is_internally_consistent(&self) -> bool {
1833        match self {
1834            Self::BeaconChain(body) => body.is_internally_consistent(),
1835            Self::IntermediateShard(body) => body.is_internally_consistent(),
1836            Self::LeafShard(body) => body.is_internally_consistent(),
1837        }
1838    }
1839
1840    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1841    /// checks
1842    #[inline]
1843    pub fn try_from_bytes_unchecked(
1844        bytes: &'a [u8],
1845        shard_kind: RealShardKind,
1846    ) -> Option<(Self, &'a [u8])> {
1847        match shard_kind {
1848            RealShardKind::BeaconChain => {
1849                let (body, remainder) = BeaconChainBody::try_from_bytes_unchecked(bytes)?;
1850                Some((Self::BeaconChain(body), remainder))
1851            }
1852            RealShardKind::IntermediateShard => {
1853                let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(bytes)?;
1854                Some((Self::IntermediateShard(body), remainder))
1855            }
1856            RealShardKind::LeafShard => {
1857                let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(bytes)?;
1858                Some((Self::LeafShard(body), remainder))
1859            }
1860        }
1861    }
1862
1863    /// Create an owned version of this body
1864    #[cfg(feature = "alloc")]
1865    #[inline(always)]
1866    pub fn to_owned(self) -> OwnedBlockBody {
1867        match self {
1868            Self::BeaconChain(body) => body.to_owned().into(),
1869            Self::IntermediateShard(body) => body.to_owned().into(),
1870            Self::LeafShard(body) => body.to_owned().into(),
1871        }
1872    }
1873
1874    /// Compute block body root.
1875    ///
1876    /// Block body hash is actually a Merkle Tree Root. The leaves are derived from individual
1877    /// fields this enum in the declaration order.
1878    #[inline]
1879    pub fn root(&self) -> Blake3Hash {
1880        match self {
1881            Self::BeaconChain(body) => body.root(),
1882            Self::IntermediateShard(body) => body.root(),
1883            Self::LeafShard(body) => body.root(),
1884        }
1885    }
1886}