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 crate::trees::{
19    BeaconChainBodyMerkleTree, ChildIntermediateShardBlocksMerkleTree,
20    ChildLeafShardBlocksMerkleTree, IntermediateShardBodyMerkleTree,
21    IntermediateShardsSegmentsMerkleTree, LeafShardBodyMerkleTree, LeafShardsSegmentsMerkleTree,
22    OwnSegmentsMerkleTree, SegmentRootsMerkleTree, ShardOwnSegmentsMerkleTree,
23    TransactionsMerkleTree,
24};
25#[cfg(feature = "alloc")]
26use ab_aligned_buffer::SharedAlignedBuffer;
27use ab_blake3::{BLOCK_LEN, single_block_hash};
28use ab_io_type::alignment::Alignment;
29use ab_io_type::trivial_type::TrivialType;
30use ab_io_type::unaligned::Unaligned;
31use core::iter::{Step, TrustedLen};
32use core::num::NonZeroU8;
33use core::{cmp, fmt, iter};
34use derive_more::From;
35use yoke::Yokeable;
36
37/// Generic block body
38pub trait GenericBlockBody<'a>
39where
40    Self: Copy + fmt::Debug + Into<BlockBody<'a>> + Send + Sync,
41{
42    /// Shard kind
43    const SHARD_KIND: RealShardKind;
44
45    /// Owned block body
46    #[cfg(feature = "alloc")]
47    type Owned: GenericOwnedBlockBody<Body<'a> = Self>
48    where
49        Self: 'a;
50
51    /// Turn into an owned version
52    #[cfg(feature = "alloc")]
53    fn to_owned(self) -> Self::Owned;
54
55    /// Compute block body root
56    fn root(&self) -> Blake3Hash;
57}
58
59/// Calculates a Merkle Tree root for a provided list of segment roots
60#[inline]
61pub fn compute_segments_root<'a, const MAX_SEGMENTS: u64, Iter>(segment_roots: Iter) -> Blake3Hash
62where
63    Iter: IntoIterator<Item = &'a SegmentRoot, IntoIter: TrustedLen>,
64{
65    let root = SegmentRootsMerkleTree::compute_root_only::<MAX_SEGMENTS, _, _>(
66        segment_roots.into_iter().copied(),
67    );
68
69    Blake3Hash::new(root.unwrap_or_default())
70}
71
72/// Own segments produced by a shard.
73///
74/// They have between 1 and `u8::MAX` segment roots, which is how many a body can store. The local
75/// segment index after the last segment root fits into `u64`, because that is where the next
76/// segment of the shard starts. Local segment indices never get close to `u64::MAX` in practice.
77#[derive(Debug, Copy, Clone)]
78pub struct OwnSegments<'a> {
79    /// Local segment index of the first own segment root
80    pub mut(self) first_local_segment_index: LocalSegmentIndex,
81    /// Segment roots produced by a shard
82    pub mut(self) segment_roots: &'a [SegmentRoot],
83}
84
85impl<'a> OwnSegments<'a> {
86    /// Create own segments from the local segment index of the first segment root and the roots.
87    ///
88    /// Returns `None` if there are no segment roots, more than `u8::MAX` of them, or the local
89    /// segment index after the last one doesn't fit into `u64`.
90    #[inline]
91    pub fn new(
92        first_local_segment_index: LocalSegmentIndex,
93        segment_roots: &'a [SegmentRoot],
94    ) -> Option<Self> {
95        if segment_roots.is_empty()
96            || segment_roots.len() > usize::from(u8::MAX)
97            || LocalSegmentIndex::forward_checked(first_local_segment_index, segment_roots.len())
98                .is_none()
99        {
100            return None;
101        }
102
103        Some(Self::new_unchecked(
104            first_local_segment_index,
105            segment_roots,
106        ))
107    }
108
109    /// Create own segments without checking them.
110    ///
111    /// Iterators of child shard blocks use it for own segments that their parsers have already
112    /// checked: layouts only allow between 1 and `u8::MAX` segment roots, and the parsers check
113    /// that the local segment index after the last one fits into `u64`.
114    #[inline(always)]
115    const fn new_unchecked(
116        first_local_segment_index: LocalSegmentIndex,
117        segment_roots: &'a [SegmentRoot],
118    ) -> Self {
119        Self {
120            first_local_segment_index,
121            segment_roots,
122        }
123    }
124
125    /// Number of segment roots
126    #[inline(always)]
127    pub fn num_segment_roots(&self) -> NonZeroU8 {
128        u8::try_from(self.segment_roots.len())
129            .ok()
130            .and_then(NonZeroU8::new)
131            .expect("Constructors ensure between 1 and `u8::MAX` segment roots; qed")
132    }
133
134    /// Iterator over segment roots with their local segment indices
135    #[inline]
136    pub fn iter(
137        &self,
138    ) -> impl ExactSizeIterator<Item = (LocalSegmentIndex, &'a SegmentRoot)> + TrustedLen + use<'a>
139    {
140        let first_local_segment_index = self.first_local_segment_index;
141
142        self.segment_roots
143            .iter()
144            .enumerate()
145            .map(move |(offset, segment_root)| {
146                let local_segment_index =
147                    LocalSegmentIndex::forward_checked(first_local_segment_index, offset).expect(
148                        "`Self::new()` and parsers check that the index after the last root fits \
149                        into `u64`; qed",
150                    );
151
152                (local_segment_index, segment_root)
153            })
154    }
155
156    /// Compute the root of own segments
157    #[inline]
158    pub fn root(&self) -> Blake3Hash {
159        let root = OwnSegmentsMerkleTree::compute_root_only(&[
160            single_block_hash(self.first_local_segment_index.as_bytes())
161                .expect("Less than a single block worth of bytes; qed"),
162            *compute_segments_root::<{ u64::from(u8::MAX) }, _>(self.segment_roots),
163        ]);
164
165        Blake3Hash::new(root)
166    }
167
168    /// Compute the root of own segments while mixing in shard index.
169    ///
170    /// This method is useful for deriving roots on intermediate shard that can be verified by the
171    /// beacon chain later (which will have just shard index available rather than the whole leaf
172    /// shard block header).
173    #[inline]
174    pub fn root_with_shard_index(&self, shard_index: ShardIndex) -> Blake3Hash {
175        let root = ShardOwnSegmentsMerkleTree::compute_root_only(&[
176            {
177                const {
178                    assert!((ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize <= BLOCK_LEN);
179                }
180                let mut pair = [0u8; (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize];
181                pair[..ShardIndex::SIZE as usize].copy_from_slice(shard_index.as_bytes());
182                pair[ShardIndex::SIZE as usize..]
183                    .copy_from_slice(self.first_local_segment_index.as_bytes());
184
185                single_block_hash(&pair).expect("Less than a single block worth of bytes; qed")
186            },
187            *compute_segments_root::<{ u64::from(u8::MAX) }, _>(self.segment_roots),
188        ]);
189
190        Blake3Hash::new(root)
191    }
192}
193
194/// Compute the root of segments of leaf shard blocks.
195///
196/// Takes positions of leaf shard blocks with segments among `num_leaf_shard_blocks` blocks, their
197/// shard indices and segments. Returns `None` if positions aren't strictly increasing or not
198/// smaller than `num_leaf_shard_blocks`.
199fn compute_leaf_shards_segments_root<'a, Iter>(
200    num_leaf_shard_blocks: u16,
201    leaf_shard_blocks_segments: Iter,
202) -> Option<Blake3Hash>
203where
204    Iter: IntoIterator<Item = (u16, ShardIndex, OwnSegments<'a>)>,
205{
206    let mut leaves =
207        leaf_shard_blocks_segments
208            .into_iter()
209            .map(|(position, shard_index, own_segments)| {
210                (
211                    u64::from(position),
212                    *own_segments.root_with_shard_index(shard_index),
213                )
214            });
215
216    if num_leaf_shard_blocks == 0 {
217        return leaves.next().is_none().then_some(Blake3Hash::default());
218    }
219
220    LeafShardsSegmentsMerkleTree::compute_root_only_sparse::<{ u64::from(u16::MAX) }, _, _>(
221        u64::from(num_leaf_shard_blocks),
222        leaves,
223    )
224    .map(Blake3Hash::new)
225}
226
227/// Compute the body root of an intermediate shard block
228fn intermediate_shard_body_root(
229    own_segments: Option<&OwnSegments<'_>>,
230    leaf_shards_segments_root: &Blake3Hash,
231    leaf_shard_blocks_root: &Blake3Hash,
232) -> Blake3Hash {
233    Blake3Hash::new(IntermediateShardBodyMerkleTree::compute_root_only_array(&[
234        *own_segments.map_or_default(OwnSegments::root),
235        **leaf_shards_segments_root,
236        **leaf_shard_blocks_root,
237    ]))
238}
239
240/// Information about intermediate shard block
241#[derive(Debug, Clone)]
242#[expect(
243    clippy::partial_pub_fields,
244    reason = "Intentionally exposing immediately decoded fields above and hiding the rest of the implementation"
245)]
246pub struct IntermediateShardBlockInfo<'a> {
247    /// A block header that corresponds to an intermediate shard
248    pub header: IntermediateShardHeader<'a>,
249    /// Segments in the corresponding block
250    pub own_segments: Option<OwnSegments<'a>>,
251    num_leaf_shard_blocks: u16,
252    num_leaf_shard_blocks_with_segments: u16,
253    leaf_shards_segments_bytes: &'a [u8],
254}
255
256impl<'a> IntermediateShardBlockInfo<'a> {
257    /// Number of leaf shard blocks in the corresponding intermediate shard block
258    #[inline(always)]
259    pub const fn num_leaf_shard_blocks(&self) -> u16 {
260        self.num_leaf_shard_blocks
261    }
262
263    /// Number of leaf shard blocks with segments in the corresponding intermediate shard block
264    #[inline(always)]
265    pub const fn num_leaf_shard_blocks_with_segments(&self) -> u16 {
266        self.num_leaf_shard_blocks_with_segments
267    }
268
269    /// Segments of leaf shards in the corresponding intermediate shard block, with positions of
270    /// their blocks among leaf shard blocks of the intermediate shard block
271    #[inline]
272    pub fn leaf_shards_segments(
273        &self,
274    ) -> impl ExactSizeIterator<Item = (u16, ShardIndex, OwnSegments<'a>)> + TrustedLen + use<'a>
275    {
276        // SAFETY: Checked in constructor
277        let (counts, mut remainder) = unsafe {
278            self.leaf_shards_segments_bytes.split_at_unchecked(
279                usize::from(self.num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
280            )
281        };
282        counts.iter().map(move |&num_own_segment_roots| {
283            let num_own_segment_roots = usize::from(num_own_segment_roots);
284
285            let position;
286            // SAFETY: Checked in constructor
287            (position, remainder) = unsafe { remainder.split_at_unchecked(size_of::<u16>()) };
288            let position = u16::from_le_bytes([position[0], position[1]]);
289
290            let shard_index;
291            // SAFETY: Checked in constructor
292            (shard_index, remainder) =
293                unsafe { remainder.split_at_unchecked(ShardIndex::SIZE as usize) };
294            let shard_index = Unaligned::<ShardIndex>::from_bytes_exact(shard_index)
295                .expect("Correct size; qed")
296                .as_inner();
297
298            let first_local_segment_index;
299            // SAFETY: Checked in constructor
300            (first_local_segment_index, remainder) =
301                unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
302            let first_local_segment_index =
303                Unaligned::<LocalSegmentIndex>::from_bytes_exact(first_local_segment_index)
304                    .expect("Correct size; qed")
305                    .as_inner();
306
307            let own_segment_roots;
308            (own_segment_roots, remainder) =
309                SegmentRoot::slice_from_bytes(remainder, num_own_segment_roots)
310                    .expect("Already checked in constructor; qed");
311
312            (
313                position,
314                shard_index,
315                OwnSegments::new_unchecked(first_local_segment_index, own_segment_roots),
316            )
317        })
318    }
319
320    /// Compute the root of segments of leaf shards in the corresponding intermediate shard block
321    #[inline]
322    fn leaf_shards_segments_root(&self) -> Blake3Hash {
323        compute_leaf_shards_segments_root(self.num_leaf_shard_blocks, self.leaf_shards_segments())
324            .expect("Positions are checked in constructor; qed")
325    }
326
327    /// Compute the body root of the corresponding intermediate shard block.
328    ///
329    /// It commits to the root of leaf shard blocks in the header, which is the same as in the body
330    /// of a consistent intermediate shard block.
331    #[inline]
332    pub fn body_root(&self) -> Blake3Hash {
333        intermediate_shard_body_root(
334            self.own_segments.as_ref(),
335            &self.leaf_shards_segments_root(),
336            self.header.leaf_shard_blocks_root(),
337        )
338    }
339}
340
341/// Information about a collection of intermediate shard blocks
342#[derive(Debug, Copy, Clone)]
343pub struct IntermediateShardBlocksInfo<'a> {
344    num_blocks: usize,
345    bytes: &'a [u8],
346}
347
348impl<'a> IntermediateShardBlocksInfo<'a> {
349    /// Create an instance from provided bytes.
350    ///
351    /// `bytes` do not need to be aligned, but padding in the layout depends on their address and
352    /// must be present in full.
353    ///
354    /// Blocks must be ordered by shard index, and blocks of the same shard by block number and by
355    /// local segment index of own segments. Leaf shards with segments in a block must be children
356    /// of its shard, ordered by shard index, and segments of the same leaf shard by local segment
357    /// index. The body root of each block must match its header.
358    ///
359    /// Returns an instance and remaining bytes on success.
360    #[inline]
361    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
362        // The layout here is as follows:
363        // * number of blocks: u16 as unaligned little-endian bytes
364        // * for each block:
365        //   * number of own segment roots: u8
366        //   * number of leaf shard blocks: u16 as unaligned little-endian bytes
367        //   * number of leaf shard blocks with segments: u16 as unaligned little-endian bytes
368        // * padding to 8-bytes boundary with zeroes
369        // * for each block:
370        //   * block header: IntermediateShardHeader
371        //   * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
372        //   * concatenated own segment roots
373        //   * for each leaf shard block with segments:
374        //     * number of own segment roots: NonZeroU8
375        //   * for each leaf shard block with segments:
376        //     * position among leaf shard blocks, strictly increasing: u16 as unaligned
377        //       little-endian bytes
378        //     * shard index: u32 as unaligned little-endian bytes
379        //     * local segment index of the first segment root: unaligned `LocalSegmentIndex`
380        //     * concatenated own segment roots
381        //   * padding to 8-bytes boundary with zeroes
382
383        let num_blocks = bytes.split_off(..size_of::<u16>())?;
384        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
385
386        let bytes_start = bytes;
387
388        let mut counts =
389            bytes.split_off(..num_blocks * (size_of::<u8>() + 2 * size_of::<u16>()))?;
390
391        let mut remainder = align_to_and_ensure_zero_padding(bytes, Alignment::of::<u64>())?;
392
393        for _ in 0..num_blocks {
394            let num_own_segment_roots = usize::from(counts[0]);
395            let num_leaf_shard_blocks = u16::from_le_bytes([counts[1], counts[2]]);
396            let num_leaf_shard_blocks_with_segments =
397                usize::from(u16::from_le_bytes([counts[3], counts[4]]));
398            counts = &counts[5..];
399
400            (_, remainder) = IntermediateShardHeader::try_from_bytes(remainder)?;
401
402            if num_own_segment_roots > 0 {
403                let first_local_segment_index;
404                (first_local_segment_index, remainder) =
405                    Unaligned::<LocalSegmentIndex>::from_bytes(remainder)?;
406                // The local segment index after the last segment root must fit into `u64`
407                let _next_local_segment_index = LocalSegmentIndex::forward_checked(
408                    first_local_segment_index.as_inner(),
409                    num_own_segment_roots,
410                )?;
411                (_, remainder) = SegmentRoot::slice_from_bytes(remainder, num_own_segment_roots)?;
412            }
413
414            let leaf_shard_segment_roots_counts =
415                remainder.split_off(..num_leaf_shard_blocks_with_segments * size_of::<u8>())?;
416
417            // Positions of leaf shard blocks with segments must be strictly increasing and smaller
418            // than the number of leaf shard blocks, the root of their segments relies on it
419            let mut next_position = 0;
420            for &num_own_segment_roots in leaf_shard_segment_roots_counts {
421                let num_own_segment_roots = NonZeroU8::new(num_own_segment_roots)?;
422                let position = remainder.split_off(..size_of::<u16>())?;
423                let position = u16::from_le_bytes([position[0], position[1]]);
424                if position < next_position || position >= num_leaf_shard_blocks {
425                    return None;
426                }
427                next_position = position + 1;
428                let _shard_index = remainder.split_off(..ShardIndex::SIZE as usize)?;
429                let first_local_segment_index;
430                (first_local_segment_index, remainder) =
431                    Unaligned::<LocalSegmentIndex>::from_bytes(remainder)?;
432                // The local segment index after the last segment root must fit into `u64`
433                let _next_local_segment_index = LocalSegmentIndex::forward_checked(
434                    first_local_segment_index.as_inner(),
435                    usize::from(num_own_segment_roots.get()),
436                )?;
437                (_, remainder) = SegmentRoot::slice_from_bytes(
438                    remainder,
439                    usize::from(num_own_segment_roots.get()),
440                )?;
441            }
442
443            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())?;
444        }
445
446        let info = Self {
447            num_blocks,
448            bytes: &bytes_start[..bytes_start.len() - remainder.len()],
449        };
450
451        if !info.is_internally_consistent() {
452            return None;
453        }
454
455        Some((info, remainder))
456    }
457
458    /// Check intermediate shard info's internal consistency, see [`Self::try_from_bytes()`]
459    #[inline]
460    fn is_internally_consistent(&self) -> bool {
461        let mut last_intermediate_shard_info =
462            None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
463
464        self.iter().all(|intermediate_shard_block| {
465            let shard_index = intermediate_shard_block.header.prefix.shard_index;
466
467            // Ensure increasing order of shard indices, block numbers and local segment indices
468            if let Some((
469                last_intermediate_shard_index,
470                last_intermediate_shard_block_number,
471                last_intermediate_shard_first_local_segment_index,
472            )) = last_intermediate_shard_info
473            {
474                match last_intermediate_shard_index.cmp(&shard_index) {
475                    cmp::Ordering::Less => {
476                        last_intermediate_shard_info.replace((
477                            shard_index,
478                            intermediate_shard_block.header.prefix.number,
479                            intermediate_shard_block
480                                .own_segments
481                                .as_ref()
482                                .map(|own_segments| own_segments.first_local_segment_index),
483                        ));
484                    }
485                    cmp::Ordering::Equal => {
486                        if last_intermediate_shard_block_number
487                            >= intermediate_shard_block.header.prefix.number
488                        {
489                            return false;
490                        }
491                        if let Some(own_segments) = &intermediate_shard_block.own_segments {
492                            if let Some(last_intermediate_shard_first_local_segment_index) =
493                                last_intermediate_shard_first_local_segment_index
494                                && last_intermediate_shard_first_local_segment_index
495                                    >= own_segments.first_local_segment_index
496                            {
497                                return false;
498                            }
499
500                            last_intermediate_shard_info.replace((
501                                shard_index,
502                                intermediate_shard_block.header.prefix.number,
503                                Some(own_segments.first_local_segment_index),
504                            ));
505                        } else {
506                            last_intermediate_shard_info.replace((
507                                shard_index,
508                                intermediate_shard_block.header.prefix.number,
509                                last_intermediate_shard_first_local_segment_index,
510                            ));
511                        }
512                    }
513                    cmp::Ordering::Greater => {
514                        return false;
515                    }
516                }
517            } else {
518                last_intermediate_shard_info.replace((
519                    shard_index,
520                    intermediate_shard_block.header.prefix.number,
521                    intermediate_shard_block
522                        .own_segments
523                        .as_ref()
524                        .map(|own_segments| own_segments.first_local_segment_index),
525                ));
526            }
527
528            let mut last_leaf_shard_info = None::<(ShardIndex, LocalSegmentIndex)>;
529
530            // Ensure increasing order of shard indices and local segment indices
531            if !intermediate_shard_block.leaf_shards_segments().all(
532                |(_position, leaf_shard_index, own_segments)| {
533                    if !leaf_shard_index.is_child_of(shard_index) {
534                        return false;
535                    }
536
537                    if let Some((
538                        last_leaf_shard_index,
539                        last_leaf_shard_first_local_segment_index,
540                    )) = last_leaf_shard_info
541                    {
542                        match last_leaf_shard_index.cmp(&leaf_shard_index) {
543                            cmp::Ordering::Less => {
544                                // Expected
545                            }
546                            cmp::Ordering::Equal => {
547                                if last_leaf_shard_first_local_segment_index
548                                    >= own_segments.first_local_segment_index
549                                {
550                                    return false;
551                                }
552                            }
553                            cmp::Ordering::Greater => {
554                                return false;
555                            }
556                        }
557                    }
558                    last_leaf_shard_info
559                        .replace((leaf_shard_index, own_segments.first_local_segment_index));
560
561                    true
562                },
563            ) {
564                return false;
565            }
566
567            // Also checks blocks without segments, positions and the number of leaf shard blocks,
568            // the root of segments of leaf shards commits to them
569            intermediate_shard_block.body_root() == intermediate_shard_block.header.result.body_root
570        })
571    }
572
573    /// Iterator over intermediate shard blocks in a collection
574    #[inline]
575    pub fn iter(
576        &self,
577    ) -> impl ExactSizeIterator<Item = IntermediateShardBlockInfo<'a>> + TrustedLen + use<'a> {
578        // SAFETY: Checked in constructor
579        let (mut counts, remainder) = unsafe {
580            self.bytes
581                .split_at_unchecked(self.num_blocks * (size_of::<u8>() + 2 * size_of::<u16>()))
582        };
583        let mut remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
584            .expect("Already checked in constructor; qed");
585
586        iter::repeat_with(move || {
587            let num_own_segment_roots = usize::from(counts[0]);
588            let num_leaf_shard_blocks = u16::from_le_bytes([counts[1], counts[2]]);
589            let num_leaf_shard_blocks_with_segments = u16::from_le_bytes([counts[3], counts[4]]);
590            counts = &counts[5..];
591
592            // TODO: Unchecked method would have been helpful here
593            let header;
594            (header, remainder) = IntermediateShardHeader::try_from_bytes(remainder)
595                .expect("Already checked in constructor; qed");
596
597            let own_segments = if num_own_segment_roots > 0 {
598                let first_local_segment_index;
599                // SAFETY: Checked in constructor
600                (first_local_segment_index, remainder) =
601                    unsafe { remainder.split_at_unchecked(size_of::<LocalSegmentIndex>()) };
602                let first_local_segment_index =
603                    Unaligned::<LocalSegmentIndex>::from_bytes_exact(first_local_segment_index)
604                        .expect("Correct size; qed")
605                        .as_inner();
606
607                let own_segment_roots;
608                (own_segment_roots, remainder) =
609                    SegmentRoot::slice_from_bytes(remainder, num_own_segment_roots)
610                        .expect("Already checked in constructor; qed");
611
612                Some(OwnSegments::new_unchecked(
613                    first_local_segment_index,
614                    own_segment_roots,
615                ))
616            } else {
617                None
618            };
619
620            // SAFETY: Checked in constructor
621            let leaf_shard_segment_roots_counts = unsafe {
622                remainder.get_unchecked(
623                    ..usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
624                )
625            };
626
627            let leaf_shards_segments_bytes;
628            // SAFETY: Checked in constructor
629            (leaf_shards_segments_bytes, remainder) = unsafe {
630                remainder.split_at_unchecked(
631                    usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>()
632                        + usize::from(num_leaf_shard_blocks_with_segments)
633                            * (size_of::<u16>()
634                                + (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize)
635                        + leaf_shard_segment_roots_counts.iter().fold(
636                            0usize,
637                            |acc, &num_own_segment_roots| {
638                                acc + usize::from(num_own_segment_roots) * SegmentRoot::SIZE
639                            },
640                        ),
641                )
642            };
643
644            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
645                .expect("Already checked in constructor; qed");
646
647            IntermediateShardBlockInfo {
648                header,
649                own_segments,
650                num_leaf_shard_blocks,
651                num_leaf_shard_blocks_with_segments,
652                leaf_shards_segments_bytes,
653            }
654        })
655        .take(self.num_blocks)
656    }
657
658    /// Number of intermediate shard blocks
659    #[inline(always)]
660    pub const fn len(&self) -> usize {
661        self.num_blocks
662    }
663
664    /// Returns `true` if there are no intermediate shard blocks
665    #[inline(always)]
666    pub const fn is_empty(&self) -> bool {
667        self.num_blocks == 0
668    }
669
670    /// Compute the root of segments of the intermediate shard blocks info.
671    ///
672    /// Returns the default value for an empty collection of shard blocks.
673    #[inline]
674    pub fn segments_root(&self) -> Blake3Hash {
675        let root = IntermediateShardsSegmentsMerkleTree::compute_root_only::<
676            { u64::from(u16::MAX) },
677            _,
678            _,
679        >(self.iter().map(|shard_block_info| {
680            // The hasher doesn't depend on the number of leaves, so this is the inner node over the
681            // first two leaves of the body tree of the intermediate shard block, which commits to
682            // the same segments
683            IntermediateShardBodyMerkleTree::compute_root_only_array(&[
684                *shard_block_info
685                    .own_segments
686                    .as_ref()
687                    .map_or_default(OwnSegments::root),
688                *shard_block_info.leaf_shards_segments_root(),
689            ])
690        }))
691        .unwrap_or_default();
692
693        Blake3Hash::new(root)
694    }
695
696    /// Compute the root of child block roots, which the header stores.
697    ///
698    /// Returns the default value for an empty collection of shard blocks.
699    #[inline]
700    pub fn headers_root(&self) -> Blake3Hash {
701        let root = ChildIntermediateShardBlocksMerkleTree::compute_root_only::<
702            { u64::from(u16::MAX) },
703            _,
704            _,
705        >(
706            self.iter()
707                .map(|shard_block_info| **shard_block_info.header.root()),
708        )
709        .unwrap_or_default();
710
711        Blake3Hash::new(root)
712    }
713}
714
715/// Block body that corresponds to the beacon chain
716#[derive(Debug, Copy, Clone, Yokeable)]
717// Prevent creation of potentially broken invariants externally
718#[non_exhaustive]
719pub struct BeaconChainBody<'a> {
720    /// Segments produced by this shard
721    own_segments: Option<OwnSegments<'a>>,
722    /// Intermediate shard blocks
723    intermediate_shard_blocks: IntermediateShardBlocksInfo<'a>,
724    /// Proof of time checkpoints from after future proof of time of the parent block to the
725    /// current block's future proof of time (inclusive)
726    pot_checkpoints: &'a [PotCheckpoints],
727    /// All bytes of the body, which owned versions are created from
728    #[cfg(feature = "alloc")]
729    bytes: &'a [u8],
730}
731
732impl<'a> GenericBlockBody<'a> for BeaconChainBody<'a> {
733    const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
734
735    #[cfg(feature = "alloc")]
736    type Owned = OwnedBeaconChainBody;
737
738    #[cfg(feature = "alloc")]
739    #[inline(always)]
740    fn to_owned(self) -> Self::Owned {
741        self.to_owned()
742    }
743
744    #[inline(always)]
745    fn root(&self) -> Blake3Hash {
746        self.root()
747    }
748}
749
750impl<'a> BeaconChainBody<'a> {
751    /// Create an instance from provided correctly aligned bytes.
752    ///
753    /// `bytes` must be 16-bytes aligned, as in owned bodies, because padding in the layout depends
754    /// on the address of the bytes. Bodies longer than `u32::MAX` bytes are rejected, since owned
755    /// bodies can't hold them.
756    ///
757    /// Returns an instance and remaining bytes on success.
758    #[inline]
759    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
760        // The layout here is as follows:
761        // * number of PoT checkpoints: u32 as aligned little-endian bytes
762        // * number of own segment roots: u8
763        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
764        // * concatenated own segment roots
765        // * intermediate shard blocks: IntermediateShardBlocksInfo
766        // * concatenated PoT checkpoints
767
768        if !Alignment::MAX.is_slice_aligned(bytes) {
769            return None;
770        }
771
772        let bytes_start = bytes;
773
774        let num_pot_checkpoints;
775        (num_pot_checkpoints, bytes) = u32::from_bytes(bytes)?;
776        let num_pot_checkpoints = *num_pot_checkpoints as usize;
777
778        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
779        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
780
781        let own_segments = if num_own_segment_roots > 0 {
782            let first_local_segment_index;
783            (first_local_segment_index, bytes) = Unaligned::<LocalSegmentIndex>::from_bytes(bytes)?;
784
785            let own_segment_roots;
786            (own_segment_roots, bytes) =
787                SegmentRoot::slice_from_bytes(bytes, num_own_segment_roots)?;
788
789            Some(OwnSegments::new(
790                first_local_segment_index.as_inner(),
791                own_segment_roots,
792            )?)
793        } else {
794            None
795        };
796
797        let (intermediate_shard_blocks, remainder) =
798            IntermediateShardBlocksInfo::try_from_bytes(bytes)?;
799
800        let (pot_checkpoints, remainder) =
801            PotCheckpoints::slice_from_bytes(remainder, num_pot_checkpoints)?;
802
803        let bytes = &bytes_start[..bytes_start.len() - remainder.len()];
804        // An owned body can't hold more than `u32::MAX` bytes, which PoT checkpoints alone can
805        // exceed
806        if u32::try_from(bytes.len()).is_err() {
807            return None;
808        }
809
810        let body = Self {
811            own_segments,
812            intermediate_shard_blocks,
813            pot_checkpoints,
814            #[cfg(feature = "alloc")]
815            bytes,
816        };
817
818        Some((body, remainder))
819    }
820
821    /// Create an owned version of this body
822    #[cfg(feature = "alloc")]
823    #[inline(always)]
824    pub fn to_owned(self) -> OwnedBeaconChainBody {
825        OwnedBeaconChainBody::from_buffer(SharedAlignedBuffer::from_bytes(self.bytes)).expect(
826            "The parser accepted the same bytes at a 16-byte aligned address, as in the buffer, \
827            and checked that they fit into it; qed",
828        )
829    }
830
831    /// Segment roots produced by this shard
832    #[inline(always)]
833    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
834        self.own_segments
835    }
836
837    /// Intermediate shard blocks
838    #[inline(always)]
839    pub fn intermediate_shard_blocks(&self) -> &IntermediateShardBlocksInfo<'a> {
840        &self.intermediate_shard_blocks
841    }
842
843    /// Proof of time checkpoints from after future proof of time of the parent block to the current
844    /// block's future proof of time (inclusive)
845    #[inline(always)]
846    pub fn pot_checkpoints(&self) -> &'a [PotCheckpoints] {
847        self.pot_checkpoints
848    }
849
850    /// Compute block body root
851    #[inline]
852    pub fn root(&self) -> Blake3Hash {
853        self.root_with_intermediate_shard_blocks_root(
854            &self.intermediate_shard_blocks.headers_root(),
855        )
856    }
857
858    /// The same as [`Self::root()`], but with an already computed
859    /// [`IntermediateShardBlocksInfo::headers_root()`], which is expensive to compute
860    #[inline]
861    pub(crate) fn root_with_intermediate_shard_blocks_root(
862        &self,
863        intermediate_shard_blocks_root: &Blake3Hash,
864    ) -> Blake3Hash {
865        let root = BeaconChainBodyMerkleTree::compute_root_only(&[
866            *self.own_segments.as_ref().map_or_default(OwnSegments::root),
867            *self.intermediate_shard_blocks.segments_root(),
868            **intermediate_shard_blocks_root,
869            blake3::hash(PotCheckpoints::slice_as_bytes(self.pot_checkpoints)).into(),
870        ]);
871
872        Blake3Hash::new(root)
873    }
874}
875
876/// Compute the body root of a leaf shard block
877fn leaf_shard_body_root(
878    own_segments: Option<&OwnSegments<'_>>,
879    transactions_root: &Blake3Hash,
880) -> Blake3Hash {
881    Blake3Hash::new(LeafShardBodyMerkleTree::compute_root_only(&[
882        *own_segments.map_or_default(OwnSegments::root),
883        **transactions_root,
884    ]))
885}
886
887/// Information about the leaf shard block container inside the intermediate shard block body
888#[derive(Debug, Clone)]
889pub struct LeafShardBlockInfo<'a> {
890    /// A block header that corresponds to a leaf shard
891    pub header: LeafShardHeader<'a>,
892    /// Root of transactions in the corresponding block
893    pub transactions_root: &'a Blake3Hash,
894    /// Segments in the corresponding block
895    pub own_segments: Option<OwnSegments<'a>>,
896}
897
898/// Information about a collection of leaf shard blocks
899#[derive(Debug, Copy, Clone)]
900pub struct LeafShardBlocksInfo<'a> {
901    num_blocks: u16,
902    bytes: &'a [u8],
903}
904
905impl<'a> LeafShardBlocksInfo<'a> {
906    /// Create an instance from provided bytes.
907    ///
908    /// `bytes` do not need to be aligned, but padding in the layout depends on their address and
909    /// must be present in full.
910    ///
911    /// Blocks must be ordered by shard index, and blocks of the same shard by block number and by
912    /// local segment index of own segments. The body root of each block must match its header.
913    ///
914    /// Returns an instance and remaining bytes on success.
915    #[inline]
916    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
917        // The layout here is as follows:
918        // * number of blocks: u16 as unaligned little-endian bytes
919        // * for each block:
920        //   * number of own segment roots: u8
921        // * padding to 8-bytes boundary with zeroes
922        // * for each block:
923        //   * block header: LeafShardHeader
924        //   * padding to 8-bytes boundary with zeroes
925        //   * root of transactions: `Blake3Hash`
926        //   * local segment index of the first segment root (if any): `LocalSegmentIndex`
927        //   * concatenated own segment roots
928
929        let num_blocks = bytes.split_off(..size_of::<u16>())?;
930        let num_blocks = u16::from_le_bytes([num_blocks[0], num_blocks[1]]);
931
932        let bytes_start = bytes;
933
934        let mut counts = bytes.split_off(..usize::from(num_blocks) * size_of::<u8>())?;
935
936        let mut remainder = align_to_and_ensure_zero_padding(bytes, Alignment::of::<u64>())?;
937
938        for _ in 0..num_blocks {
939            let num_own_segment_roots = usize::from(counts[0]);
940            counts = &counts[1..];
941
942            (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
943
944            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())?;
945
946            (_, remainder) = Blake3Hash::from_bytes(remainder)?;
947
948            if num_own_segment_roots > 0 {
949                let first_local_segment_index;
950                (first_local_segment_index, remainder) = LocalSegmentIndex::from_bytes(remainder)?;
951                // The local segment index after the last segment root must fit into `u64`
952                let _next_local_segment_index = LocalSegmentIndex::forward_checked(
953                    *first_local_segment_index,
954                    num_own_segment_roots,
955                )?;
956                (_, remainder) = SegmentRoot::slice_from_bytes(remainder, num_own_segment_roots)?;
957            }
958        }
959
960        let info = Self {
961            num_blocks,
962            bytes: &bytes_start[..bytes_start.len() - remainder.len()],
963        };
964
965        if !info.is_internally_consistent() {
966            return None;
967        }
968
969        Some((info, remainder))
970    }
971
972    /// Check leaf shard info's internal consistency, see [`Self::try_from_bytes()`]
973    #[inline]
974    fn is_internally_consistent(&self) -> bool {
975        let mut last_leaf_shard_info = None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
976
977        self.iter().all(|leaf_shard_block| {
978            let shard_index = leaf_shard_block.header.prefix.shard_index;
979
980            // Ensure increasing order of shard indices, block numbers and local segment indices
981            if let Some((
982                last_leaf_shard_index,
983                last_leaf_shard_block_number,
984                last_leaf_shard_first_local_segment_index,
985            )) = last_leaf_shard_info
986            {
987                match last_leaf_shard_index.cmp(&shard_index) {
988                    cmp::Ordering::Less => {
989                        last_leaf_shard_info.replace((
990                            shard_index,
991                            leaf_shard_block.header.prefix.number,
992                            leaf_shard_block
993                                .own_segments
994                                .as_ref()
995                                .map(|own_segments| own_segments.first_local_segment_index),
996                        ));
997                    }
998                    cmp::Ordering::Equal => {
999                        if last_leaf_shard_block_number >= leaf_shard_block.header.prefix.number {
1000                            return false;
1001                        }
1002                        if let Some(own_segments) = &leaf_shard_block.own_segments {
1003                            if let Some(last_leaf_shard_first_local_segment_index) =
1004                                last_leaf_shard_first_local_segment_index
1005                                && last_leaf_shard_first_local_segment_index
1006                                    >= own_segments.first_local_segment_index
1007                            {
1008                                return false;
1009                            }
1010
1011                            last_leaf_shard_info.replace((
1012                                shard_index,
1013                                leaf_shard_block.header.prefix.number,
1014                                Some(own_segments.first_local_segment_index),
1015                            ));
1016                        } else {
1017                            last_leaf_shard_info.replace((
1018                                shard_index,
1019                                leaf_shard_block.header.prefix.number,
1020                                last_leaf_shard_first_local_segment_index,
1021                            ));
1022                        }
1023                    }
1024                    cmp::Ordering::Greater => {
1025                        return false;
1026                    }
1027                }
1028            } else {
1029                last_leaf_shard_info.replace((
1030                    shard_index,
1031                    leaf_shard_block.header.prefix.number,
1032                    leaf_shard_block
1033                        .own_segments
1034                        .as_ref()
1035                        .map(|own_segments| own_segments.first_local_segment_index),
1036                ));
1037            }
1038
1039            // Also checks blocks without segments, whose body root has a zero leaf for segments
1040            leaf_shard_body_root(
1041                leaf_shard_block.own_segments.as_ref(),
1042                leaf_shard_block.transactions_root,
1043            ) == leaf_shard_block.header.result.body_root
1044        })
1045    }
1046
1047    /// Iterator over leaf shard blocks in a collection
1048    #[inline]
1049    pub fn iter(
1050        &self,
1051    ) -> impl ExactSizeIterator<Item = LeafShardBlockInfo<'a>> + TrustedLen + use<'a> {
1052        // SAFETY: Checked in constructor
1053        let (mut counts, remainder) = unsafe {
1054            self.bytes
1055                .split_at_unchecked(usize::from(self.num_blocks) * size_of::<u8>())
1056        };
1057        let mut remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
1058            .expect("Already checked in constructor; qed");
1059
1060        iter::repeat_with(move || {
1061            let num_own_segment_roots = usize::from(counts[0]);
1062            counts = &counts[1..];
1063
1064            // TODO: Unchecked method would have been helpful here
1065            let header;
1066            (header, remainder) = LeafShardHeader::try_from_bytes(remainder)
1067                .expect("Already checked in constructor; qed");
1068
1069            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
1070                .expect("Already checked in constructor; qed");
1071
1072            let transactions_root;
1073            // SAFETY: Checked in constructor
1074            (transactions_root, remainder) =
1075                unsafe { remainder.split_at_unchecked(Blake3Hash::SIZE) };
1076            let transactions_root = Blake3Hash::from_bytes_exact(transactions_root)
1077                .expect("Already checked in constructor; qed");
1078
1079            let own_segments = if num_own_segment_roots > 0 {
1080                let first_local_segment_index;
1081                // SAFETY: Checked in constructor
1082                (first_local_segment_index, remainder) =
1083                    unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
1084                let first_local_segment_index =
1085                    *LocalSegmentIndex::from_bytes_exact(first_local_segment_index)
1086                        .expect("Already checked in constructor; qed");
1087
1088                let own_segment_roots;
1089                (own_segment_roots, remainder) =
1090                    SegmentRoot::slice_from_bytes(remainder, num_own_segment_roots)
1091                        .expect("Already checked in constructor; qed");
1092
1093                Some(OwnSegments::new_unchecked(
1094                    first_local_segment_index,
1095                    own_segment_roots,
1096                ))
1097            } else {
1098                None
1099            };
1100
1101            LeafShardBlockInfo {
1102                header,
1103                transactions_root,
1104                own_segments,
1105            }
1106        })
1107        .take(usize::from(self.num_blocks))
1108    }
1109
1110    /// Number of leaf shard blocks
1111    #[inline(always)]
1112    pub const fn len(&self) -> usize {
1113        self.num_blocks as usize
1114    }
1115
1116    /// Returns `true` if there are no leaf shard blocks
1117    #[inline(always)]
1118    pub const fn is_empty(&self) -> bool {
1119        self.num_blocks == 0
1120    }
1121
1122    /// Compute the root of segments of the leaf shard blocks info.
1123    ///
1124    /// Returns the default value for an empty collection of shard blocks.
1125    #[inline]
1126    pub fn segments_root(&self) -> Blake3Hash {
1127        // Blocks come first, so positions never step past `u16::MAX`
1128        compute_leaf_shards_segments_root(
1129            self.num_blocks,
1130            self.iter()
1131                .zip(0..)
1132                .filter_map(|(shard_block_info, position)| {
1133                    shard_block_info.own_segments.map(|own_segments| {
1134                        (
1135                            position,
1136                            shard_block_info.header.prefix.shard_index,
1137                            own_segments,
1138                        )
1139                    })
1140                }),
1141        )
1142        .expect("Positions are strictly increasing and smaller than the number of blocks; qed")
1143    }
1144
1145    /// Compute the root of child block roots, which the header stores.
1146    ///
1147    /// Returns the default value for an empty collection of shard blocks.
1148    #[inline]
1149    pub fn headers_root(&self) -> Blake3Hash {
1150        let root =
1151            ChildLeafShardBlocksMerkleTree::compute_root_only::<{ u64::from(u16::MAX) }, _, _>(
1152                self.iter()
1153                    .map(|shard_block_info| **shard_block_info.header.root()),
1154            )
1155            .unwrap_or_default();
1156
1157        Blake3Hash::new(root)
1158    }
1159}
1160
1161/// Block body that corresponds to an intermediate shard
1162#[derive(Debug, Copy, Clone, Yokeable)]
1163// Prevent creation of potentially broken invariants externally
1164#[non_exhaustive]
1165pub struct IntermediateShardBody<'a> {
1166    /// Segments produced by this shard
1167    own_segments: Option<OwnSegments<'a>>,
1168    /// Leaf shard blocks
1169    leaf_shard_blocks: LeafShardBlocksInfo<'a>,
1170    /// All bytes of the body, which owned versions are created from
1171    #[cfg(feature = "alloc")]
1172    bytes: &'a [u8],
1173}
1174
1175impl<'a> GenericBlockBody<'a> for IntermediateShardBody<'a> {
1176    const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
1177
1178    #[cfg(feature = "alloc")]
1179    type Owned = OwnedIntermediateShardBody;
1180
1181    #[cfg(feature = "alloc")]
1182    #[inline(always)]
1183    fn to_owned(self) -> Self::Owned {
1184        self.to_owned()
1185    }
1186
1187    #[inline(always)]
1188    fn root(&self) -> Blake3Hash {
1189        self.root()
1190    }
1191}
1192
1193impl<'a> IntermediateShardBody<'a> {
1194    /// Create an instance from provided correctly aligned bytes.
1195    ///
1196    /// `bytes` must be 16-bytes aligned, as in owned bodies, because padding in the layout depends
1197    /// on the address of the bytes. Bodies longer than `u32::MAX` bytes are rejected, since owned
1198    /// bodies can't hold them.
1199    ///
1200    /// Returns an instance and remaining bytes on success.
1201    #[inline]
1202    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1203        // The layout here is as follows:
1204        // * number of own segment roots: u8
1205        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1206        // * concatenated own segment roots
1207        // * leaf shard blocks: LeafShardBlocksInfo
1208
1209        if !Alignment::MAX.is_slice_aligned(bytes) {
1210            return None;
1211        }
1212
1213        let bytes_start = bytes;
1214
1215        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1216        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1217
1218        let own_segments = if num_own_segment_roots > 0 {
1219            let first_local_segment_index;
1220            (first_local_segment_index, bytes) = Unaligned::<LocalSegmentIndex>::from_bytes(bytes)?;
1221
1222            let own_segment_roots;
1223            (own_segment_roots, bytes) =
1224                SegmentRoot::slice_from_bytes(bytes, num_own_segment_roots)?;
1225
1226            Some(OwnSegments::new(
1227                first_local_segment_index.as_inner(),
1228                own_segment_roots,
1229            )?)
1230        } else {
1231            None
1232        };
1233
1234        let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes(bytes)?;
1235
1236        let bytes = &bytes_start[..bytes_start.len() - remainder.len()];
1237        // An owned body can't hold more than `u32::MAX` bytes, which leaf shard blocks don't
1238        // exceed with current limits, but might if those change
1239        if u32::try_from(bytes.len()).is_err() {
1240            return None;
1241        }
1242
1243        let body = Self {
1244            own_segments,
1245            leaf_shard_blocks,
1246            #[cfg(feature = "alloc")]
1247            bytes,
1248        };
1249
1250        Some((body, remainder))
1251    }
1252
1253    /// Segment roots produced by this shard
1254    #[inline(always)]
1255    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1256        self.own_segments
1257    }
1258
1259    /// Leaf shard blocks
1260    #[inline(always)]
1261    pub fn leaf_shard_blocks(&self) -> &LeafShardBlocksInfo<'a> {
1262        &self.leaf_shard_blocks
1263    }
1264
1265    /// Create an owned version of this body
1266    #[cfg(feature = "alloc")]
1267    #[inline(always)]
1268    pub fn to_owned(self) -> OwnedIntermediateShardBody {
1269        OwnedIntermediateShardBody::from_buffer(SharedAlignedBuffer::from_bytes(self.bytes)).expect(
1270            "The parser accepted the same bytes at a 16-byte aligned address, as in the buffer, \
1271            and checked that they fit into it; qed",
1272        )
1273    }
1274
1275    /// Compute block body root
1276    #[inline]
1277    pub fn root(&self) -> Blake3Hash {
1278        self.root_with_leaf_shard_blocks_root(&self.leaf_shard_blocks.headers_root())
1279    }
1280
1281    /// The same as [`Self::root()`], but with an already computed
1282    /// [`LeafShardBlocksInfo::headers_root()`], which is expensive to compute
1283    #[inline]
1284    pub(crate) fn root_with_leaf_shard_blocks_root(
1285        &self,
1286        leaf_shard_blocks_root: &Blake3Hash,
1287    ) -> Blake3Hash {
1288        intermediate_shard_body_root(
1289            self.own_segments.as_ref(),
1290            &self.leaf_shard_blocks.segments_root(),
1291            leaf_shard_blocks_root,
1292        )
1293    }
1294}
1295
1296/// Collection of transactions
1297#[derive(Debug, Copy, Clone)]
1298pub struct Transactions<'a> {
1299    num_transactions: usize,
1300    bytes: &'a [u8],
1301}
1302
1303impl<'a> Transactions<'a> {
1304    /// Create an instance from provided bytes.
1305    ///
1306    /// `bytes` do not need to be aligned, but padding in the layout depends on their address and
1307    /// must be present in full.
1308    ///
1309    /// Returns an instance and remaining bytes on success.
1310    #[inline]
1311    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1312        // The layout here is as follows:
1313        // * number of transactions: u32 as unaligned little-endian bytes
1314        // * padding to 16-bytes boundary with zeroes
1315        // * for each transaction
1316        //   * transaction: Transaction
1317        //   * padding to 16-bytes boundary with zeroes
1318
1319        let num_transactions = bytes.split_off(..size_of::<u32>())?;
1320        let num_transactions = u32::from_le_bytes([
1321            num_transactions[0],
1322            num_transactions[1],
1323            num_transactions[2],
1324            num_transactions[3],
1325        ]) as usize;
1326
1327        let mut remainder = align_to_and_ensure_zero_padding(bytes, Alignment::MAX)?;
1328        let bytes_start = remainder;
1329
1330        for _ in 0..num_transactions {
1331            (_, remainder) = Transaction::try_from_bytes(remainder)?;
1332            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
1333        }
1334
1335        Some((
1336            Self {
1337                num_transactions,
1338                bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1339            },
1340            remainder,
1341        ))
1342    }
1343
1344    /// Iterator over transactions in a collection
1345    #[inline]
1346    pub fn iter(&self) -> impl ExactSizeIterator<Item = Transaction<'a>> + TrustedLen + use<'a> {
1347        let mut remainder = self.bytes;
1348
1349        iter::repeat_with(move || {
1350            let transaction;
1351            (transaction, remainder) = Transaction::try_from_bytes(remainder)
1352                .expect("Already checked in constructor; qed");
1353            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)
1354                .expect("Already checked in constructor; qed");
1355
1356            transaction
1357        })
1358        .take(self.num_transactions)
1359    }
1360
1361    /// Number of transactions
1362    #[inline(always)]
1363    pub const fn len(&self) -> usize {
1364        self.num_transactions
1365    }
1366
1367    /// Returns `true` if there are no transactions
1368    #[inline(always)]
1369    pub const fn is_empty(&self) -> bool {
1370        self.num_transactions == 0
1371    }
1372
1373    /// Compute the root of transactions.
1374    ///
1375    /// Returns the default value for an empty collection of transactions.
1376    #[inline]
1377    pub fn root(&self) -> Blake3Hash {
1378        let root = TransactionsMerkleTree::compute_root_only::<{ u64::from(u32::MAX) }, _, _>(
1379            self.iter().map(|transaction| ***transaction.hash()),
1380        )
1381        .unwrap_or_default();
1382
1383        Blake3Hash::new(root)
1384    }
1385}
1386
1387/// Block body that corresponds to a leaf shard
1388#[derive(Debug, Copy, Clone, Yokeable)]
1389// Prevent creation of potentially broken invariants externally
1390#[non_exhaustive]
1391pub struct LeafShardBody<'a> {
1392    /// Segments produced by this shard
1393    own_segments: Option<OwnSegments<'a>>,
1394    /// User transactions
1395    transactions: Transactions<'a>,
1396    /// All bytes of the body, which owned versions are created from
1397    #[cfg(feature = "alloc")]
1398    bytes: &'a [u8],
1399}
1400
1401impl<'a> GenericBlockBody<'a> for LeafShardBody<'a> {
1402    const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
1403
1404    #[cfg(feature = "alloc")]
1405    type Owned = OwnedLeafShardBody;
1406
1407    #[cfg(feature = "alloc")]
1408    #[inline(always)]
1409    fn to_owned(self) -> Self::Owned {
1410        self.to_owned()
1411    }
1412
1413    #[inline(always)]
1414    fn root(&self) -> Blake3Hash {
1415        self.root()
1416    }
1417}
1418
1419impl<'a> LeafShardBody<'a> {
1420    /// Create an instance from provided correctly aligned bytes.
1421    ///
1422    /// `bytes` must be 16-bytes aligned, as in owned bodies, because padding in the layout depends
1423    /// on the address of the bytes. Bodies longer than `u32::MAX` bytes are rejected, since owned
1424    /// bodies can't hold them.
1425    ///
1426    /// Returns an instance and remaining bytes on success.
1427    #[inline]
1428    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1429        // The layout here is as follows:
1430        // * number of own segment roots: u8
1431        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1432        // * concatenated own segment roots
1433        // * transactions: Transactions
1434
1435        if !Alignment::MAX.is_slice_aligned(bytes) {
1436            return None;
1437        }
1438
1439        let bytes_start = bytes;
1440
1441        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1442        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1443
1444        let own_segments = if num_own_segment_roots > 0 {
1445            let first_local_segment_index;
1446            (first_local_segment_index, bytes) = Unaligned::<LocalSegmentIndex>::from_bytes(bytes)?;
1447
1448            let own_segment_roots;
1449            (own_segment_roots, bytes) =
1450                SegmentRoot::slice_from_bytes(bytes, num_own_segment_roots)?;
1451
1452            Some(OwnSegments::new(
1453                first_local_segment_index.as_inner(),
1454                own_segment_roots,
1455            )?)
1456        } else {
1457            None
1458        };
1459
1460        let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1461
1462        let bytes = &bytes_start[..bytes_start.len() - remainder.len()];
1463        // An owned body can't hold more than `u32::MAX` bytes, which transactions alone can
1464        // exceed
1465        if u32::try_from(bytes.len()).is_err() {
1466            return None;
1467        }
1468
1469        let body = Self {
1470            own_segments,
1471            transactions,
1472            #[cfg(feature = "alloc")]
1473            bytes,
1474        };
1475
1476        Some((body, remainder))
1477    }
1478
1479    /// Segment roots produced by this shard
1480    #[inline(always)]
1481    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1482        self.own_segments
1483    }
1484
1485    /// User transactions
1486    #[inline(always)]
1487    pub fn transactions(&self) -> &Transactions<'a> {
1488        &self.transactions
1489    }
1490
1491    /// Create an owned version of this body
1492    #[cfg(feature = "alloc")]
1493    #[inline(always)]
1494    pub fn to_owned(self) -> OwnedLeafShardBody {
1495        OwnedLeafShardBody::from_buffer(SharedAlignedBuffer::from_bytes(self.bytes)).expect(
1496            "The parser accepted the same bytes at a 16-byte aligned address, as in the buffer, \
1497            and checked that they fit into it; qed",
1498        )
1499    }
1500
1501    /// Compute block body root
1502    #[inline]
1503    pub fn root(&self) -> Blake3Hash {
1504        leaf_shard_body_root(self.own_segments.as_ref(), &self.transactions.root())
1505    }
1506}
1507
1508/// Block body that together with [`BlockHeader`] form a [`Block`]
1509///
1510/// [`BlockHeader`]: crate::block::header::BlockHeader
1511/// [`Block`]: crate::block::Block
1512#[derive(Debug, Copy, Clone, From)]
1513pub enum BlockBody<'a> {
1514    /// Block body corresponds to the beacon chain
1515    BeaconChain(BeaconChainBody<'a>),
1516    /// Block body corresponds to an intermediate shard
1517    IntermediateShard(IntermediateShardBody<'a>),
1518    /// Block body corresponds to a leaf shard
1519    LeafShard(LeafShardBody<'a>),
1520}
1521
1522impl<'a> BlockBody<'a> {
1523    /// Try to create a new instance from provided bytes for the provided shard index.
1524    ///
1525    /// `bytes` must be 16-bytes aligned. Bodies longer than `u32::MAX` bytes are rejected.
1526    ///
1527    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
1528    /// bytes are not properly aligned or input is otherwise invalid.
1529    #[inline]
1530    pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
1531        match shard_kind {
1532            RealShardKind::BeaconChain => {
1533                let (body, remainder) = BeaconChainBody::try_from_bytes(bytes)?;
1534                Some((Self::BeaconChain(body), remainder))
1535            }
1536            RealShardKind::IntermediateShard => {
1537                let (body, remainder) = IntermediateShardBody::try_from_bytes(bytes)?;
1538                Some((Self::IntermediateShard(body), remainder))
1539            }
1540            RealShardKind::LeafShard => {
1541                let (body, remainder) = LeafShardBody::try_from_bytes(bytes)?;
1542                Some((Self::LeafShard(body), remainder))
1543            }
1544        }
1545    }
1546
1547    /// Create an owned version of this body
1548    #[cfg(feature = "alloc")]
1549    #[inline(always)]
1550    pub fn to_owned(self) -> OwnedBlockBody {
1551        match self {
1552            Self::BeaconChain(body) => body.to_owned().into(),
1553            Self::IntermediateShard(body) => body.to_owned().into(),
1554            Self::LeafShard(body) => body.to_owned().into(),
1555        }
1556    }
1557
1558    /// Compute block body root.
1559    ///
1560    /// Block body hash is actually a Merkle Tree Root. The leaves are derived from individual
1561    /// fields this enum in the declaration order.
1562    #[inline]
1563    pub fn root(&self) -> Blake3Hash {
1564        match self {
1565            Self::BeaconChain(body) => body.root(),
1566            Self::IntermediateShard(body) => body.root(),
1567            Self::LeafShard(body) => body.root(),
1568        }
1569    }
1570}