1#[cfg(all(
6 test,
7 any(
8 not(miri),
9 not(any(
10 feature = "ed25519",
11 feature = "parallel",
12 feature = "scale-codec",
13 feature = "serde"
14 )),
15 all(
16 feature = "ed25519",
17 feature = "parallel",
18 feature = "scale-codec",
19 feature = "serde"
20 ),
21 ),
22))]
23mod tests;
24
25use crate::block::body::{
26 BeaconChainBody, BlockBody, GenericBlockBody, IntermediateShardBlockInfo,
27 IntermediateShardBody, LeafShardBlockInfo, LeafShardBody,
28};
29use crate::block::header::owned::{
30 OwnedIntermediateShardHeader, OwnedIntermediateShardHeaderError, OwnedLeafShardHeader,
31};
32use crate::pot::PotCheckpoints;
33use crate::segments::{LocalSegmentIndex, SegmentRoot};
34use crate::shard::RealShardKind;
35use crate::transaction::Transaction;
36use crate::transaction::owned::OwnedTransaction;
37use ab_aligned_buffer::alignment::Align16;
38use ab_aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
39use ab_io_type::alignment::Alignment;
40use ab_io_type::trivial_type::TrivialType;
41use core::fmt;
42use core::iter::{Step, TrustedLen};
43use derive_more::From;
44use triomphe::Arc;
45use yoke::Yoke;
46
47pub trait GenericOwnedBlockBody:
49 Clone + fmt::Debug + Send + Sync + Into<OwnedBlockBody> + 'static
50{
51 const SHARD_KIND: RealShardKind;
53
54 type Body<'a>: GenericBlockBody<'a>
56 where
57 Self: 'a;
58
59 fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer>;
61
62 fn buffer(&self) -> &SharedAlignedBuffer;
64
65 fn ref_count(&self) -> usize;
67
68 fn body(&self) -> &Self::Body<'_>;
70}
71
72pub impl(self) trait WritableBodyTransaction {
77 fn write_into(&self, buffer: &mut OwnedAlignedBuffer) -> Result<(), OwnedLeafShardBodyError>;
82}
83
84impl WritableBodyTransaction for Transaction<'_> {
85 #[inline(always)]
86 fn write_into(&self, buffer: &mut OwnedAlignedBuffer) -> Result<(), OwnedLeafShardBodyError> {
87 if buffer.append(self.bytes()) {
88 Ok(())
89 } else {
90 Err(OwnedLeafShardBodyError::BlockBodyIsTooLarge)
91 }
92 }
93}
94
95impl WritableBodyTransaction for &OwnedTransaction {
96 #[inline(always)]
97 fn write_into(&self, buffer: &mut OwnedAlignedBuffer) -> Result<(), OwnedLeafShardBodyError> {
98 if buffer.append(self.buffer().as_slice()) {
99 Ok(())
100 } else {
101 Err(OwnedLeafShardBodyError::BlockBodyIsTooLarge)
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
107struct TransactionBuilder {
108 num_transactions_offset: usize,
109 buffer: OwnedAlignedBuffer,
110}
111
112impl TransactionBuilder {
113 fn new(num_transactions_offset: usize, buffer: OwnedAlignedBuffer) -> Self {
114 Self {
115 num_transactions_offset,
116 buffer,
117 }
118 }
119
120 fn add_transaction<T>(&mut self, transaction: T) -> Result<(), OwnedLeafShardBodyError>
125 where
126 T: WritableBodyTransaction,
127 {
128 self.inc_transaction_count();
131
132 let old_buffer_len = self.buffer.len();
133
134 if let Err(error) = transaction.write_into(&mut self.buffer) {
135 self.dec_transaction_count();
136 return Err(error);
137 }
138
139 if !align_to_16_bytes_with_padding(&mut self.buffer) {
140 self.dec_transaction_count();
141 unsafe {
143 self.buffer.set_len(old_buffer_len);
144 }
145 return Err(OwnedLeafShardBodyError::BlockBodyIsTooLarge);
146 }
147
148 Ok(())
149 }
150
151 #[inline(always)]
153 fn finish(self) -> OwnedAlignedBuffer {
154 self.buffer
155 }
156
157 #[inline(always)]
159 fn inc_transaction_count(&mut self) {
160 unsafe {
163 let num_transactions_ptr = self
164 .buffer
165 .as_mut_ptr()
166 .byte_add(self.num_transactions_offset)
167 .cast::<u32>();
168 let num_transactions = num_transactions_ptr.read_unaligned();
169 let num_transactions = num_transactions.checked_add(1).expect(
170 "Each transaction takes at least `TransactionHeader::SIZE` bytes of the buffer, \
171 which can't be longer than `u32::MAX` bytes; qed",
172 );
173 num_transactions_ptr.write_unaligned(num_transactions);
174 }
175 }
176
177 #[inline(always)]
179 fn dec_transaction_count(&mut self) {
180 unsafe {
183 let num_transactions_ptr = self
184 .buffer
185 .as_mut_ptr()
186 .byte_add(self.num_transactions_offset)
187 .cast::<u32>();
188 let num_transactions = num_transactions_ptr.read_unaligned();
189 let num_transactions = num_transactions.saturating_sub(1);
190 num_transactions_ptr.write_unaligned(num_transactions);
191 }
192 }
193}
194
195#[derive(Debug, thiserror::Error)]
197pub enum OwnedBeaconChainBodyError {
198 #[error("Too many PoT checkpoints: {actual}")]
200 TooManyPotCheckpoints {
201 actual: usize,
203 },
204 #[error("Too many own segment roots: {actual}")]
206 TooManyOwnSegmentRoots {
207 actual: usize,
209 },
210 #[error("Own segment index overflow, first local segment index: {first_local_segment_index}")]
212 OwnSegmentIndexOverflow {
213 first_local_segment_index: LocalSegmentIndex,
215 },
216 #[error(
218 "Own segment indices are not consecutive: {local_segment_index} after \
219 {previous_local_segment_index}"
220 )]
221 NonConsecutiveOwnSegmentIndices {
222 previous_local_segment_index: LocalSegmentIndex,
224 local_segment_index: LocalSegmentIndex,
226 },
227 #[error("Too many intermediate shard blocks: {actual}")]
229 TooManyIntermediateShardBlocks {
230 actual: usize,
232 },
233 #[error("Block body is too large")]
235 BlockBodyIsTooLarge,
236 #[error("Intermediate shard blocks are inconsistent")]
242 InconsistentIntermediateShardBlocks,
243}
244
245#[derive(Debug, Clone)]
250pub struct OwnedBeaconChainBody {
251 inner: Arc<Yoke<BeaconChainBody<'static>, SharedAlignedBuffer>>,
252}
253
254impl GenericOwnedBlockBody for OwnedBeaconChainBody {
255 const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
256
257 type Body<'a> = BeaconChainBody<'a>;
258
259 #[inline(always)]
260 fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
261 Self::from_buffer(buffer)
262 }
263
264 #[inline(always)]
265 fn buffer(&self) -> &SharedAlignedBuffer {
266 self.buffer()
267 }
268
269 #[inline(always)]
270 fn ref_count(&self) -> usize {
271 self.ref_count()
272 }
273
274 #[inline(always)]
275 fn body(&self) -> &Self::Body<'_> {
276 self.body()
277 }
278}
279
280impl OwnedBeaconChainBody {
281 pub fn new<'a, OS, ISB>(
283 own_segments: OS,
284 intermediate_shard_blocks: ISB,
285 pot_checkpoints: &[PotCheckpoints],
286 ) -> Result<Self, OwnedBeaconChainBodyError>
287 where
288 OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
289 ISB: TrustedLen<Item = IntermediateShardBlockInfo<'a>> + 'a,
292 {
293 let num_pot_checkpoints = pot_checkpoints.len();
294 let num_pot_checkpoints = u32::try_from(num_pot_checkpoints).map_err(|_error| {
295 OwnedBeaconChainBodyError::TooManyPotCheckpoints {
296 actual: num_pot_checkpoints,
297 }
298 })?;
299 let num_own_segment_roots = own_segments.size_hint().0;
300 let num_own_segment_roots = u8::try_from(num_own_segment_roots).map_err(|_error| {
301 OwnedBeaconChainBodyError::TooManyOwnSegmentRoots {
302 actual: num_own_segment_roots,
303 }
304 })?;
305 let num_blocks = intermediate_shard_blocks.size_hint().0;
306 let num_blocks = u16::try_from(num_blocks).map_err(|_error| {
307 OwnedBeaconChainBodyError::TooManyIntermediateShardBlocks { actual: num_blocks }
308 })?;
309
310 let pot_checkpoints_size = num_pot_checkpoints
312 .checked_mul(PotCheckpoints::SIZE as u32)
313 .ok_or(OwnedBeaconChainBodyError::BlockBodyIsTooLarge)?;
314 let fixed_size = (
315 u32::SIZE
317 + u8::SIZE
319 + if num_own_segment_roots > 0 { LocalSegmentIndex::SIZE } else { 0 }
321 + u32::from(num_own_segment_roots) * SegmentRoot::SIZE as u32
323 + u16::SIZE
325 + u32::from(num_blocks) * (u8::SIZE + 2 * u16::SIZE)
328 )
329 .next_multiple_of(u32::from(Alignment::of::<u64>().get()));
331 #[cfg_attr(
334 target_pointer_width = "64",
335 expect(
336 clippy::absurd_extreme_comparisons,
337 reason = "`MAX_CAPACITY` is `u32::MAX` where `usize` is 64-bit"
338 )
339 )]
340 let min_size = fixed_size
341 .checked_add(pot_checkpoints_size)
342 .filter(|&size| size <= OwnedAlignedBuffer::<Align16>::MAX_CAPACITY)
343 .ok_or(OwnedBeaconChainBodyError::BlockBodyIsTooLarge)?;
344 let intermediate_shard_blocks_size =
347 u32::from(num_blocks) * OwnedIntermediateShardHeader::MAX_ALLOCATION * 2;
348 #[cfg_attr(
350 target_pointer_width = "64",
351 expect(
352 clippy::unnecessary_min_or_max,
353 reason = "`MAX_CAPACITY` is `u32::MAX` where `usize` is 64-bit"
354 )
355 )]
356 let mut buffer = OwnedAlignedBuffer::with_capacity(
357 min_size
358 .saturating_add(intermediate_shard_blocks_size)
359 .min(OwnedAlignedBuffer::<Align16>::MAX_CAPACITY),
360 );
361
362 let true = buffer.append(&num_pot_checkpoints.to_le_bytes()) else {
363 unreachable!("Fixed size data structures that are guaranteed to fit; qed");
364 };
365
366 let true = buffer.append(&[num_own_segment_roots]) else {
367 unreachable!("Fixed size data structures that are guaranteed to fit; qed");
368 };
369 let mut own_segments = own_segments.peekable();
370 if let Some(&(first_local_segment_index, _own_segment_root)) = own_segments.peek() {
371 if LocalSegmentIndex::forward_checked(
374 first_local_segment_index,
375 usize::from(num_own_segment_roots),
376 )
377 .is_none()
378 {
379 return Err(OwnedBeaconChainBodyError::OwnSegmentIndexOverflow {
380 first_local_segment_index,
381 });
382 }
383 let true = buffer.append(first_local_segment_index.as_bytes()) else {
384 unreachable!("Checked size above; qed");
385 };
386 }
387 let mut previous_local_segment_index = None;
388 for (local_segment_index, own_segment_root) in own_segments {
389 if let Some(previous_local_segment_index) = previous_local_segment_index
391 && LocalSegmentIndex::forward_checked(previous_local_segment_index, 1)
392 != Some(local_segment_index)
393 {
394 return Err(OwnedBeaconChainBodyError::NonConsecutiveOwnSegmentIndices {
395 previous_local_segment_index,
396 local_segment_index,
397 });
398 }
399 previous_local_segment_index = Some(local_segment_index);
400
401 let true = buffer.append(own_segment_root.as_ref()) else {
402 unreachable!("Checked size above; qed");
403 };
404 }
405 {
407 let true = buffer.append(&num_blocks.to_le_bytes()) else {
408 unreachable!("Fixed size data structures that are guaranteed to fit; qed");
409 };
410 let mut segments_roots_num_cursor = buffer.len() as usize;
411 for _ in 0..num_blocks {
412 let true = buffer.append(&[0; 5]) else {
415 unreachable!("Checked size above; qed");
416 };
417 }
418 let true = align_to_8_with_padding(&mut buffer) else {
419 unreachable!("Checked size above; qed");
420 };
421 for intermediate_shard_block in intermediate_shard_blocks {
422 let num_own_segment_roots = intermediate_shard_block
423 .own_segments
424 .as_ref()
425 .map_or_default(|own_segments| own_segments.num_segment_roots().get());
426 let num_leaf_shard_blocks_with_segments =
427 intermediate_shard_block.num_leaf_shard_blocks_with_segments();
428
429 buffer.as_mut_slice()[segments_roots_num_cursor] = num_own_segment_roots;
430 buffer.as_mut_slice()[segments_roots_num_cursor + 1..][..2].copy_from_slice(
431 &intermediate_shard_block
432 .num_leaf_shard_blocks()
433 .to_le_bytes(),
434 );
435 buffer.as_mut_slice()[segments_roots_num_cursor + 3..][..2]
436 .copy_from_slice(&num_leaf_shard_blocks_with_segments.to_le_bytes());
437 segments_roots_num_cursor += 5;
438
439 OwnedIntermediateShardHeader::from_parts_into(
440 intermediate_shard_block.header.prefix,
441 intermediate_shard_block.header.result,
442 intermediate_shard_block.header.consensus_info,
443 intermediate_shard_block.header.beacon_chain_info(),
444 intermediate_shard_block.header.leaf_shard_blocks_root(),
445 &mut buffer,
446 )
447 .map_err(|error| match error {
448 OwnedIntermediateShardHeaderError::HeaderTooLarge => {
449 OwnedBeaconChainBodyError::BlockBodyIsTooLarge
450 }
451 OwnedIntermediateShardHeaderError::InvalidParts { error: _ } => {
452 unreachable!("Parts of a parsed header are consistent; qed")
453 }
454 })?
455 .with_seal(intermediate_shard_block.header.seal)
456 .expect("Seal of a parsed header matches its solution; qed");
457
458 if let Some(own_segments) = &intermediate_shard_block.own_segments {
459 if !buffer.append(own_segments.first_local_segment_index.as_bytes()) {
460 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
461 }
462 if !buffer.append(SegmentRoot::slice_as_bytes(own_segments.segment_roots)) {
463 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
464 }
465 }
466
467 for (_position, _shard_index, own_segments) in
468 intermediate_shard_block.leaf_shards_segments()
469 {
470 if !buffer.append(&[own_segments.num_segment_roots().get()]) {
471 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
472 }
473 }
474 for (position, shard_index, own_segments) in
475 intermediate_shard_block.leaf_shards_segments()
476 {
477 if !buffer.append(&position.to_le_bytes()) {
478 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
479 }
480 if !buffer.append(shard_index.as_bytes()) {
481 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
482 }
483 if !buffer.append(own_segments.first_local_segment_index.as_bytes()) {
484 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
485 }
486 if !buffer.append(SegmentRoot::slice_as_bytes(own_segments.segment_roots)) {
487 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
488 }
489 }
490
491 if !align_to_8_with_padding(&mut buffer) {
492 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
493 }
494 }
495 }
496
497 let true = buffer.append(PotCheckpoints::slice_as_bytes(pot_checkpoints)) else {
498 return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
499 };
500
501 Self::from_buffer(buffer.into_shared()).map_err(|_buffer| {
502 OwnedBeaconChainBodyError::InconsistentIntermediateShardBlocks
505 })
506 }
507
508 #[inline]
510 pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
511 let inner = Yoke::try_attach_to_cart(buffer.clone(), |buffer| {
514 let Some((body, extra_bytes)) = BeaconChainBody::try_from_bytes(buffer) else {
515 return Err(());
516 };
517 if !extra_bytes.is_empty() {
518 return Err(());
519 }
520
521 Ok(body)
522 })
523 .map_err(move |()| buffer)?;
524
525 Ok(Self {
526 inner: Arc::new(inner),
527 })
528 }
529
530 #[inline(always)]
532 pub fn buffer(&self) -> &SharedAlignedBuffer {
533 self.inner.backing_cart()
534 }
535
536 #[inline(always)]
538 pub fn ref_count(&self) -> usize {
539 Arc::count(&self.inner)
540 }
541
542 #[inline(always)]
544 pub fn body(&self) -> &BeaconChainBody<'_> {
545 self.inner.get()
546 }
547}
548
549#[derive(Debug, thiserror::Error)]
551pub enum OwnedIntermediateShardBodyError {
552 #[error("Too many own segment roots: {actual}")]
554 TooManyOwnSegmentRoots {
555 actual: usize,
557 },
558 #[error("Own segment index overflow, first local segment index: {first_local_segment_index}")]
560 OwnSegmentIndexOverflow {
561 first_local_segment_index: LocalSegmentIndex,
563 },
564 #[error(
566 "Own segment indices are not consecutive: {local_segment_index} after \
567 {previous_local_segment_index}"
568 )]
569 NonConsecutiveOwnSegmentIndices {
570 previous_local_segment_index: LocalSegmentIndex,
572 local_segment_index: LocalSegmentIndex,
574 },
575 #[error("Too many leaf shard blocks: {actual}")]
577 TooManyLeafShardBlocks {
578 actual: usize,
580 },
581 #[error("Leaf shard blocks are inconsistent")]
586 InconsistentLeafShardBlocks,
587}
588
589#[derive(Debug, Clone)]
594pub struct OwnedIntermediateShardBody {
595 inner: Arc<Yoke<IntermediateShardBody<'static>, SharedAlignedBuffer>>,
596}
597
598impl GenericOwnedBlockBody for OwnedIntermediateShardBody {
599 const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
600
601 type Body<'a> = IntermediateShardBody<'a>;
602
603 #[inline(always)]
604 fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
605 Self::from_buffer(buffer)
606 }
607
608 #[inline(always)]
609 fn buffer(&self) -> &SharedAlignedBuffer {
610 self.buffer()
611 }
612
613 #[inline(always)]
614 fn ref_count(&self) -> usize {
615 self.ref_count()
616 }
617
618 #[inline(always)]
619 fn body(&self) -> &Self::Body<'_> {
620 self.body()
621 }
622}
623
624impl OwnedIntermediateShardBody {
625 pub fn new<'a, OS, LSB>(
627 own_segments: OS,
628 leaf_shard_blocks: LSB,
629 ) -> Result<Self, OwnedIntermediateShardBodyError>
630 where
631 OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
632 LSB: TrustedLen<Item = LeafShardBlockInfo<'a>> + 'a,
633 {
634 let num_own_segment_roots = own_segments.size_hint().0;
635 let num_own_segment_roots = u8::try_from(num_own_segment_roots).map_err(|_error| {
636 OwnedIntermediateShardBodyError::TooManyOwnSegmentRoots {
637 actual: num_own_segment_roots,
638 }
639 })?;
640 let num_blocks = leaf_shard_blocks.size_hint().0;
641 let num_blocks = u16::try_from(num_blocks).map_err(|_error| {
642 OwnedIntermediateShardBodyError::TooManyLeafShardBlocks { actual: num_blocks }
643 })?;
644
645 let mut buffer = OwnedAlignedBuffer::with_capacity(
646 (
647 u8::SIZE
649 + if num_own_segment_roots > 0 { LocalSegmentIndex::SIZE } else { 0 }
651 + u32::from(num_own_segment_roots) * SegmentRoot::SIZE as u32
653 + u16::SIZE
655 + u32::from(num_blocks) * u8::SIZE
657 )
658 .next_multiple_of(u32::from(Alignment::of::<u64>().get()))
660 + u32::from(num_blocks) * OwnedLeafShardHeader::MAX_ALLOCATION * 2,
663 );
664
665 let true = buffer.append(&[num_own_segment_roots]) else {
666 unreachable!("Fixed size data structures that are guaranteed to fit; qed");
667 };
668 let mut own_segments = own_segments.peekable();
669 if let Some(&(first_local_segment_index, _own_segment_root)) = own_segments.peek() {
670 if LocalSegmentIndex::forward_checked(
673 first_local_segment_index,
674 usize::from(num_own_segment_roots),
675 )
676 .is_none()
677 {
678 return Err(OwnedIntermediateShardBodyError::OwnSegmentIndexOverflow {
679 first_local_segment_index,
680 });
681 }
682 let true = buffer.append(first_local_segment_index.as_bytes()) else {
683 unreachable!("Checked size above; qed");
684 };
685 }
686 let mut previous_local_segment_index = None;
687 for (local_segment_index, own_segment_root) in own_segments {
688 if let Some(previous_local_segment_index) = previous_local_segment_index
690 && LocalSegmentIndex::forward_checked(previous_local_segment_index, 1)
691 != Some(local_segment_index)
692 {
693 return Err(
694 OwnedIntermediateShardBodyError::NonConsecutiveOwnSegmentIndices {
695 previous_local_segment_index,
696 local_segment_index,
697 },
698 );
699 }
700 previous_local_segment_index = Some(local_segment_index);
701
702 let true = buffer.append(own_segment_root.as_ref()) else {
703 unreachable!("Checked size above; qed");
704 };
705 }
706 {
708 let true = buffer.append(&num_blocks.to_le_bytes()) else {
709 unreachable!("Fixed size data structures that are guaranteed to fit; qed");
710 };
711 let mut own_segments_roots_num_cursor = buffer.len() as usize;
712 for _ in 0..num_blocks {
713 let true = buffer.append(&[0]) else {
714 unreachable!("Checked size above; qed");
715 };
716 }
717 let true = align_to_8_with_padding(&mut buffer) else {
718 unreachable!("Checked size above; qed");
719 };
720 for leaf_shard_block in leaf_shard_blocks {
721 if let Some(own_segments) = &leaf_shard_block.own_segments {
722 buffer.as_mut_slice()[own_segments_roots_num_cursor] =
723 own_segments.num_segment_roots().get();
724 }
725 own_segments_roots_num_cursor += 1;
726
727 OwnedLeafShardHeader::from_parts_into(
728 leaf_shard_block.header.prefix,
729 leaf_shard_block.header.result,
730 leaf_shard_block.header.consensus_info,
731 leaf_shard_block.header.beacon_chain_info(),
732 &mut buffer,
733 )
734 .expect(
735 "Parts of a parsed header are consistent, and bodies with at most `u16::MAX` \
736 leaf shard blocks with at most `u8::MAX` segment roots each are smaller than \
737 1 GiB, which fits into a buffer even where `usize` is 32-bit; qed",
738 )
739 .with_seal(leaf_shard_block.header.seal)
740 .expect("Seal of a parsed header matches its solution; qed");
741 let true = align_to_8_with_padding(&mut buffer) else {
742 unreachable!("Checked size above; qed");
743 };
744
745 let true = buffer.append(leaf_shard_block.transactions_root.as_bytes()) else {
746 unreachable!("Checked size above; qed");
747 };
748 if let Some(own_segments) = &leaf_shard_block.own_segments {
749 let true = buffer.append(own_segments.first_local_segment_index.as_bytes())
750 else {
751 unreachable!("Checked size above; qed");
752 };
753 let true =
754 buffer.append(SegmentRoot::slice_as_bytes(own_segments.segment_roots))
755 else {
756 unreachable!("Checked size above; qed");
757 };
758 }
759 }
760 }
761
762 Self::from_buffer(buffer.into_shared()).map_err(|_buffer| {
763 OwnedIntermediateShardBodyError::InconsistentLeafShardBlocks
766 })
767 }
768
769 #[inline]
771 pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
772 let inner = Yoke::try_attach_to_cart(buffer.clone(), |buffer| {
775 let Some((body, extra_bytes)) = IntermediateShardBody::try_from_bytes(buffer) else {
776 return Err(());
777 };
778 if !extra_bytes.is_empty() {
779 return Err(());
780 }
781
782 Ok(body)
783 })
784 .map_err(move |()| buffer)?;
785
786 Ok(Self {
787 inner: Arc::new(inner),
788 })
789 }
790
791 #[inline(always)]
793 pub fn buffer(&self) -> &SharedAlignedBuffer {
794 self.inner.backing_cart()
795 }
796
797 #[inline(always)]
799 pub fn ref_count(&self) -> usize {
800 Arc::count(&self.inner)
801 }
802
803 #[inline(always)]
805 pub fn body(&self) -> &IntermediateShardBody<'_> {
806 self.inner.get()
807 }
808}
809
810#[derive(Debug, thiserror::Error)]
812pub enum OwnedLeafShardBodyError {
813 #[error("Too many own segment roots: {actual}")]
815 TooManyOwnSegmentRoots {
816 actual: usize,
818 },
819 #[error("Own segment index overflow, first local segment index: {first_local_segment_index}")]
821 OwnSegmentIndexOverflow {
822 first_local_segment_index: LocalSegmentIndex,
824 },
825 #[error(
827 "Own segment indices are not consecutive: {local_segment_index} after \
828 {previous_local_segment_index}"
829 )]
830 NonConsecutiveOwnSegmentIndices {
831 previous_local_segment_index: LocalSegmentIndex,
833 local_segment_index: LocalSegmentIndex,
835 },
836 #[error("Block body is too large")]
838 BlockBodyIsTooLarge,
839}
840
841#[derive(Debug, Clone)]
846pub struct OwnedLeafShardBody {
847 inner: Arc<Yoke<LeafShardBody<'static>, SharedAlignedBuffer>>,
848}
849
850impl GenericOwnedBlockBody for OwnedLeafShardBody {
851 const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
852
853 type Body<'a> = LeafShardBody<'a>;
854
855 #[inline(always)]
856 fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
857 Self::from_buffer(buffer)
858 }
859
860 #[inline(always)]
861 fn buffer(&self) -> &SharedAlignedBuffer {
862 self.buffer()
863 }
864
865 #[inline(always)]
866 fn ref_count(&self) -> usize {
867 self.ref_count()
868 }
869
870 #[inline(always)]
871 fn body(&self) -> &Self::Body<'_> {
872 self.body()
873 }
874}
875
876impl OwnedLeafShardBody {
877 pub fn init<OS>(
879 own_segments: OS,
880 ) -> Result<OwnedLeafShardBlockBodyBuilder, OwnedLeafShardBodyError>
881 where
882 OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
883 {
884 let num_own_segment_roots = own_segments.size_hint().0;
885 let num_own_segment_roots = u8::try_from(num_own_segment_roots).map_err(|_error| {
886 OwnedLeafShardBodyError::TooManyOwnSegmentRoots {
887 actual: num_own_segment_roots,
888 }
889 })?;
890
891 let mut buffer = OwnedAlignedBuffer::with_capacity(
893 (
894 u8::SIZE
896 + if num_own_segment_roots > 0 {
898 LocalSegmentIndex::SIZE
899 } else {
900 0
901 }
902 + u32::from(num_own_segment_roots) * SegmentRoot::SIZE as u32
904 + u32::SIZE
906 )
907 .next_multiple_of(u32::from(Alignment::MAX.get())),
909 );
910
911 let true = buffer.append(&[num_own_segment_roots]) else {
912 unreachable!("Fixed size data structures that are guaranteed to fit; qed");
913 };
914 let mut own_segments = own_segments.peekable();
915 if let Some(&(first_local_segment_index, _own_segment_root)) = own_segments.peek() {
916 if LocalSegmentIndex::forward_checked(
919 first_local_segment_index,
920 usize::from(num_own_segment_roots),
921 )
922 .is_none()
923 {
924 return Err(OwnedLeafShardBodyError::OwnSegmentIndexOverflow {
925 first_local_segment_index,
926 });
927 }
928 let true = buffer.append(first_local_segment_index.as_bytes()) else {
929 unreachable!("Checked size above; qed");
930 };
931 }
932 let mut previous_local_segment_index = None;
933 for (local_segment_index, own_segment_root) in own_segments {
934 if let Some(previous_local_segment_index) = previous_local_segment_index
936 && LocalSegmentIndex::forward_checked(previous_local_segment_index, 1)
937 != Some(local_segment_index)
938 {
939 return Err(OwnedLeafShardBodyError::NonConsecutiveOwnSegmentIndices {
940 previous_local_segment_index,
941 local_segment_index,
942 });
943 }
944 previous_local_segment_index = Some(local_segment_index);
945
946 let true = buffer.append(own_segment_root.as_ref()) else {
947 unreachable!("Checked size above; qed");
948 };
949 }
950
951 let num_transactions_offset = buffer.len() as usize;
952 let true = buffer.append(&0u32.to_le_bytes()) else {
953 unreachable!("Checked size above; qed");
954 };
955 let true = align_to_16_bytes_with_padding(&mut buffer) else {
957 unreachable!("Checked size above; qed");
958 };
959
960 Ok(OwnedLeafShardBlockBodyBuilder {
961 transaction_builder: TransactionBuilder::new(num_transactions_offset, buffer),
962 })
963 }
964
965 #[inline]
967 pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
968 let inner = Yoke::try_attach_to_cart(buffer.clone(), |buffer| {
971 let Some((body, extra_bytes)) = LeafShardBody::try_from_bytes(buffer) else {
972 return Err(());
973 };
974 if !extra_bytes.is_empty() {
975 return Err(());
976 }
977
978 Ok(body)
979 })
980 .map_err(move |()| buffer)?;
981
982 Ok(Self {
983 inner: Arc::new(inner),
984 })
985 }
986
987 #[inline(always)]
989 pub fn buffer(&self) -> &SharedAlignedBuffer {
990 self.inner.backing_cart()
991 }
992
993 #[inline(always)]
995 pub fn ref_count(&self) -> usize {
996 Arc::count(&self.inner)
997 }
998
999 #[inline(always)]
1001 pub fn body(&self) -> &LeafShardBody<'_> {
1002 self.inner.get()
1003 }
1004}
1005
1006#[derive(Debug, Clone)]
1008pub struct OwnedLeafShardBlockBodyBuilder {
1009 transaction_builder: TransactionBuilder,
1010}
1011
1012impl OwnedLeafShardBlockBodyBuilder {
1013 #[inline(always)]
1015 pub fn add_transaction<T>(&mut self, transaction: T) -> Result<(), OwnedLeafShardBodyError>
1016 where
1017 T: WritableBodyTransaction,
1018 {
1019 self.transaction_builder.add_transaction(transaction)?;
1020
1021 Ok(())
1022 }
1023
1024 pub fn finish(self) -> OwnedLeafShardBody {
1026 OwnedLeafShardBody::from_buffer(self.transaction_builder.finish().into_shared())
1027 .expect("Known to be created correctly; qed")
1028 }
1029}
1030
1031#[derive(Debug, Clone, From)]
1036pub enum OwnedBlockBody {
1037 BeaconChain(OwnedBeaconChainBody),
1039 IntermediateShard(OwnedIntermediateShardBody),
1041 LeafShard(OwnedLeafShardBody),
1043}
1044
1045impl OwnedBlockBody {
1046 #[inline]
1048 pub fn from_buffer(
1049 buffer: SharedAlignedBuffer,
1050 shard_kind: RealShardKind,
1051 ) -> Result<Self, SharedAlignedBuffer> {
1052 Ok(match shard_kind {
1053 RealShardKind::BeaconChain => {
1054 Self::BeaconChain(OwnedBeaconChainBody::from_buffer(buffer)?)
1055 }
1056 RealShardKind::IntermediateShard => {
1057 Self::IntermediateShard(OwnedIntermediateShardBody::from_buffer(buffer)?)
1058 }
1059 RealShardKind::LeafShard => Self::LeafShard(OwnedLeafShardBody::from_buffer(buffer)?),
1060 })
1061 }
1062
1063 #[inline]
1065 pub fn buffer(&self) -> &SharedAlignedBuffer {
1066 match self {
1067 Self::BeaconChain(owned_body) => owned_body.buffer(),
1068 Self::IntermediateShard(owned_body) => owned_body.buffer(),
1069 Self::LeafShard(owned_body) => owned_body.buffer(),
1070 }
1071 }
1072
1073 #[inline(always)]
1075 pub fn ref_count(&self) -> usize {
1076 match self {
1077 Self::BeaconChain(owned_body) => owned_body.ref_count(),
1078 Self::IntermediateShard(owned_body) => owned_body.ref_count(),
1079 Self::LeafShard(owned_body) => owned_body.ref_count(),
1080 }
1081 }
1082
1083 #[inline]
1085 pub fn body(&self) -> BlockBody<'_> {
1086 match self {
1087 Self::BeaconChain(owned_body) => BlockBody::BeaconChain(*owned_body.body()),
1088 Self::IntermediateShard(owned_body) => BlockBody::IntermediateShard(*owned_body.body()),
1089 Self::LeafShard(owned_body) => BlockBody::LeafShard(*owned_body.body()),
1090 }
1091 }
1092}
1093
1094#[inline(always)]
1098#[must_use = "Check if buffer becomes too long"]
1099fn align_to_8_with_padding(buffer: &mut OwnedAlignedBuffer) -> bool {
1100 let alignment = usize::from(Alignment::of::<u64>().get());
1101 let unaligned_by = buffer.len() as usize % alignment;
1102 if unaligned_by > 0 {
1103 let padding_bytes = unsafe { alignment.unchecked_sub(unaligned_by) };
1105
1106 if !buffer.append(&0u64.to_le_bytes()[..padding_bytes]) {
1107 return false;
1108 }
1109 }
1110
1111 true
1112}
1113
1114#[inline(always)]
1118#[must_use = "Check if buffer becomes too long"]
1119fn align_to_16_bytes_with_padding(buffer: &mut OwnedAlignedBuffer) -> bool {
1120 let alignment = usize::from(Alignment::MAX.get());
1121 let unaligned_by = buffer.len() as usize % alignment;
1122 if unaligned_by > 0 {
1123 let padding_bytes = unsafe { alignment.unchecked_sub(unaligned_by) };
1125
1126 if !buffer.append(&0u128.to_le_bytes()[..padding_bytes]) {
1127 return false;
1128 }
1129 }
1130
1131 true
1132}