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::alignment::Alignment;
20use ab_io_type::trivial_type::TrivialType;
21use ab_io_type::unaligned::Unaligned;
22use ab_merkle_tree::balanced::BalancedMerkleTree;
23use ab_merkle_tree::unbalanced::UnbalancedMerkleTree;
24use core::iter::TrustedLen;
25use core::{array, cmp, fmt, iter, slice};
26use derive_more::From;
27use yoke::Yokeable;
28
29/// Generic block body
30pub trait GenericBlockBody<'a>
31where
32    Self: Copy + fmt::Debug + Into<BlockBody<'a>> + Send + Sync,
33{
34    /// Shard kind
35    const SHARD_KIND: RealShardKind;
36
37    /// Owned block body
38    #[cfg(feature = "alloc")]
39    type Owned: GenericOwnedBlockBody<Body<'a> = Self>
40    where
41        Self: 'a;
42
43    /// Turn into an owned version
44    #[cfg(feature = "alloc")]
45    fn to_owned(self) -> Self::Owned;
46
47    /// Compute block body root
48    fn root(&self) -> Blake3Hash;
49}
50
51/// Calculates a Merkle Tree root for a provided list of segment roots
52#[inline]
53pub fn compute_segments_root<'a, const MAX_SEGMENTS: u64, Iter>(segment_roots: Iter) -> Blake3Hash
54where
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::<{ u64::from(u8::MAX) }, _>(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::<{ u64::from(u8::MAX) }, _>(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(bytes, Alignment::of::<u64>())?;
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(remainder, Alignment::of::<u64>())?;
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(bytes, Alignment::of::<u64>())?;
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(remainder, Alignment::of::<u64>())?;
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            let leaf_shards_root =
479                UnbalancedMerkleTree::compute_root_only::<{ u64::from(u16::MAX) }, _, _>(
480                    intermediate_shard_block.leaf_shards_segments().map(
481                        |(shard_index, own_segments)| {
482                            own_segments.root_with_shard_index(shard_index)
483                        },
484                    ),
485                )
486                .unwrap_or_default();
487
488            BalancedMerkleTree::<2>::verify(
489                &intermediate_shard_block.header.result.body_root,
490                array::from_ref(segments_proof),
491                0,
492                BalancedMerkleTree::compute_root_only(&[
493                    *intermediate_shard_block
494                        .own_segments
495                        .as_ref()
496                        .map(OwnSegments::root)
497                        .unwrap_or_default(),
498                    leaf_shards_root,
499                ]),
500            )
501        })
502    }
503
504    /// Iterator over intermediate shard blocks in a collection
505    #[inline]
506    pub fn iter(
507        &self,
508    ) -> impl ExactSizeIterator<Item = IntermediateShardBlockInfo<'a>> + TrustedLen + use<'a> {
509        // SAFETY: Checked in constructor
510        let (mut counts, mut remainder) = unsafe {
511            self.bytes
512                .split_at_unchecked(self.num_blocks * (size_of::<u8>() + size_of::<u16>()))
513        };
514
515        iter::repeat_with(move || {
516            let num_own_segment_roots = usize::from(counts[0]);
517            let num_leaf_shard_blocks_with_segments = counts[1];
518            counts = &counts[2..];
519
520            // TODO: Unchecked method would have been helpful here
521            let header;
522            (header, remainder) = IntermediateShardHeader::try_from_bytes(remainder)
523                .expect("Already checked in constructor; qed");
524
525            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
526                .expect("Already checked in constructor; qed");
527
528            let segments_proof =
529                if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
530                    let segments_proof;
531                    // SAFETY: Checked in constructor
532                    (segments_proof, remainder) = unsafe { remainder.split_at_unchecked(OUT_LEN) };
533                    // SAFETY: Valid pointer and size, no alignment requirements
534                    Some(unsafe {
535                        segments_proof
536                            .as_ptr()
537                            .cast::<[u8; OUT_LEN]>()
538                            .as_ref_unchecked()
539                    })
540                } else {
541                    None
542                };
543
544            let own_segments = if num_own_segment_roots > 0 {
545                let first_local_segment_index;
546                // SAFETY: Checked in constructor
547                (first_local_segment_index, remainder) =
548                    unsafe { remainder.split_at_unchecked(size_of::<LocalSegmentIndex>()) };
549                // SAFETY: Correct alignment and size
550                let first_local_segment_index =
551                    *unsafe { LocalSegmentIndex::from_bytes_unchecked(first_local_segment_index) };
552
553                let own_segment_roots;
554                // SAFETY: Checked in constructor
555                (own_segment_roots, remainder) = unsafe {
556                    remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE)
557                };
558                // SAFETY: Valid pointer and size, no alignment requirements
559                let own_segment_roots = unsafe {
560                    slice::from_raw_parts(
561                        own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
562                        num_own_segment_roots,
563                    )
564                };
565                let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
566
567                Some(OwnSegments {
568                    first_local_segment_index,
569                    segment_roots: own_segment_roots,
570                })
571            } else {
572                None
573            };
574
575            // SAFETY: Checked in constructor
576            let leaf_shard_segment_roots_counts = unsafe {
577                remainder.get_unchecked(
578                    ..usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
579                )
580            };
581
582            let leaf_shards_segments_bytes;
583            // SAFETY: Checked in constructor
584            (leaf_shards_segments_bytes, remainder) = unsafe {
585                remainder.split_at_unchecked(
586                    usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>()
587                        + usize::from(num_leaf_shard_blocks_with_segments)
588                            * (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize
589                        + leaf_shard_segment_roots_counts.iter().fold(
590                            0usize,
591                            |acc, &num_own_segment_roots| {
592                                acc + usize::from(num_own_segment_roots) * SegmentRoot::SIZE
593                            },
594                        ),
595                )
596            };
597
598            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
599                .expect("Already checked in constructor; qed");
600
601            IntermediateShardBlockInfo {
602                header,
603                segments_proof,
604                own_segments,
605                num_leaf_shard_blocks_with_segments,
606                leaf_shards_segments_bytes,
607            }
608        })
609        .take(self.num_blocks)
610    }
611
612    /// Number of intermediate shard blocks
613    #[inline(always)]
614    pub const fn len(&self) -> usize {
615        self.num_blocks
616    }
617
618    /// Returns `true` if there are no intermediate shard blocks
619    #[inline(always)]
620    pub const fn is_empty(&self) -> bool {
621        self.num_blocks == 0
622    }
623
624    /// Compute the root of segments of the intermediate shard blocks info.
625    ///
626    /// Returns the default value for an empty collection of segment roots.
627    #[inline]
628    pub fn segments_root(&self) -> Blake3Hash {
629        let root = UnbalancedMerkleTree::compute_root_only::<{ u64::from(u16::MAX) }, _, _>(
630            self.iter().map(|shard_block_info| {
631                UnbalancedMerkleTree::compute_root_only::<{ u64::from(u32::MAX) }, _, _>(
632                    shard_block_info
633                        .own_segments
634                        .as_ref()
635                        .map(OwnSegments::root)
636                        .into_iter()
637                        .chain(shard_block_info.leaf_shards_segments().map(
638                            |(shard_index, own_segments)| {
639                                own_segments.root_with_shard_index(shard_index)
640                            },
641                        )),
642                )
643                .unwrap_or_default()
644            }),
645        )
646        .unwrap_or_default();
647
648        Blake3Hash::new(root)
649    }
650
651    /// Compute the root of headers of the intermediate shard blocks info.
652    ///
653    /// Returns the default value for an empty collection of shard blocks.
654    #[inline]
655    pub fn headers_root(&self) -> Blake3Hash {
656        let root = UnbalancedMerkleTree::compute_root_only::<{ u64::from(u16::MAX) }, _, _>(
657            // TODO: Keyed hash
658            self.iter().map(|shard_block_info| {
659                // Hash the root again so we can prove it, otherwise the root of headers is
660                // indistinguishable from individual block roots and can be used to confuse
661                // verifier
662                single_block_hash(shard_block_info.header.root().as_ref())
663                    .expect("Less than a single block worth of bytes; qed")
664            }),
665        )
666        .unwrap_or_default();
667
668        Blake3Hash::new(root)
669    }
670}
671
672/// Block body that corresponds to the beacon chain
673#[derive(Debug, Copy, Clone, Yokeable)]
674// Prevent creation of potentially broken invariants externally
675#[non_exhaustive]
676pub struct BeaconChainBody<'a> {
677    /// Segments produced by this shard
678    own_segments: Option<OwnSegments<'a>>,
679    /// Intermediate shard blocks
680    intermediate_shard_blocks: IntermediateShardBlocksInfo<'a>,
681    /// Proof of time checkpoints from after future proof of time of the parent block to the
682    /// current block's future proof of time (inclusive)
683    pot_checkpoints: &'a [PotCheckpoints],
684}
685
686impl<'a> GenericBlockBody<'a> for BeaconChainBody<'a> {
687    const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
688
689    #[cfg(feature = "alloc")]
690    type Owned = OwnedBeaconChainBody;
691
692    #[cfg(feature = "alloc")]
693    #[inline(always)]
694    fn to_owned(self) -> Self::Owned {
695        self.to_owned()
696    }
697
698    #[inline(always)]
699    fn root(&self) -> Blake3Hash {
700        self.root()
701    }
702}
703
704impl<'a> BeaconChainBody<'a> {
705    /// Create an instance from provided correctly aligned bytes.
706    ///
707    /// `bytes` should be 4-bytes aligned.
708    ///
709    /// Returns an instance and remaining bytes on success.
710    #[inline]
711    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
712        // The layout here is as follows:
713        // * number of PoT checkpoints: u32 as aligned little-endian bytes
714        // * number of own segment roots: u8
715        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
716        // * concatenated own segment roots
717        // * intermediate shard blocks: IntermediateShardBlocksInfo
718        // * concatenated PoT checkpoints
719
720        let num_pot_checkpoints = bytes.split_off(..size_of::<u32>())?;
721        // SAFETY: All bit patterns are valid
722        let num_pot_checkpoints =
723            *unsafe { <u32 as TrivialType>::from_bytes(num_pot_checkpoints) }? as usize;
724
725        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
726        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
727
728        let own_segments = if num_own_segment_roots > 0 {
729            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
730            // SAFETY: Unaligned and correct size
731            let first_local_segment_index = unsafe {
732                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
733            }
734            .as_inner();
735
736            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
737            // SAFETY: Valid pointer and size, no alignment requirements
738            let own_segment_roots = unsafe {
739                slice::from_raw_parts(
740                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
741                    num_own_segment_roots,
742                )
743            };
744            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
745
746            Some(OwnSegments {
747                first_local_segment_index,
748                segment_roots: own_segment_roots,
749            })
750        } else {
751            None
752        };
753
754        let (intermediate_shard_blocks, mut remainder) =
755            IntermediateShardBlocksInfo::try_from_bytes(bytes)?;
756
757        let pot_checkpoints = remainder.split_off(..num_pot_checkpoints * PotCheckpoints::SIZE)?;
758        // SAFETY: Valid pointer and size, no alignment requirements
759        let pot_checkpoints = unsafe {
760            slice::from_raw_parts(
761                pot_checkpoints
762                    .as_ptr()
763                    .cast::<[u8; PotCheckpoints::SIZE]>(),
764                num_pot_checkpoints,
765            )
766        };
767        let pot_checkpoints = PotCheckpoints::slice_from_bytes(pot_checkpoints);
768
769        let body = Self {
770            own_segments,
771            intermediate_shard_blocks,
772            pot_checkpoints,
773        };
774
775        if !body.is_internally_consistent() {
776            return None;
777        }
778
779        Some((body, remainder))
780    }
781
782    /// Check block body's internal consistency.
783    ///
784    /// This is usually not necessary to be called explicitly since internal consistency is checked
785    /// by [`Self::try_from_bytes()`] internally.
786    #[inline]
787    pub fn is_internally_consistent(&self) -> bool {
788        // Nothing to check here
789        true
790    }
791
792    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
793    /// checks
794    #[inline]
795    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
796        // The layout here is as follows:
797        // * number of PoT checkpoints: u32 as aligned little-endian bytes
798        // * number of own segment roots: u8
799        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
800        // * concatenated own segment roots
801        // * intermediate shard blocks: IntermediateShardBlocksInfo
802        // * concatenated PoT checkpoints
803
804        let num_pot_checkpoints = bytes.split_off(..size_of::<u32>())?;
805        // SAFETY: All bit patterns are valid
806        let num_pot_checkpoints =
807            *unsafe { <u32 as TrivialType>::from_bytes(num_pot_checkpoints) }? as usize;
808
809        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
810        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
811
812        let own_segments = if num_own_segment_roots > 0 {
813            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
814            // SAFETY: Unaligned and correct size
815            let first_local_segment_index = unsafe {
816                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
817            }
818            .as_inner();
819
820            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
821            // SAFETY: Valid pointer and size, no alignment requirements
822            let own_segment_roots = unsafe {
823                slice::from_raw_parts(
824                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
825                    num_own_segment_roots,
826                )
827            };
828            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
829
830            Some(OwnSegments {
831                first_local_segment_index,
832                segment_roots: own_segment_roots,
833            })
834        } else {
835            None
836        };
837
838        let (intermediate_shard_blocks, mut remainder) =
839            IntermediateShardBlocksInfo::try_from_bytes_unchecked(bytes)?;
840
841        let pot_checkpoints = remainder.split_off(..num_pot_checkpoints * PotCheckpoints::SIZE)?;
842        // SAFETY: Valid pointer and size, no alignment requirements
843        let pot_checkpoints = unsafe {
844            slice::from_raw_parts(
845                pot_checkpoints
846                    .as_ptr()
847                    .cast::<[u8; PotCheckpoints::SIZE]>(),
848                num_pot_checkpoints,
849            )
850        };
851        let pot_checkpoints = PotCheckpoints::slice_from_bytes(pot_checkpoints);
852
853        Some((
854            Self {
855                own_segments,
856                intermediate_shard_blocks,
857                pot_checkpoints,
858            },
859            remainder,
860        ))
861    }
862
863    /// Create an owned version of this body
864    #[cfg(feature = "alloc")]
865    #[inline(always)]
866    pub fn to_owned(self) -> OwnedBeaconChainBody {
867        if let Some(own_segments) = self.own_segments {
868            let first_local_segment_index = own_segments.first_local_segment_index;
869
870            OwnedBeaconChainBody::new(
871                own_segments.segment_roots.iter().copied().enumerate().map(
872                    |(index, segment_root)| {
873                        (
874                            first_local_segment_index + LocalSegmentIndex::from(index as u64),
875                            segment_root,
876                        )
877                    },
878                ),
879                self.intermediate_shard_blocks.iter(),
880                self.pot_checkpoints,
881            )
882            .expect("`self` is always a valid invariant; qed")
883        } else {
884            OwnedBeaconChainBody::new(
885                iter::empty(),
886                self.intermediate_shard_blocks.iter(),
887                self.pot_checkpoints,
888            )
889            .expect("`self` is always a valid invariant; qed")
890        }
891    }
892
893    /// Segment roots produced by this shard
894    #[inline(always)]
895    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
896        self.own_segments
897    }
898
899    /// Intermediate shard blocks
900    #[inline(always)]
901    pub fn intermediate_shard_blocks(&self) -> &IntermediateShardBlocksInfo<'a> {
902        &self.intermediate_shard_blocks
903    }
904
905    /// Proof of time checkpoints from after future proof of time of the parent block to the current
906    /// block's future proof of time (inclusive)
907    #[inline(always)]
908    pub fn pot_checkpoints(&self) -> &'a [PotCheckpoints] {
909        self.pot_checkpoints
910    }
911
912    /// Compute block body root
913    #[inline]
914    pub fn root(&self) -> Blake3Hash {
915        // TODO: Keyed hash
916        let root = BalancedMerkleTree::compute_root_only(&[
917            *self
918                .own_segments
919                .as_ref()
920                .map(OwnSegments::root)
921                .unwrap_or_default(),
922            *self.intermediate_shard_blocks.segments_root(),
923            *self.intermediate_shard_blocks.headers_root(),
924            blake3::hash(PotCheckpoints::bytes_from_slice(self.pot_checkpoints).as_flattened())
925                .into(),
926        ]);
927
928        Blake3Hash::new(root)
929    }
930}
931
932/// Information about leaf shard segments
933#[derive(Debug, Copy, Clone)]
934pub struct LeafShardOwnSegments<'a> {
935    /// Segment roots proof
936    pub segment_roots_proof: &'a [u8; OUT_LEN],
937    /// Segments produced by this shard
938    pub own_segments: OwnSegments<'a>,
939}
940
941/// Information about the leaf shard block container inside the intermediate shard block body
942#[derive(Debug, Clone)]
943pub struct LeafShardBlockInfo<'a> {
944    /// A block header that corresponds to a leaf shard
945    pub header: LeafShardHeader<'a>,
946    /// Segments in the corresponding block
947    pub segments: Option<LeafShardOwnSegments<'a>>,
948}
949
950/// Information about a collection of leaf shard blocks
951#[derive(Debug, Copy, Clone)]
952pub struct LeafShardBlocksInfo<'a> {
953    num_blocks: usize,
954    bytes: &'a [u8],
955}
956
957impl<'a> LeafShardBlocksInfo<'a> {
958    /// Create an instance from provided bytes.
959    ///
960    /// `bytes` do not need to be aligned.
961    ///
962    /// Returns an instance and remaining bytes on success.
963    #[inline]
964    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
965        // The layout here is as follows:
966        // * number of blocks: u16 as unaligned little-endian bytes
967        // * for each block:
968        //   * number of own segment roots: u8
969        // * padding to 8-bytes boundary with zeroes
970        // * for each block:
971        //   * block header: LeafShardHeader
972        //   * padding to 8-bytes boundary with zeroes
973        //   * local segment index of the first segment root (if any): `LocalSegmentIndex`
974        //   * segment roots proof (if there is at least one segment root)
975        //   * concatenated own segment roots
976
977        let num_blocks = bytes.split_off(..size_of::<u16>())?;
978        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
979
980        let bytes_start = bytes;
981
982        let mut counts = bytes.split_off(..num_blocks * size_of::<u8>())?;
983
984        let mut remainder = align_to_and_ensure_zero_padding(bytes, Alignment::of::<u64>())?;
985
986        for _ in 0..num_blocks {
987            let num_own_segment_roots = usize::from(counts[0]);
988            counts = &counts[1..];
989
990            (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
991
992            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())?;
993
994            if num_own_segment_roots > 0 {
995                let _first_local_segment_index =
996                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
997                let _segment_roots_proof = remainder.split_off(..OUT_LEN)?;
998                let _own_segment_roots =
999                    remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1000            }
1001        }
1002
1003        let info = Self {
1004            num_blocks,
1005            bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1006        };
1007
1008        if !info.is_internally_consistent() {
1009            return None;
1010        }
1011
1012        Some((info, remainder))
1013    }
1014
1015    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1016    /// checks
1017    #[inline]
1018    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1019        // The layout here is as follows:
1020        // * number of blocks: u16 as unaligned little-endian bytes
1021        // * for each block:
1022        //   * number of own segment roots: u8
1023        // * padding to 8-bytes boundary with zeroes
1024        // * for each block:
1025        //   * block header: LeafShardHeader
1026        //   * padding to 8-bytes boundary with zeroes
1027        //   * local segment index of the first segment root (if any): `LocalSegmentIndex`
1028        //   * segment roots proof (if there is at least one segment root)
1029        //   * concatenated own segment roots
1030
1031        let num_blocks = bytes.split_off(..size_of::<u16>())?;
1032        let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
1033
1034        let bytes_start = bytes;
1035
1036        let mut counts = bytes.split_off(..num_blocks * size_of::<u8>())?;
1037
1038        let mut remainder = align_to_and_ensure_zero_padding(bytes, Alignment::of::<u64>())?;
1039
1040        for _ in 0..num_blocks {
1041            let num_own_segment_roots = usize::from(counts[0]);
1042            counts = &counts[1..];
1043
1044            (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
1045
1046            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())?;
1047
1048            if num_own_segment_roots > 0 {
1049                let _first_local_segment_index =
1050                    remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
1051                let _segment_roots_proof = remainder.split_off(..OUT_LEN)?;
1052                let _own_segment_roots =
1053                    remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1054            }
1055        }
1056
1057        Some((
1058            Self {
1059                num_blocks,
1060                bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1061            },
1062            remainder,
1063        ))
1064    }
1065
1066    /// Check leaf shard info's internal consistency.
1067    ///
1068    /// This is usually not necessary to be called explicitly since internal consistency is checked
1069    /// by [`Self::try_from_bytes()`] internally.
1070    #[inline]
1071    pub fn is_internally_consistent(&self) -> bool {
1072        let mut last_leaf_shard_info = None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
1073
1074        self.iter().all(|leaf_shard_block| {
1075            let shard_index = leaf_shard_block.header.prefix.shard_index;
1076
1077            // Ensure increasing order of shard indices, block numbers and local segment indices
1078            if let Some((
1079                last_leaf_shard_index,
1080                last_leaf_shard_block_number,
1081                last_leaf_shard_first_local_segment_index,
1082            )) = last_leaf_shard_info
1083            {
1084                match last_leaf_shard_index.cmp(&shard_index) {
1085                    cmp::Ordering::Less => {
1086                        last_leaf_shard_info.replace((
1087                            shard_index,
1088                            leaf_shard_block.header.prefix.number,
1089                            leaf_shard_block
1090                                .segments
1091                                .as_ref()
1092                                .map(|segments| segments.own_segments.first_local_segment_index),
1093                        ));
1094                    }
1095                    cmp::Ordering::Equal => {
1096                        if last_leaf_shard_block_number >= leaf_shard_block.header.prefix.number {
1097                            return false;
1098                        }
1099                        if let Some(leaf_shard_segments) = &leaf_shard_block.segments {
1100                            if let Some(last_leaf_shard_first_local_segment_index) =
1101                                last_leaf_shard_first_local_segment_index
1102                                && last_leaf_shard_first_local_segment_index
1103                                    >= leaf_shard_segments.own_segments.first_local_segment_index
1104                            {
1105                                return false;
1106                            }
1107
1108                            last_leaf_shard_info.replace((
1109                                shard_index,
1110                                leaf_shard_block.header.prefix.number,
1111                                Some(leaf_shard_segments.own_segments.first_local_segment_index),
1112                            ));
1113                        } else {
1114                            last_leaf_shard_info.replace((
1115                                shard_index,
1116                                leaf_shard_block.header.prefix.number,
1117                                last_leaf_shard_first_local_segment_index,
1118                            ));
1119                        }
1120                    }
1121                    cmp::Ordering::Greater => {
1122                        return false;
1123                    }
1124                }
1125            } else {
1126                last_leaf_shard_info.replace((
1127                    shard_index,
1128                    leaf_shard_block.header.prefix.number,
1129                    leaf_shard_block
1130                        .segments
1131                        .as_ref()
1132                        .map(|segments| segments.own_segments.first_local_segment_index),
1133                ));
1134            }
1135
1136            let Some(segments) = leaf_shard_block.segments else {
1137                return true;
1138            };
1139
1140            BalancedMerkleTree::<2>::verify(
1141                &leaf_shard_block.header.result.body_root,
1142                array::from_ref(segments.segment_roots_proof),
1143                0,
1144                *segments.own_segments.root(),
1145            )
1146        })
1147    }
1148
1149    /// Iterator over leaf shard blocks in a collection
1150    #[inline]
1151    pub fn iter(
1152        &self,
1153    ) -> impl ExactSizeIterator<Item = LeafShardBlockInfo<'a>> + TrustedLen + use<'a> {
1154        // SAFETY: Checked in constructor
1155        let (mut counts, mut remainder) = unsafe {
1156            self.bytes
1157                .split_at_unchecked(self.num_blocks * size_of::<u8>())
1158        };
1159
1160        iter::repeat_with(move || {
1161            let num_own_segment_roots = usize::from(counts[0]);
1162            counts = &counts[1..];
1163
1164            // TODO: Unchecked method would have been helpful here
1165            let header;
1166            (header, remainder) = LeafShardHeader::try_from_bytes(remainder)
1167                .expect("Already checked in constructor; qed");
1168
1169            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::of::<u64>())
1170                .expect("Already checked in constructor; qed");
1171
1172            let segments = if num_own_segment_roots > 0 {
1173                let first_local_segment_index;
1174                // SAFETY: Checked in constructor
1175                (first_local_segment_index, remainder) =
1176                    unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
1177                // SAFETY: Correct alignment and size
1178                let first_local_segment_index =
1179                    *unsafe { LocalSegmentIndex::from_bytes_unchecked(first_local_segment_index) };
1180
1181                let segment_roots_proof;
1182                // SAFETY: Checked in constructor
1183                (segment_roots_proof, remainder) = unsafe { remainder.split_at_unchecked(OUT_LEN) };
1184                // SAFETY: Valid pointer and size, no alignment requirements
1185                let segment_roots_proof = unsafe {
1186                    segment_roots_proof
1187                        .as_ptr()
1188                        .cast::<[u8; OUT_LEN]>()
1189                        .as_ref_unchecked()
1190                };
1191
1192                let own_segment_roots;
1193                // SAFETY: Checked in constructor
1194                (own_segment_roots, remainder) = unsafe {
1195                    remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE)
1196                };
1197                // SAFETY: Valid pointer and size, no alignment requirements
1198                let own_segment_roots = unsafe {
1199                    slice::from_raw_parts(
1200                        own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1201                        num_own_segment_roots,
1202                    )
1203                };
1204                let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1205
1206                Some(LeafShardOwnSegments {
1207                    segment_roots_proof,
1208                    own_segments: OwnSegments {
1209                        first_local_segment_index,
1210                        segment_roots: own_segment_roots,
1211                    },
1212                })
1213            } else {
1214                None
1215            };
1216
1217            LeafShardBlockInfo { header, segments }
1218        })
1219        .take(self.num_blocks)
1220    }
1221
1222    /// Number of leaf shard blocks
1223    #[inline(always)]
1224    pub const fn len(&self) -> usize {
1225        self.num_blocks
1226    }
1227
1228    /// Returns `true` if there are no leaf shard blocks
1229    #[inline(always)]
1230    pub const fn is_empty(&self) -> bool {
1231        self.num_blocks == 0
1232    }
1233
1234    /// Compute the root of segments of the leaf shard blocks info.
1235    ///
1236    /// Returns the default value for an empty collection of segment roots.
1237    #[inline]
1238    pub fn segments_root(&self) -> Blake3Hash {
1239        let root = UnbalancedMerkleTree::compute_root_only::<{ u64::from(u16::MAX) }, _, _>(
1240            self.iter().map(|shard_block_info| {
1241                shard_block_info
1242                    .segments
1243                    .map(|own_segments| {
1244                        own_segments
1245                            .own_segments
1246                            .root_with_shard_index(shard_block_info.header.prefix.shard_index)
1247                    })
1248                    .unwrap_or_default()
1249            }),
1250        )
1251        .unwrap_or_default();
1252
1253        Blake3Hash::new(root)
1254    }
1255
1256    /// Compute the root of headers of the leaf shard blocks info.
1257    ///
1258    /// Returns the default value for an empty collection of shard blocks.
1259    #[inline]
1260    pub fn headers_root(&self) -> Blake3Hash {
1261        let root = UnbalancedMerkleTree::compute_root_only::<{ u64::from(u16::MAX) }, _, _>(
1262            self.iter().map(|shard_block_info| {
1263                // Hash the root again so we can prove it, otherwise root of headers is
1264                // indistinguishable from individual block roots and can be used to confuse
1265                // verifier
1266                single_block_hash(shard_block_info.header.root().as_ref())
1267                    .expect("Less than a single block worth of bytes; qed")
1268            }),
1269        )
1270        .unwrap_or_default();
1271
1272        Blake3Hash::new(root)
1273    }
1274}
1275
1276/// Block body that corresponds to an intermediate shard
1277#[derive(Debug, Copy, Clone, Yokeable)]
1278// Prevent creation of potentially broken invariants externally
1279#[non_exhaustive]
1280pub struct IntermediateShardBody<'a> {
1281    /// Segments produced by this shard
1282    own_segments: Option<OwnSegments<'a>>,
1283    /// Leaf shard blocks
1284    leaf_shard_blocks: LeafShardBlocksInfo<'a>,
1285}
1286
1287impl<'a> GenericBlockBody<'a> for IntermediateShardBody<'a> {
1288    const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
1289
1290    #[cfg(feature = "alloc")]
1291    type Owned = OwnedIntermediateShardBody;
1292
1293    #[cfg(feature = "alloc")]
1294    #[inline(always)]
1295    fn to_owned(self) -> Self::Owned {
1296        self.to_owned()
1297    }
1298
1299    #[inline(always)]
1300    fn root(&self) -> Blake3Hash {
1301        self.root()
1302    }
1303}
1304
1305impl<'a> IntermediateShardBody<'a> {
1306    /// Create an instance from provided bytes.
1307    ///
1308    /// `bytes` do not need to be aligned.
1309    ///
1310    /// Returns an instance and remaining bytes on success.
1311    #[inline]
1312    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1313        // The layout here is as follows:
1314        // * number of own segment roots: u8
1315        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1316        // * concatenated own segment roots
1317        // * leaf shard blocks: LeafShardBlocksInfo
1318
1319        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1320        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1321
1322        let own_segments = if num_own_segment_roots > 0 {
1323            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1324            // SAFETY: Unaligned and correct size
1325            let first_local_segment_index = unsafe {
1326                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1327            }
1328            .as_inner();
1329
1330            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1331            // SAFETY: Valid pointer and size, no alignment requirements
1332            let own_segment_roots = unsafe {
1333                slice::from_raw_parts(
1334                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1335                    num_own_segment_roots,
1336                )
1337            };
1338            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1339
1340            Some(OwnSegments {
1341                first_local_segment_index,
1342                segment_roots: own_segment_roots,
1343            })
1344        } else {
1345            None
1346        };
1347
1348        let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes(bytes)?;
1349
1350        let body = Self {
1351            own_segments,
1352            leaf_shard_blocks,
1353        };
1354
1355        if !body.is_internally_consistent() {
1356            return None;
1357        }
1358
1359        Some((body, remainder))
1360    }
1361
1362    /// Check block body's internal consistency.
1363    ///
1364    /// This is usually not necessary to be called explicitly since internal consistency is checked
1365    /// by [`Self::try_from_bytes()`] internally.
1366    #[inline]
1367    pub fn is_internally_consistent(&self) -> bool {
1368        // Nothing to check here
1369        true
1370    }
1371
1372    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1373    /// checks
1374    #[inline]
1375    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1376        // The layout here is as follows:
1377        // * number of own segment roots: u8
1378        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1379        // * concatenated own segment roots
1380        // * leaf shard blocks: LeafShardBlocksInfo
1381
1382        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1383        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1384
1385        let own_segments = if num_own_segment_roots > 0 {
1386            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1387            // SAFETY: Unaligned and correct size
1388            let first_local_segment_index = unsafe {
1389                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1390            }
1391            .as_inner();
1392
1393            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1394            // SAFETY: Valid pointer and size, no alignment requirements
1395            let own_segment_roots = unsafe {
1396                slice::from_raw_parts(
1397                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1398                    num_own_segment_roots,
1399                )
1400            };
1401            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1402
1403            Some(OwnSegments {
1404                first_local_segment_index,
1405                segment_roots: own_segment_roots,
1406            })
1407        } else {
1408            None
1409        };
1410
1411        let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes_unchecked(bytes)?;
1412
1413        Some((
1414            Self {
1415                own_segments,
1416                leaf_shard_blocks,
1417            },
1418            remainder,
1419        ))
1420    }
1421
1422    /// Segment roots produced by this shard
1423    #[inline(always)]
1424    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1425        self.own_segments
1426    }
1427
1428    /// Leaf shard blocks
1429    #[inline(always)]
1430    pub fn leaf_shard_blocks(&self) -> &LeafShardBlocksInfo<'a> {
1431        &self.leaf_shard_blocks
1432    }
1433
1434    /// Create an owned version of this body
1435    #[cfg(feature = "alloc")]
1436    #[inline(always)]
1437    pub fn to_owned(self) -> OwnedIntermediateShardBody {
1438        if let Some(own_segments) = self.own_segments {
1439            let first_local_segment_index = own_segments.first_local_segment_index;
1440
1441            OwnedIntermediateShardBody::new(
1442                own_segments.segment_roots.iter().copied().enumerate().map(
1443                    |(index, segment_root)| {
1444                        (
1445                            first_local_segment_index + LocalSegmentIndex::from(index as u64),
1446                            segment_root,
1447                        )
1448                    },
1449                ),
1450                self.leaf_shard_blocks.iter(),
1451            )
1452            .expect("`self` is always a valid invariant; qed")
1453        } else {
1454            OwnedIntermediateShardBody::new(iter::empty(), self.leaf_shard_blocks.iter())
1455                .expect("`self` is always a valid invariant; qed")
1456        }
1457    }
1458
1459    /// Compute block body root
1460    #[inline]
1461    pub fn root(&self) -> Blake3Hash {
1462        // Explicit nested trees to emphasize that the proof size for segments is just one hash
1463        let root = BalancedMerkleTree::compute_root_only(&[
1464            BalancedMerkleTree::compute_root_only(&[
1465                *self
1466                    .own_segments
1467                    .as_ref()
1468                    .map(OwnSegments::root)
1469                    .unwrap_or_default(),
1470                *self.leaf_shard_blocks.segments_root(),
1471            ]),
1472            *self.leaf_shard_blocks.headers_root(),
1473        ]);
1474
1475        Blake3Hash::new(root)
1476    }
1477}
1478
1479/// Collection of transactions
1480#[derive(Debug, Copy, Clone)]
1481pub struct Transactions<'a> {
1482    num_transactions: usize,
1483    bytes: &'a [u8],
1484}
1485
1486impl<'a> Transactions<'a> {
1487    /// Create an instance from provided bytes.
1488    ///
1489    /// `bytes` do not need to be aligned.
1490    ///
1491    /// Returns an instance and remaining bytes on success.
1492    #[inline]
1493    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1494        // The layout here is as follows:
1495        // * number of transactions: u32 as unaligned little-endian bytes
1496        // * padding to 16-bytes boundary with zeroes
1497        // * for each transaction
1498        //   * transaction: Transaction
1499        //   * padding to 16-bytes boundary with zeroes
1500
1501        let num_transactions = bytes.split_off(..size_of::<u32>())?;
1502        let num_transactions = u32::from_le_bytes([
1503            num_transactions[0],
1504            num_transactions[1],
1505            num_transactions[2],
1506            num_transactions[3],
1507        ]) as usize;
1508
1509        let mut remainder = align_to_and_ensure_zero_padding(bytes, Alignment::MAX)?;
1510        let bytes_start = remainder;
1511
1512        for _ in 0..num_transactions {
1513            (_, remainder) = Transaction::try_from_bytes(remainder)?;
1514            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)?;
1515        }
1516
1517        Some((
1518            Self {
1519                num_transactions,
1520                bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1521            },
1522            remainder,
1523        ))
1524    }
1525
1526    /// Iterator over transactions in a collection
1527    #[inline]
1528    pub fn iter(&self) -> impl ExactSizeIterator<Item = Transaction<'a>> + TrustedLen + use<'a> {
1529        let mut remainder = self.bytes;
1530
1531        iter::repeat_with(move || {
1532            // SAFETY: Checked in constructor
1533            let transaction = unsafe { Transaction::from_bytes_unchecked(remainder) };
1534
1535            remainder = &remainder[transaction.encoded_size()..];
1536            remainder = align_to_and_ensure_zero_padding(remainder, Alignment::MAX)
1537                .expect("Already checked in constructor; qed");
1538
1539            transaction
1540        })
1541        .take(self.num_transactions)
1542    }
1543
1544    /// Number of transactions
1545    #[inline(always)]
1546    pub const fn len(&self) -> usize {
1547        self.num_transactions
1548    }
1549
1550    /// Returns `true` if there are no transactions
1551    #[inline(always)]
1552    pub const fn is_empty(&self) -> bool {
1553        self.num_transactions == 0
1554    }
1555
1556    /// Compute the root of transactions.
1557    ///
1558    /// Returns the default value for an empty collection of transactions.
1559    #[inline]
1560    pub fn root(&self) -> Blake3Hash {
1561        let root = UnbalancedMerkleTree::compute_root_only::<{ u64::from(u32::MAX) }, _, _>(
1562            self.iter().map(|transaction| {
1563                // Hash the hash again so we can prove it, otherwise root of transactions is
1564                // indistinguishable from individual transaction roots and can be used to
1565                // confuse verifier
1566                single_block_hash(transaction.hash().as_ref())
1567                    .expect("Less than a single block worth of bytes; qed")
1568            }),
1569        )
1570        .unwrap_or_default();
1571
1572        Blake3Hash::new(root)
1573    }
1574}
1575
1576/// Block body that corresponds to a leaf shard
1577#[derive(Debug, Copy, Clone, Yokeable)]
1578// Prevent creation of potentially broken invariants externally
1579#[non_exhaustive]
1580pub struct LeafShardBody<'a> {
1581    /// Segments produced by this shard
1582    own_segments: Option<OwnSegments<'a>>,
1583    /// User transactions
1584    transactions: Transactions<'a>,
1585}
1586
1587impl<'a> GenericBlockBody<'a> for LeafShardBody<'a> {
1588    const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
1589
1590    #[cfg(feature = "alloc")]
1591    type Owned = OwnedLeafShardBody;
1592
1593    #[cfg(feature = "alloc")]
1594    #[inline(always)]
1595    fn to_owned(self) -> Self::Owned {
1596        self.to_owned()
1597    }
1598
1599    #[inline(always)]
1600    fn root(&self) -> Blake3Hash {
1601        self.root()
1602    }
1603}
1604
1605impl<'a> LeafShardBody<'a> {
1606    /// Create an instance from provided bytes.
1607    ///
1608    /// `bytes` do not need to be aligned.
1609    ///
1610    /// Returns an instance and remaining bytes on success.
1611    #[inline]
1612    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1613        // The layout here is as follows:
1614        // * number of own segment roots: u8
1615        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1616        // * concatenated own segment roots
1617        // * transactions: Transactions
1618
1619        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1620        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1621
1622        let own_segments = if num_own_segment_roots > 0 {
1623            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1624            // SAFETY: Unaligned and correct size
1625            let first_local_segment_index = unsafe {
1626                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1627            }
1628            .as_inner();
1629
1630            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1631            // SAFETY: Valid pointer and size, no alignment requirements
1632            let own_segment_roots = unsafe {
1633                slice::from_raw_parts(
1634                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1635                    num_own_segment_roots,
1636                )
1637            };
1638            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1639
1640            Some(OwnSegments {
1641                first_local_segment_index,
1642                segment_roots: own_segment_roots,
1643            })
1644        } else {
1645            None
1646        };
1647
1648        let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1649
1650        let body = Self {
1651            own_segments,
1652            transactions,
1653        };
1654
1655        if !body.is_internally_consistent() {
1656            return None;
1657        }
1658
1659        Some((body, remainder))
1660    }
1661
1662    /// Check block body's internal consistency.
1663    ///
1664    /// This is usually not necessary to be called explicitly since internal consistency is checked
1665    /// by [`Self::try_from_bytes()`] internally.
1666    #[inline]
1667    pub fn is_internally_consistent(&self) -> bool {
1668        // Nothing to check here
1669        true
1670    }
1671
1672    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1673    /// checks
1674    #[inline]
1675    pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1676        // The layout here is as follows:
1677        // * number of own segment roots: u8
1678        // * local segment index of the first segment root (if any): unaligned `LocalSegmentIndex`
1679        // * concatenated own segment roots
1680        // * transactions: Transactions
1681
1682        let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1683        let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1684
1685        let own_segments = if num_own_segment_roots > 0 {
1686            let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1687            // SAFETY: Unaligned and correct size
1688            let first_local_segment_index = unsafe {
1689                Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1690            }
1691            .as_inner();
1692
1693            let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1694            // SAFETY: Valid pointer and size, no alignment requirements
1695            let own_segment_roots = unsafe {
1696                slice::from_raw_parts(
1697                    own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1698                    num_own_segment_roots,
1699                )
1700            };
1701            let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1702
1703            Some(OwnSegments {
1704                first_local_segment_index,
1705                segment_roots: own_segment_roots,
1706            })
1707        } else {
1708            None
1709        };
1710
1711        let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1712
1713        Some((
1714            Self {
1715                own_segments,
1716                transactions,
1717            },
1718            remainder,
1719        ))
1720    }
1721
1722    /// Segment roots produced by this shard
1723    #[inline(always)]
1724    pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1725        self.own_segments
1726    }
1727
1728    /// User transactions
1729    #[inline(always)]
1730    pub fn transactions(&self) -> &Transactions<'a> {
1731        &self.transactions
1732    }
1733
1734    /// Create an owned version of this body
1735    #[cfg(feature = "alloc")]
1736    #[inline(always)]
1737    pub fn to_owned(self) -> OwnedLeafShardBody {
1738        let mut builder = if let Some(own_segments) = self.own_segments {
1739            let first_local_segment_index = own_segments.first_local_segment_index;
1740
1741            OwnedLeafShardBody::init(own_segments.segment_roots.iter().copied().enumerate().map(
1742                |(index, segment_root)| {
1743                    (
1744                        first_local_segment_index + LocalSegmentIndex::from(index as u64),
1745                        segment_root,
1746                    )
1747                },
1748            ))
1749            .expect("`self` is always a valid invariant; qed")
1750        } else {
1751            OwnedLeafShardBody::init(iter::empty())
1752                .expect("`self` is always a valid invariant; qed")
1753        };
1754        for transaction in self.transactions.iter() {
1755            builder
1756                .add_transaction(transaction)
1757                .expect("`self` is always a valid invariant; qed");
1758        }
1759
1760        builder.finish()
1761    }
1762
1763    /// Compute block body root
1764    #[inline]
1765    pub fn root(&self) -> Blake3Hash {
1766        let root = BalancedMerkleTree::compute_root_only(&[
1767            *self
1768                .own_segments
1769                .as_ref()
1770                .map(OwnSegments::root)
1771                .unwrap_or_default(),
1772            *self.transactions.root(),
1773        ]);
1774
1775        Blake3Hash::new(root)
1776    }
1777}
1778
1779/// Block body that together with [`BlockHeader`] form a [`Block`]
1780///
1781/// [`BlockHeader`]: crate::block::header::BlockHeader
1782/// [`Block`]: crate::block::Block
1783#[derive(Debug, Copy, Clone, From)]
1784pub enum BlockBody<'a> {
1785    /// Block body corresponds to the beacon chain
1786    BeaconChain(BeaconChainBody<'a>),
1787    /// Block body corresponds to an intermediate shard
1788    IntermediateShard(IntermediateShardBody<'a>),
1789    /// Block body corresponds to a leaf shard
1790    LeafShard(LeafShardBody<'a>),
1791}
1792
1793impl<'a> BlockBody<'a> {
1794    /// Try to create a new instance from provided bytes for the provided shard index.
1795    ///
1796    /// `bytes` do not need to be aligned.
1797    ///
1798    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
1799    /// bytes are not properly aligned or input is otherwise invalid.
1800    #[inline]
1801    pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
1802        match shard_kind {
1803            RealShardKind::BeaconChain => {
1804                let (body, remainder) = BeaconChainBody::try_from_bytes(bytes)?;
1805                Some((Self::BeaconChain(body), remainder))
1806            }
1807            RealShardKind::IntermediateShard => {
1808                let (body, remainder) = IntermediateShardBody::try_from_bytes(bytes)?;
1809                Some((Self::IntermediateShard(body), remainder))
1810            }
1811            RealShardKind::LeafShard => {
1812                let (body, remainder) = LeafShardBody::try_from_bytes(bytes)?;
1813                Some((Self::LeafShard(body), remainder))
1814            }
1815        }
1816    }
1817
1818    /// Check block body's internal consistency.
1819    ///
1820    /// This is usually not necessary to be called explicitly since internal consistency is checked
1821    /// by [`Self::try_from_bytes()`] internally.
1822    #[inline]
1823    pub fn is_internally_consistent(&self) -> bool {
1824        match self {
1825            Self::BeaconChain(body) => body.is_internally_consistent(),
1826            Self::IntermediateShard(body) => body.is_internally_consistent(),
1827            Self::LeafShard(body) => body.is_internally_consistent(),
1828        }
1829    }
1830
1831    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1832    /// checks
1833    #[inline]
1834    pub fn try_from_bytes_unchecked(
1835        bytes: &'a [u8],
1836        shard_kind: RealShardKind,
1837    ) -> Option<(Self, &'a [u8])> {
1838        match shard_kind {
1839            RealShardKind::BeaconChain => {
1840                let (body, remainder) = BeaconChainBody::try_from_bytes_unchecked(bytes)?;
1841                Some((Self::BeaconChain(body), remainder))
1842            }
1843            RealShardKind::IntermediateShard => {
1844                let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(bytes)?;
1845                Some((Self::IntermediateShard(body), remainder))
1846            }
1847            RealShardKind::LeafShard => {
1848                let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(bytes)?;
1849                Some((Self::LeafShard(body), remainder))
1850            }
1851        }
1852    }
1853
1854    /// Create an owned version of this body
1855    #[cfg(feature = "alloc")]
1856    #[inline(always)]
1857    pub fn to_owned(self) -> OwnedBlockBody {
1858        match self {
1859            Self::BeaconChain(body) => body.to_owned().into(),
1860            Self::IntermediateShard(body) => body.to_owned().into(),
1861            Self::LeafShard(body) => body.to_owned().into(),
1862        }
1863    }
1864
1865    /// Compute block body root.
1866    ///
1867    /// Block body hash is actually a Merkle Tree Root. The leaves are derived from individual
1868    /// fields this enum in the declaration order.
1869    #[inline]
1870    pub fn root(&self) -> Blake3Hash {
1871        match self {
1872            Self::BeaconChain(body) => body.root(),
1873            Self::IntermediateShard(body) => body.root(),
1874            Self::LeafShard(body) => body.root(),
1875        }
1876    }
1877}