Skip to main content

ab_core_primitives/block/
body.rs

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