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