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