1#[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
28pub trait GenericBlockBody<'a>
30where
31 Self: Copy + fmt::Debug + Into<BlockBody<'a>> + Send + Sync,
32{
33 const SHARD_KIND: RealShardKind;
35
36 #[cfg(feature = "alloc")]
38 type Owned: GenericOwnedBlockBody<Body<'a> = Self>
39 where
40 Self: 'a;
41
42 #[cfg(feature = "alloc")]
44 fn to_owned(self) -> Self::Owned;
45
46 fn root(&self) -> Blake3Hash;
48}
49
50#[inline]
52pub fn compute_segments_root<'a, const MAX_SEGMENTS: u64, Iter>(segment_roots: Iter) -> Blake3Hash
53where
54 [(); MAX_SEGMENTS.next_power_of_two().ilog2() as usize + 1]:,
55 Iter: IntoIterator<Item = &'a SegmentRoot>,
56{
57 let root = UnbalancedMerkleTree::compute_root_only::<MAX_SEGMENTS, _, _>(
59 segment_roots.into_iter().map(|segment_root| {
60 single_block_hash(segment_root.as_ref())
63 .expect("Less than a single block worth of bytes; qed")
64 }),
65 );
66
67 Blake3Hash::new(root.unwrap_or_default())
68}
69
70#[derive(Debug, Copy, Clone)]
72pub struct OwnSegments<'a> {
73 pub first_local_segment_index: LocalSegmentIndex,
75 pub segment_roots: &'a [SegmentRoot],
77}
78
79impl OwnSegments<'_> {
80 #[inline]
82 pub fn root(&self) -> Blake3Hash {
83 let root = BalancedMerkleTree::compute_root_only(&[
85 single_block_hash(self.first_local_segment_index.as_bytes())
86 .expect("Less than a single block worth of bytes; qed"),
87 *compute_segments_root::<{ u8::MAX as u64 }, _>(self.segment_roots),
88 ]);
89
90 Blake3Hash::new(root)
91 }
92
93 #[inline]
99 pub fn root_with_shard_index(&self, shard_index: ShardIndex) -> Blake3Hash {
100 let root = BalancedMerkleTree::compute_root_only(&[
102 {
103 const {
104 assert!((ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize <= BLOCK_LEN);
105 }
106 let mut pair = [0u8; (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize];
107 pair[..ShardIndex::SIZE as usize].copy_from_slice(shard_index.as_bytes());
108 pair[ShardIndex::SIZE as usize..]
109 .copy_from_slice(self.first_local_segment_index.as_bytes());
110
111 single_block_hash(&pair).expect("Less than a single block worth of bytes; qed")
112 },
113 *compute_segments_root::<{ u8::MAX as u64 }, _>(self.segment_roots),
114 ]);
115
116 Blake3Hash::new(root)
117 }
118}
119
120#[derive(Debug, Clone)]
122#[expect(
123 clippy::partial_pub_fields,
124 reason = "Intentionally exposing immediately decoded fields above and hiding the rest of the implementation"
125)]
126pub struct IntermediateShardBlockInfo<'a> {
127 pub header: IntermediateShardHeader<'a>,
129 pub segments_proof: Option<&'a [u8; OUT_LEN]>,
131 pub own_segments: Option<OwnSegments<'a>>,
133 num_leaf_shard_blocks_with_segments: u8,
134 leaf_shards_segments_bytes: &'a [u8],
135}
136
137impl<'a> IntermediateShardBlockInfo<'a> {
138 #[inline]
140 pub fn leaf_shards_segments(
141 &self,
142 ) -> impl ExactSizeIterator<Item = (ShardIndex, OwnSegments<'a>)> + TrustedLen + use<'a> {
143 let (counts, mut remainder) = unsafe {
145 self.leaf_shards_segments_bytes.split_at_unchecked(
146 usize::from(self.num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
147 )
148 };
149 counts.iter().map(move |&num_own_segment_roots| {
150 let num_own_segment_roots = usize::from(num_own_segment_roots);
151
152 let shard_index;
153 (shard_index, remainder) =
155 unsafe { remainder.split_at_unchecked(ShardIndex::SIZE as usize) };
156 let shard_index =
158 unsafe { Unaligned::<ShardIndex>::from_bytes_unchecked(shard_index) }.as_inner();
159
160 let first_local_segment_index;
161 (first_local_segment_index, remainder) =
163 unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
164 let first_local_segment_index = unsafe {
166 Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
167 }
168 .as_inner();
169
170 let own_segment_roots;
171 (own_segment_roots, remainder) =
173 unsafe { remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE) };
174 let own_segment_roots = unsafe {
176 slice::from_raw_parts(
177 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
178 num_own_segment_roots,
179 )
180 };
181 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
182
183 (
184 shard_index,
185 OwnSegments {
186 first_local_segment_index,
187 segment_roots: own_segment_roots,
188 },
189 )
190 })
191 }
192}
193
194#[derive(Debug, Copy, Clone)]
196pub struct IntermediateShardBlocksInfo<'a> {
197 num_blocks: usize,
198 bytes: &'a [u8],
199}
200
201impl<'a> IntermediateShardBlocksInfo<'a> {
202 #[inline]
208 pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
209 let num_blocks = bytes.split_off(..size_of::<u16>())?;
229 let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
230
231 let bytes_start = bytes;
232
233 let mut counts = bytes.split_off(..num_blocks * (size_of::<u8>() * 2))?;
234
235 let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
236
237 for _ in 0..num_blocks {
238 let num_own_segment_roots = usize::from(counts[0]);
239 let num_leaf_shard_blocks_with_segments = usize::from(counts[1]);
240 counts = &counts[2..];
241
242 (_, remainder) = IntermediateShardHeader::try_from_bytes(remainder)?;
243
244 if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
245 let _segments_proof = remainder.split_off(..SegmentRoot::SIZE)?;
246 }
247
248 if num_own_segment_roots > 0 {
249 let _first_local_segment_index =
250 remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
251 let _own_segment_roots =
252 remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
253 }
254
255 let leaf_shard_segment_roots_counts =
256 remainder.split_off(..num_leaf_shard_blocks_with_segments * size_of::<u8>())?;
257
258 for &num_own_segment_roots in leaf_shard_segment_roots_counts {
259 let _shard_index = remainder.split_off(..ShardIndex::SIZE as usize)?;
260 let _first_local_segment_index =
261 remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
262 let _own_segment_roots = remainder
263 .split_off(..usize::from(num_own_segment_roots) * SegmentRoot::SIZE)?;
264 }
265
266 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
267 }
268
269 let info = Self {
270 num_blocks,
271 bytes: &bytes_start[..bytes_start.len() - remainder.len()],
272 };
273
274 if !info.is_internally_consistent() {
275 return None;
276 }
277
278 Some((info, remainder))
279 }
280
281 #[inline]
284 pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
285 let num_blocks = bytes.split_off(..size_of::<u16>())?;
305 let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
306
307 let bytes_start = bytes;
308
309 let mut counts = bytes.split_off(..num_blocks * (size_of::<u8>() * 2))?;
310
311 let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
312
313 for _ in 0..num_blocks {
314 let num_own_segment_roots = usize::from(counts[0]);
315 let num_leaf_shard_blocks_with_segments = usize::from(counts[1]);
316 counts = &counts[2..];
317
318 (_, remainder) = IntermediateShardHeader::try_from_bytes(remainder)?;
319
320 if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
321 let _segments_proof = remainder.split_off(..OUT_LEN)?;
322 }
323
324 if num_own_segment_roots > 0 {
325 let _first_local_segment_index =
326 remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
327 let _own_segment_roots =
328 remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
329 }
330
331 let leaf_shard_segment_roots_counts =
332 remainder.split_off(..num_leaf_shard_blocks_with_segments * size_of::<u8>())?;
333
334 for &num_own_segment_roots in leaf_shard_segment_roots_counts {
335 let _shard_index = remainder.split_off(..ShardIndex::SIZE as usize)?;
336 let _first_local_segment_index =
337 remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
338 let _own_segment_roots = remainder
339 .split_off(..usize::from(num_own_segment_roots) * SegmentRoot::SIZE)?;
340 }
341
342 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
343 }
344
345 Some((
346 Self {
347 num_blocks,
348 bytes: &bytes_start[..bytes_start.len() - remainder.len()],
349 },
350 remainder,
351 ))
352 }
353
354 #[inline]
359 pub fn is_internally_consistent(&self) -> bool {
360 let mut last_intermediate_shard_info =
361 None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
362
363 self.iter().all(|intermediate_shard_block| {
364 let shard_index = intermediate_shard_block.header.prefix.shard_index;
365
366 if let Some((
368 last_intermediate_shard_index,
369 last_intermediate_shard_block_number,
370 last_intermediate_shard_first_local_segment_index,
371 )) = last_intermediate_shard_info
372 {
373 match last_intermediate_shard_index.cmp(&shard_index) {
374 cmp::Ordering::Less => {
375 last_intermediate_shard_info.replace((
376 shard_index,
377 intermediate_shard_block.header.prefix.number,
378 intermediate_shard_block
379 .own_segments
380 .as_ref()
381 .map(|own_segments| own_segments.first_local_segment_index),
382 ));
383 }
384 cmp::Ordering::Equal => {
385 if last_intermediate_shard_block_number
386 >= intermediate_shard_block.header.prefix.number
387 {
388 return false;
389 }
390 if let Some(own_segments) = &intermediate_shard_block.own_segments {
391 if let Some(last_intermediate_shard_first_local_segment_index) =
392 last_intermediate_shard_first_local_segment_index
393 && last_intermediate_shard_first_local_segment_index
394 >= own_segments.first_local_segment_index
395 {
396 return false;
397 }
398
399 last_intermediate_shard_info.replace((
400 shard_index,
401 intermediate_shard_block.header.prefix.number,
402 Some(own_segments.first_local_segment_index),
403 ));
404 } else {
405 last_intermediate_shard_info.replace((
406 shard_index,
407 intermediate_shard_block.header.prefix.number,
408 last_intermediate_shard_first_local_segment_index,
409 ));
410 }
411 }
412 cmp::Ordering::Greater => {
413 return false;
414 }
415 }
416 } else {
417 last_intermediate_shard_info.replace((
418 shard_index,
419 intermediate_shard_block.header.prefix.number,
420 intermediate_shard_block
421 .own_segments
422 .as_ref()
423 .map(|own_segments| own_segments.first_local_segment_index),
424 ));
425 }
426
427 let mut last_leaf_shard_info = None::<(ShardIndex, LocalSegmentIndex)>;
428
429 if !intermediate_shard_block.leaf_shards_segments().all(
431 |(leaf_shard_index, own_segments)| {
432 if !leaf_shard_index.is_child_of(shard_index) {
433 return false;
434 }
435
436 if let Some((
437 last_leaf_shard_index,
438 last_leaf_shard_first_local_segment_index,
439 )) = last_leaf_shard_info
440 {
441 match last_leaf_shard_index.cmp(&leaf_shard_index) {
442 cmp::Ordering::Less => {
443 }
445 cmp::Ordering::Equal => {
446 if last_leaf_shard_first_local_segment_index
447 >= own_segments.first_local_segment_index
448 {
449 return false;
450 }
451 }
452 cmp::Ordering::Greater => {
453 return false;
454 }
455 }
456 }
457 last_leaf_shard_info
458 .replace((leaf_shard_index, own_segments.first_local_segment_index));
459
460 true
461 },
462 ) {
463 return false;
464 }
465
466 if intermediate_shard_block.own_segments.is_none()
467 && intermediate_shard_block
468 .leaf_shards_segments()
469 .size_hint()
470 .0
471 == 0
472 {
473 return intermediate_shard_block.segments_proof.is_none();
474 }
475
476 let segments_proof = intermediate_shard_block.segments_proof.unwrap_or(&[0; _]);
477
478 #[expect(clippy::assertions_on_constants, reason = "Intentional documentation")]
482 #[expect(clippy::eq_op, reason = "Intentional documentation")]
483 const {
484 assert!(u16::MAX == 65535);
485 }
486 let leaf_shards_root = UnbalancedMerkleTree::compute_root_only::<65535, _, _>(
487 intermediate_shard_block.leaf_shards_segments().map(
488 |(shard_index, own_segments)| own_segments.root_with_shard_index(shard_index),
489 ),
490 )
491 .unwrap_or_default();
492
493 BalancedMerkleTree::<2>::verify(
494 &intermediate_shard_block.header.result.body_root,
495 array::from_ref(segments_proof),
496 0,
497 BalancedMerkleTree::compute_root_only(&[
498 *intermediate_shard_block
499 .own_segments
500 .as_ref()
501 .map(OwnSegments::root)
502 .unwrap_or_default(),
503 leaf_shards_root,
504 ]),
505 )
506 })
507 }
508
509 #[inline]
511 pub fn iter(
512 &self,
513 ) -> impl ExactSizeIterator<Item = IntermediateShardBlockInfo<'a>> + TrustedLen + use<'a> {
514 let (mut counts, mut remainder) = unsafe {
516 self.bytes
517 .split_at_unchecked(self.num_blocks * (size_of::<u8>() + size_of::<u16>()))
518 };
519
520 iter::repeat_with(move || {
521 let num_own_segment_roots = usize::from(counts[0]);
522 let num_leaf_shard_blocks_with_segments = counts[1];
523 counts = &counts[2..];
524
525 let header;
527 (header, remainder) = IntermediateShardHeader::try_from_bytes(remainder)
528 .expect("Already checked in constructor; qed");
529
530 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)
531 .expect("Already checked in constructor; qed");
532
533 let segments_proof =
534 if num_own_segment_roots > 0 || num_leaf_shard_blocks_with_segments > 0 {
535 let segments_proof;
536 (segments_proof, remainder) = unsafe { remainder.split_at_unchecked(OUT_LEN) };
538 Some(unsafe {
540 segments_proof
541 .as_ptr()
542 .cast::<[u8; OUT_LEN]>()
543 .as_ref_unchecked()
544 })
545 } else {
546 None
547 };
548
549 let own_segments = if num_own_segment_roots > 0 {
550 let first_local_segment_index;
551 (first_local_segment_index, remainder) =
553 unsafe { remainder.split_at_unchecked(size_of::<LocalSegmentIndex>()) };
554 let first_local_segment_index =
556 *unsafe { LocalSegmentIndex::from_bytes_unchecked(first_local_segment_index) };
557
558 let own_segment_roots;
559 (own_segment_roots, remainder) = unsafe {
561 remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE)
562 };
563 let own_segment_roots = unsafe {
565 slice::from_raw_parts(
566 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
567 num_own_segment_roots,
568 )
569 };
570 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
571
572 Some(OwnSegments {
573 first_local_segment_index,
574 segment_roots: own_segment_roots,
575 })
576 } else {
577 None
578 };
579
580 let leaf_shard_segment_roots_counts = unsafe {
582 remainder.get_unchecked(
583 ..usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>(),
584 )
585 };
586
587 let leaf_shards_segments_bytes;
588 (leaf_shards_segments_bytes, remainder) = unsafe {
590 remainder.split_at_unchecked(
591 usize::from(num_leaf_shard_blocks_with_segments) * size_of::<u8>()
592 + usize::from(num_leaf_shard_blocks_with_segments)
593 * (ShardIndex::SIZE + LocalSegmentIndex::SIZE) as usize
594 + leaf_shard_segment_roots_counts.iter().fold(
595 0usize,
596 |acc, &num_own_segment_roots| {
597 acc + usize::from(num_own_segment_roots) * SegmentRoot::SIZE
598 },
599 ),
600 )
601 };
602
603 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)
604 .expect("Already checked in constructor; qed");
605
606 IntermediateShardBlockInfo {
607 header,
608 segments_proof,
609 own_segments,
610 num_leaf_shard_blocks_with_segments,
611 leaf_shards_segments_bytes,
612 }
613 })
614 .take(self.num_blocks)
615 }
616
617 #[inline(always)]
619 pub const fn len(&self) -> usize {
620 self.num_blocks
621 }
622
623 #[inline(always)]
625 pub const fn is_empty(&self) -> bool {
626 self.num_blocks == 0
627 }
628
629 #[inline]
633 pub fn segments_root(&self) -> Blake3Hash {
634 let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
635 self.iter().map(|shard_block_info| {
636 #[expect(clippy::assertions_on_constants, reason = "Intentional documentation")]
640 #[expect(clippy::eq_op, reason = "Intentional documentation")]
641 const {
642 assert!(u32::MAX == 4_294_967_295);
643 }
644 UnbalancedMerkleTree::compute_root_only::<4_294_967_295, _, _>(
645 shard_block_info
646 .own_segments
647 .as_ref()
648 .map(OwnSegments::root)
649 .into_iter()
650 .chain(shard_block_info.leaf_shards_segments().map(
651 |(shard_index, own_segments)| {
652 own_segments.root_with_shard_index(shard_index)
653 },
654 )),
655 )
656 .unwrap_or_default()
657 }),
658 )
659 .unwrap_or_default();
660
661 Blake3Hash::new(root)
662 }
663
664 #[inline]
668 pub fn headers_root(&self) -> Blake3Hash {
669 let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
670 self.iter().map(|shard_block_info| {
672 single_block_hash(shard_block_info.header.root().as_ref())
676 .expect("Less than a single block worth of bytes; qed")
677 }),
678 )
679 .unwrap_or_default();
680
681 Blake3Hash::new(root)
682 }
683}
684
685#[derive(Debug, Copy, Clone, Yokeable)]
687#[non_exhaustive]
689pub struct BeaconChainBody<'a> {
690 own_segments: Option<OwnSegments<'a>>,
692 intermediate_shard_blocks: IntermediateShardBlocksInfo<'a>,
694 pot_checkpoints: &'a [PotCheckpoints],
697}
698
699impl<'a> GenericBlockBody<'a> for BeaconChainBody<'a> {
700 const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
701
702 #[cfg(feature = "alloc")]
703 type Owned = OwnedBeaconChainBody;
704
705 #[cfg(feature = "alloc")]
706 #[inline(always)]
707 fn to_owned(self) -> Self::Owned {
708 self.to_owned()
709 }
710
711 #[inline(always)]
712 fn root(&self) -> Blake3Hash {
713 self.root()
714 }
715}
716
717impl<'a> BeaconChainBody<'a> {
718 #[inline]
724 pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
725 let num_pot_checkpoints = bytes.split_off(..size_of::<u32>())?;
734 let num_pot_checkpoints =
736 *unsafe { <u32 as TrivialType>::from_bytes(num_pot_checkpoints) }? as usize;
737
738 let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
739 let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
740
741 let own_segments = if num_own_segment_roots > 0 {
742 let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
743 let first_local_segment_index = unsafe {
745 Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
746 }
747 .as_inner();
748
749 let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
750 let own_segment_roots = unsafe {
752 slice::from_raw_parts(
753 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
754 num_own_segment_roots,
755 )
756 };
757 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
758
759 Some(OwnSegments {
760 first_local_segment_index,
761 segment_roots: own_segment_roots,
762 })
763 } else {
764 None
765 };
766
767 let (intermediate_shard_blocks, mut remainder) =
768 IntermediateShardBlocksInfo::try_from_bytes(bytes)?;
769
770 let pot_checkpoints = remainder.split_off(..num_pot_checkpoints * PotCheckpoints::SIZE)?;
771 let pot_checkpoints = unsafe {
773 slice::from_raw_parts(
774 pot_checkpoints
775 .as_ptr()
776 .cast::<[u8; PotCheckpoints::SIZE]>(),
777 num_pot_checkpoints,
778 )
779 };
780 let pot_checkpoints = PotCheckpoints::slice_from_bytes(pot_checkpoints);
781
782 let body = Self {
783 own_segments,
784 intermediate_shard_blocks,
785 pot_checkpoints,
786 };
787
788 if !body.is_internally_consistent() {
789 return None;
790 }
791
792 Some((body, remainder))
793 }
794
795 #[inline]
800 pub fn is_internally_consistent(&self) -> bool {
801 true
803 }
804
805 #[inline]
808 pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
809 let num_pot_checkpoints = bytes.split_off(..size_of::<u32>())?;
818 let num_pot_checkpoints =
820 *unsafe { <u32 as TrivialType>::from_bytes(num_pot_checkpoints) }? as usize;
821
822 let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
823 let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
824
825 let own_segments = if num_own_segment_roots > 0 {
826 let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
827 let first_local_segment_index = unsafe {
829 Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
830 }
831 .as_inner();
832
833 let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
834 let own_segment_roots = unsafe {
836 slice::from_raw_parts(
837 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
838 num_own_segment_roots,
839 )
840 };
841 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
842
843 Some(OwnSegments {
844 first_local_segment_index,
845 segment_roots: own_segment_roots,
846 })
847 } else {
848 None
849 };
850
851 let (intermediate_shard_blocks, mut remainder) =
852 IntermediateShardBlocksInfo::try_from_bytes_unchecked(bytes)?;
853
854 let pot_checkpoints = remainder.split_off(..num_pot_checkpoints * PotCheckpoints::SIZE)?;
855 let pot_checkpoints = unsafe {
857 slice::from_raw_parts(
858 pot_checkpoints
859 .as_ptr()
860 .cast::<[u8; PotCheckpoints::SIZE]>(),
861 num_pot_checkpoints,
862 )
863 };
864 let pot_checkpoints = PotCheckpoints::slice_from_bytes(pot_checkpoints);
865
866 Some((
867 Self {
868 own_segments,
869 intermediate_shard_blocks,
870 pot_checkpoints,
871 },
872 remainder,
873 ))
874 }
875
876 #[cfg(feature = "alloc")]
878 #[inline(always)]
879 pub fn to_owned(self) -> OwnedBeaconChainBody {
880 if let Some(own_segments) = self.own_segments {
881 let first_local_segment_index = own_segments.first_local_segment_index;
882
883 OwnedBeaconChainBody::new(
884 own_segments.segment_roots.iter().copied().enumerate().map(
885 |(index, segment_root)| {
886 (
887 first_local_segment_index + LocalSegmentIndex::from(index as u64),
888 segment_root,
889 )
890 },
891 ),
892 self.intermediate_shard_blocks.iter(),
893 self.pot_checkpoints,
894 )
895 .expect("`self` is always a valid invariant; qed")
896 } else {
897 OwnedBeaconChainBody::new(
898 iter::empty(),
899 self.intermediate_shard_blocks.iter(),
900 self.pot_checkpoints,
901 )
902 .expect("`self` is always a valid invariant; qed")
903 }
904 }
905
906 #[inline(always)]
908 pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
909 self.own_segments
910 }
911
912 #[inline(always)]
914 pub fn intermediate_shard_blocks(&self) -> &IntermediateShardBlocksInfo<'a> {
915 &self.intermediate_shard_blocks
916 }
917
918 #[inline(always)]
921 pub fn pot_checkpoints(&self) -> &'a [PotCheckpoints] {
922 self.pot_checkpoints
923 }
924
925 #[inline]
927 pub fn root(&self) -> Blake3Hash {
928 let root = BalancedMerkleTree::compute_root_only(&[
930 *self
931 .own_segments
932 .as_ref()
933 .map(OwnSegments::root)
934 .unwrap_or_default(),
935 *self.intermediate_shard_blocks.segments_root(),
936 *self.intermediate_shard_blocks.headers_root(),
937 blake3::hash(PotCheckpoints::bytes_from_slice(self.pot_checkpoints).as_flattened())
938 .into(),
939 ]);
940
941 Blake3Hash::new(root)
942 }
943}
944
945#[derive(Debug, Copy, Clone)]
947pub struct LeafShardOwnSegments<'a> {
948 pub segment_roots_proof: &'a [u8; OUT_LEN],
950 pub own_segments: OwnSegments<'a>,
952}
953
954#[derive(Debug, Clone)]
956pub struct LeafShardBlockInfo<'a> {
957 pub header: LeafShardHeader<'a>,
959 pub segments: Option<LeafShardOwnSegments<'a>>,
961}
962
963#[derive(Debug, Copy, Clone)]
965pub struct LeafShardBlocksInfo<'a> {
966 num_blocks: usize,
967 bytes: &'a [u8],
968}
969
970impl<'a> LeafShardBlocksInfo<'a> {
971 #[inline]
977 pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
978 let num_blocks = bytes.split_off(..size_of::<u16>())?;
991 let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
992
993 let bytes_start = bytes;
994
995 let mut counts = bytes.split_off(..num_blocks * size_of::<u8>())?;
996
997 let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
998
999 for _ in 0..num_blocks {
1000 let num_own_segment_roots = usize::from(counts[0]);
1001 counts = &counts[1..];
1002
1003 (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
1004
1005 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
1006
1007 if num_own_segment_roots > 0 {
1008 let _first_local_segment_index =
1009 remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
1010 let _segment_roots_proof = remainder.split_off(..OUT_LEN)?;
1011 let _own_segment_roots =
1012 remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1013 }
1014 }
1015
1016 let info = Self {
1017 num_blocks,
1018 bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1019 };
1020
1021 if !info.is_internally_consistent() {
1022 return None;
1023 }
1024
1025 Some((info, remainder))
1026 }
1027
1028 #[inline]
1031 pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1032 let num_blocks = bytes.split_off(..size_of::<u16>())?;
1045 let num_blocks = usize::from(u16::from_le_bytes([num_blocks[0], num_blocks[1]]));
1046
1047 let bytes_start = bytes;
1048
1049 let mut counts = bytes.split_off(..num_blocks * size_of::<u8>())?;
1050
1051 let mut remainder = align_to_and_ensure_zero_padding::<u64>(bytes)?;
1052
1053 for _ in 0..num_blocks {
1054 let num_own_segment_roots = usize::from(counts[0]);
1055 counts = &counts[1..];
1056
1057 (_, remainder) = LeafShardHeader::try_from_bytes(remainder)?;
1058
1059 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)?;
1060
1061 if num_own_segment_roots > 0 {
1062 let _first_local_segment_index =
1063 remainder.split_off(..LocalSegmentIndex::SIZE as usize)?;
1064 let _segment_roots_proof = remainder.split_off(..OUT_LEN)?;
1065 let _own_segment_roots =
1066 remainder.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1067 }
1068 }
1069
1070 Some((
1071 Self {
1072 num_blocks,
1073 bytes: &bytes_start[..bytes_start.len() - remainder.len()],
1074 },
1075 remainder,
1076 ))
1077 }
1078
1079 #[inline]
1084 pub fn is_internally_consistent(&self) -> bool {
1085 let mut last_leaf_shard_info = None::<(ShardIndex, BlockNumber, Option<LocalSegmentIndex>)>;
1086
1087 self.iter().all(|leaf_shard_block| {
1088 let shard_index = leaf_shard_block.header.prefix.shard_index;
1089
1090 if let Some((
1092 last_leaf_shard_index,
1093 last_leaf_shard_block_number,
1094 last_leaf_shard_first_local_segment_index,
1095 )) = last_leaf_shard_info
1096 {
1097 match last_leaf_shard_index.cmp(&shard_index) {
1098 cmp::Ordering::Less => {
1099 last_leaf_shard_info.replace((
1100 shard_index,
1101 leaf_shard_block.header.prefix.number,
1102 leaf_shard_block
1103 .segments
1104 .as_ref()
1105 .map(|segments| segments.own_segments.first_local_segment_index),
1106 ));
1107 }
1108 cmp::Ordering::Equal => {
1109 if last_leaf_shard_block_number >= leaf_shard_block.header.prefix.number {
1110 return false;
1111 }
1112 if let Some(leaf_shard_segments) = &leaf_shard_block.segments {
1113 if let Some(last_leaf_shard_first_local_segment_index) =
1114 last_leaf_shard_first_local_segment_index
1115 && last_leaf_shard_first_local_segment_index
1116 >= leaf_shard_segments.own_segments.first_local_segment_index
1117 {
1118 return false;
1119 }
1120
1121 last_leaf_shard_info.replace((
1122 shard_index,
1123 leaf_shard_block.header.prefix.number,
1124 Some(leaf_shard_segments.own_segments.first_local_segment_index),
1125 ));
1126 } else {
1127 last_leaf_shard_info.replace((
1128 shard_index,
1129 leaf_shard_block.header.prefix.number,
1130 last_leaf_shard_first_local_segment_index,
1131 ));
1132 }
1133 }
1134 cmp::Ordering::Greater => {
1135 return false;
1136 }
1137 }
1138 } else {
1139 last_leaf_shard_info.replace((
1140 shard_index,
1141 leaf_shard_block.header.prefix.number,
1142 leaf_shard_block
1143 .segments
1144 .as_ref()
1145 .map(|segments| segments.own_segments.first_local_segment_index),
1146 ));
1147 }
1148
1149 let Some(segments) = leaf_shard_block.segments else {
1150 return true;
1151 };
1152
1153 BalancedMerkleTree::<2>::verify(
1154 &leaf_shard_block.header.result.body_root,
1155 array::from_ref(segments.segment_roots_proof),
1156 0,
1157 *segments.own_segments.root(),
1158 )
1159 })
1160 }
1161
1162 #[inline]
1164 pub fn iter(
1165 &self,
1166 ) -> impl ExactSizeIterator<Item = LeafShardBlockInfo<'a>> + TrustedLen + use<'a> {
1167 let (mut counts, mut remainder) = unsafe {
1169 self.bytes
1170 .split_at_unchecked(self.num_blocks * size_of::<u8>())
1171 };
1172
1173 iter::repeat_with(move || {
1174 let num_own_segment_roots = usize::from(counts[0]);
1175 counts = &counts[1..];
1176
1177 let header;
1179 (header, remainder) = LeafShardHeader::try_from_bytes(remainder)
1180 .expect("Already checked in constructor; qed");
1181
1182 remainder = align_to_and_ensure_zero_padding::<u64>(remainder)
1183 .expect("Already checked in constructor; qed");
1184
1185 let segments = if num_own_segment_roots > 0 {
1186 let first_local_segment_index;
1187 (first_local_segment_index, remainder) =
1189 unsafe { remainder.split_at_unchecked(LocalSegmentIndex::SIZE as usize) };
1190 let first_local_segment_index =
1192 *unsafe { LocalSegmentIndex::from_bytes_unchecked(first_local_segment_index) };
1193
1194 let segment_roots_proof;
1195 (segment_roots_proof, remainder) = unsafe { remainder.split_at_unchecked(OUT_LEN) };
1197 let segment_roots_proof = unsafe {
1199 segment_roots_proof
1200 .as_ptr()
1201 .cast::<[u8; OUT_LEN]>()
1202 .as_ref_unchecked()
1203 };
1204
1205 let own_segment_roots;
1206 (own_segment_roots, remainder) = unsafe {
1208 remainder.split_at_unchecked(num_own_segment_roots * SegmentRoot::SIZE)
1209 };
1210 let own_segment_roots = unsafe {
1212 slice::from_raw_parts(
1213 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1214 num_own_segment_roots,
1215 )
1216 };
1217 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1218
1219 Some(LeafShardOwnSegments {
1220 segment_roots_proof,
1221 own_segments: OwnSegments {
1222 first_local_segment_index,
1223 segment_roots: own_segment_roots,
1224 },
1225 })
1226 } else {
1227 None
1228 };
1229
1230 LeafShardBlockInfo { header, segments }
1231 })
1232 .take(self.num_blocks)
1233 }
1234
1235 #[inline(always)]
1237 pub const fn len(&self) -> usize {
1238 self.num_blocks
1239 }
1240
1241 #[inline(always)]
1243 pub const fn is_empty(&self) -> bool {
1244 self.num_blocks == 0
1245 }
1246
1247 #[inline]
1251 pub fn segments_root(&self) -> Blake3Hash {
1252 let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
1253 self.iter().map(|shard_block_info| {
1254 shard_block_info
1255 .segments
1256 .map(|own_segments| {
1257 own_segments
1258 .own_segments
1259 .root_with_shard_index(shard_block_info.header.prefix.shard_index)
1260 })
1261 .unwrap_or_default()
1262 }),
1263 )
1264 .unwrap_or_default();
1265
1266 Blake3Hash::new(root)
1267 }
1268
1269 #[inline]
1273 pub fn headers_root(&self) -> Blake3Hash {
1274 let root = UnbalancedMerkleTree::compute_root_only::<{ u16::MAX as u64 }, _, _>(
1275 self.iter().map(|shard_block_info| {
1276 single_block_hash(shard_block_info.header.root().as_ref())
1280 .expect("Less than a single block worth of bytes; qed")
1281 }),
1282 )
1283 .unwrap_or_default();
1284
1285 Blake3Hash::new(root)
1286 }
1287}
1288
1289#[derive(Debug, Copy, Clone, Yokeable)]
1291#[non_exhaustive]
1293pub struct IntermediateShardBody<'a> {
1294 own_segments: Option<OwnSegments<'a>>,
1296 leaf_shard_blocks: LeafShardBlocksInfo<'a>,
1298}
1299
1300impl<'a> GenericBlockBody<'a> for IntermediateShardBody<'a> {
1301 const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
1302
1303 #[cfg(feature = "alloc")]
1304 type Owned = OwnedIntermediateShardBody;
1305
1306 #[cfg(feature = "alloc")]
1307 #[inline(always)]
1308 fn to_owned(self) -> Self::Owned {
1309 self.to_owned()
1310 }
1311
1312 #[inline(always)]
1313 fn root(&self) -> Blake3Hash {
1314 self.root()
1315 }
1316}
1317
1318impl<'a> IntermediateShardBody<'a> {
1319 #[inline]
1325 pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1326 let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1333 let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1334
1335 let own_segments = if num_own_segment_roots > 0 {
1336 let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1337 let first_local_segment_index = unsafe {
1339 Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1340 }
1341 .as_inner();
1342
1343 let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1344 let own_segment_roots = unsafe {
1346 slice::from_raw_parts(
1347 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1348 num_own_segment_roots,
1349 )
1350 };
1351 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1352
1353 Some(OwnSegments {
1354 first_local_segment_index,
1355 segment_roots: own_segment_roots,
1356 })
1357 } else {
1358 None
1359 };
1360
1361 let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes(bytes)?;
1362
1363 let body = Self {
1364 own_segments,
1365 leaf_shard_blocks,
1366 };
1367
1368 if !body.is_internally_consistent() {
1369 return None;
1370 }
1371
1372 Some((body, remainder))
1373 }
1374
1375 #[inline]
1380 pub fn is_internally_consistent(&self) -> bool {
1381 true
1383 }
1384
1385 #[inline]
1388 pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1389 let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1396 let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1397
1398 let own_segments = if num_own_segment_roots > 0 {
1399 let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1400 let first_local_segment_index = unsafe {
1402 Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1403 }
1404 .as_inner();
1405
1406 let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1407 let own_segment_roots = unsafe {
1409 slice::from_raw_parts(
1410 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1411 num_own_segment_roots,
1412 )
1413 };
1414 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1415
1416 Some(OwnSegments {
1417 first_local_segment_index,
1418 segment_roots: own_segment_roots,
1419 })
1420 } else {
1421 None
1422 };
1423
1424 let (leaf_shard_blocks, remainder) = LeafShardBlocksInfo::try_from_bytes_unchecked(bytes)?;
1425
1426 Some((
1427 Self {
1428 own_segments,
1429 leaf_shard_blocks,
1430 },
1431 remainder,
1432 ))
1433 }
1434
1435 #[inline(always)]
1437 pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1438 self.own_segments
1439 }
1440
1441 #[inline(always)]
1443 pub fn leaf_shard_blocks(&self) -> &LeafShardBlocksInfo<'a> {
1444 &self.leaf_shard_blocks
1445 }
1446
1447 #[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 #[inline]
1474 pub fn root(&self) -> Blake3Hash {
1475 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#[derive(Debug, Copy, Clone)]
1494pub struct Transactions<'a> {
1495 num_transactions: usize,
1496 bytes: &'a [u8],
1497}
1498
1499impl<'a> Transactions<'a> {
1500 #[inline]
1506 pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1507 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 #[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 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 #[inline(always)]
1559 pub const fn len(&self) -> usize {
1560 self.num_transactions
1561 }
1562
1563 #[inline(always)]
1565 pub const fn is_empty(&self) -> bool {
1566 self.num_transactions == 0
1567 }
1568
1569 #[inline]
1573 pub fn root(&self) -> Blake3Hash {
1574 let root = UnbalancedMerkleTree::compute_root_only::<{ u32::MAX as u64 }, _, _>(
1575 self.iter().map(|transaction| {
1576 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#[derive(Debug, Copy, Clone, Yokeable)]
1591#[non_exhaustive]
1593pub struct LeafShardBody<'a> {
1594 own_segments: Option<OwnSegments<'a>>,
1596 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 #[inline]
1625 pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1626 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 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 let own_segment_roots = unsafe {
1646 slice::from_raw_parts(
1647 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1648 num_own_segment_roots,
1649 )
1650 };
1651 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1652
1653 Some(OwnSegments {
1654 first_local_segment_index,
1655 segment_roots: own_segment_roots,
1656 })
1657 } else {
1658 None
1659 };
1660
1661 let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1662
1663 let body = Self {
1664 own_segments,
1665 transactions,
1666 };
1667
1668 if !body.is_internally_consistent() {
1669 return None;
1670 }
1671
1672 Some((body, remainder))
1673 }
1674
1675 #[inline]
1680 pub fn is_internally_consistent(&self) -> bool {
1681 true
1683 }
1684
1685 #[inline]
1688 pub fn try_from_bytes_unchecked(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1689 let num_own_segment_roots = bytes.split_off(..size_of::<u8>())?;
1696 let num_own_segment_roots = usize::from(num_own_segment_roots[0]);
1697
1698 let own_segments = if num_own_segment_roots > 0 {
1699 let first_local_segment_index = bytes.split_off(..LocalSegmentIndex::SIZE as usize)?;
1700 let first_local_segment_index = unsafe {
1702 Unaligned::<LocalSegmentIndex>::from_bytes_unchecked(first_local_segment_index)
1703 }
1704 .as_inner();
1705
1706 let own_segment_roots = bytes.split_off(..num_own_segment_roots * SegmentRoot::SIZE)?;
1707 let own_segment_roots = unsafe {
1709 slice::from_raw_parts(
1710 own_segment_roots.as_ptr().cast::<[u8; SegmentRoot::SIZE]>(),
1711 num_own_segment_roots,
1712 )
1713 };
1714 let own_segment_roots = SegmentRoot::slice_from_repr(own_segment_roots);
1715
1716 Some(OwnSegments {
1717 first_local_segment_index,
1718 segment_roots: own_segment_roots,
1719 })
1720 } else {
1721 None
1722 };
1723
1724 let (transactions, remainder) = Transactions::try_from_bytes(bytes)?;
1725
1726 Some((
1727 Self {
1728 own_segments,
1729 transactions,
1730 },
1731 remainder,
1732 ))
1733 }
1734
1735 #[inline(always)]
1737 pub fn own_segments(&self) -> Option<OwnSegments<'a>> {
1738 self.own_segments
1739 }
1740
1741 #[inline(always)]
1743 pub fn transactions(&self) -> &Transactions<'a> {
1744 &self.transactions
1745 }
1746
1747 #[cfg(feature = "alloc")]
1749 #[inline(always)]
1750 pub fn to_owned(self) -> OwnedLeafShardBody {
1751 let mut builder = if let Some(own_segments) = self.own_segments {
1752 let first_local_segment_index = own_segments.first_local_segment_index;
1753
1754 OwnedLeafShardBody::init(own_segments.segment_roots.iter().copied().enumerate().map(
1755 |(index, segment_root)| {
1756 (
1757 first_local_segment_index + LocalSegmentIndex::from(index as u64),
1758 segment_root,
1759 )
1760 },
1761 ))
1762 .expect("`self` is always a valid invariant; qed")
1763 } else {
1764 OwnedLeafShardBody::init(iter::empty())
1765 .expect("`self` is always a valid invariant; qed")
1766 };
1767 for transaction in self.transactions.iter() {
1768 builder
1769 .add_transaction(transaction)
1770 .expect("`self` is always a valid invariant; qed");
1771 }
1772
1773 builder.finish()
1774 }
1775
1776 #[inline]
1778 pub fn root(&self) -> Blake3Hash {
1779 let root = BalancedMerkleTree::compute_root_only(&[
1780 *self
1781 .own_segments
1782 .as_ref()
1783 .map(OwnSegments::root)
1784 .unwrap_or_default(),
1785 *self.transactions.root(),
1786 ]);
1787
1788 Blake3Hash::new(root)
1789 }
1790}
1791
1792#[derive(Debug, Copy, Clone, From)]
1797pub enum BlockBody<'a> {
1798 BeaconChain(BeaconChainBody<'a>),
1800 IntermediateShard(IntermediateShardBody<'a>),
1802 LeafShard(LeafShardBody<'a>),
1804}
1805
1806impl<'a> BlockBody<'a> {
1807 #[inline]
1814 pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
1815 match shard_kind {
1816 RealShardKind::BeaconChain => {
1817 let (body, remainder) = BeaconChainBody::try_from_bytes(bytes)?;
1818 Some((Self::BeaconChain(body), remainder))
1819 }
1820 RealShardKind::IntermediateShard => {
1821 let (body, remainder) = IntermediateShardBody::try_from_bytes(bytes)?;
1822 Some((Self::IntermediateShard(body), remainder))
1823 }
1824 RealShardKind::LeafShard => {
1825 let (body, remainder) = LeafShardBody::try_from_bytes(bytes)?;
1826 Some((Self::LeafShard(body), remainder))
1827 }
1828 }
1829 }
1830
1831 #[inline]
1836 pub fn is_internally_consistent(&self) -> bool {
1837 match self {
1838 Self::BeaconChain(body) => body.is_internally_consistent(),
1839 Self::IntermediateShard(body) => body.is_internally_consistent(),
1840 Self::LeafShard(body) => body.is_internally_consistent(),
1841 }
1842 }
1843
1844 #[inline]
1847 pub fn try_from_bytes_unchecked(
1848 bytes: &'a [u8],
1849 shard_kind: RealShardKind,
1850 ) -> Option<(Self, &'a [u8])> {
1851 match shard_kind {
1852 RealShardKind::BeaconChain => {
1853 let (body, remainder) = BeaconChainBody::try_from_bytes_unchecked(bytes)?;
1854 Some((Self::BeaconChain(body), remainder))
1855 }
1856 RealShardKind::IntermediateShard => {
1857 let (body, remainder) = IntermediateShardBody::try_from_bytes_unchecked(bytes)?;
1858 Some((Self::IntermediateShard(body), remainder))
1859 }
1860 RealShardKind::LeafShard => {
1861 let (body, remainder) = LeafShardBody::try_from_bytes_unchecked(bytes)?;
1862 Some((Self::LeafShard(body), remainder))
1863 }
1864 }
1865 }
1866
1867 #[cfg(feature = "alloc")]
1869 #[inline(always)]
1870 pub fn to_owned(self) -> OwnedBlockBody {
1871 match self {
1872 Self::BeaconChain(body) => body.to_owned().into(),
1873 Self::IntermediateShard(body) => body.to_owned().into(),
1874 Self::LeafShard(body) => body.to_owned().into(),
1875 }
1876 }
1877
1878 #[inline]
1883 pub fn root(&self) -> Blake3Hash {
1884 match self {
1885 Self::BeaconChain(body) => body.root(),
1886 Self::IntermediateShard(body) => body.root(),
1887 Self::LeafShard(body) => body.root(),
1888 }
1889 }
1890}