Skip to main content

ab_core_primitives/block/body/
owned.rs

1//! Data structures related to the owned version of [`BlockBody`]
2
3#[cfg(test)]
4mod tests;
5
6use crate::block::body::{
7    BeaconChainBody, BlockBody, GenericBlockBody, IntermediateShardBlockInfo,
8    IntermediateShardBody, LeafShardBlockInfo, LeafShardBody,
9};
10use crate::block::header::owned::{
11    OwnedIntermediateShardHeader, OwnedIntermediateShardHeaderError, OwnedLeafShardHeader,
12};
13use crate::pot::PotCheckpoints;
14use crate::segments::{LocalSegmentIndex, SegmentRoot};
15use crate::shard::RealShardKind;
16use crate::transaction::Transaction;
17use crate::transaction::owned::{OwnedTransaction, OwnedTransactionError};
18use ab_aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
19use ab_io_type::alignment::Alignment;
20use ab_io_type::trivial_type::TrivialType;
21use core::fmt;
22use core::iter::TrustedLen;
23use derive_more::From;
24use triomphe::Arc;
25use yoke::Yoke;
26
27/// Generic owned block body
28pub trait GenericOwnedBlockBody:
29    Clone + fmt::Debug + Send + Sync + Into<OwnedBlockBody> + 'static
30{
31    /// Shard kind
32    const SHARD_KIND: RealShardKind;
33
34    /// Block body
35    type Body<'a>: GenericBlockBody<'a>
36    where
37        Self: 'a;
38
39    /// Create an owned body from a buffer
40    fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer>;
41
42    /// Inner buffer with block body contents
43    fn buffer(&self) -> &SharedAlignedBuffer;
44
45    /// Number of clones in memory
46    fn ref_count(&self) -> usize;
47
48    /// Get a regular block body out of the owned version
49    fn body(&self) -> &Self::Body<'_>;
50}
51
52/// Transaction addition error
53#[derive(Debug, thiserror::Error)]
54enum AddTransactionError {
55    /// Block body is too large
56    #[error("Block body is too large")]
57    BlockBodyIsTooLarge,
58    /// Too many transactions
59    #[error("Too many transactions")]
60    TooManyTransactions,
61    /// Failed to add transaction
62    #[error("Failed to add transaction: {error}")]
63    FailedToAddTransaction {
64        /// Inner error
65        #[from]
66        error: OwnedTransactionError,
67    },
68}
69
70/// Transaction that can be written into the body.
71///
72/// Can't be implemented outside of this module, because block body builders rely on it appending
73/// exactly one correctly encoded transaction to the buffer.
74pub impl(self) trait WritableBodyTransaction {
75    /// Write this transaction into the body
76    fn write_into(&self, buffer: &mut OwnedAlignedBuffer) -> Result<(), OwnedTransactionError>;
77}
78
79impl WritableBodyTransaction for Transaction<'_> {
80    fn write_into(&self, buffer: &mut OwnedAlignedBuffer) -> Result<(), OwnedTransactionError> {
81        OwnedTransaction::from_parts_into(
82            self.header,
83            self.read_slots,
84            self.write_slots,
85            self.payload,
86            self.seal,
87            buffer,
88        )
89    }
90}
91
92impl WritableBodyTransaction for &OwnedTransaction {
93    fn write_into(&self, buffer: &mut OwnedAlignedBuffer) -> Result<(), OwnedTransactionError> {
94        if buffer.append(self.buffer().as_slice()) {
95            Ok(())
96        } else {
97            Err(OwnedTransactionError::TransactionTooLarge)
98        }
99    }
100}
101
102#[derive(Debug, Clone)]
103struct TransactionBuilder {
104    num_transactions_offset: usize,
105    buffer: OwnedAlignedBuffer,
106}
107
108impl TransactionBuilder {
109    fn new(num_transactions_offset: usize, buffer: OwnedAlignedBuffer) -> Self {
110        Self {
111            num_transactions_offset,
112            buffer,
113        }
114    }
115
116    /// Add transaction to the body
117    fn add_transaction<T>(&mut self, transaction: T) -> Result<(), AddTransactionError>
118    where
119        T: WritableBodyTransaction,
120    {
121        // Transactions are aligned, but the very first might come after non-transaction fields that
122        // were not aligned
123        if self.inc_transaction_count()? == 1 && !align_to_16_bytes_with_padding(&mut self.buffer) {
124            self.dec_transaction_count();
125            return Err(AddTransactionError::BlockBodyIsTooLarge);
126        }
127
128        let old_buffer_len = self.buffer.len();
129
130        if let Err(error) = transaction.write_into(&mut self.buffer) {
131            self.dec_transaction_count();
132            // SAFETY: `WritableBodyTransaction` can only be implemented in this module and all
133            // implementations only append to the buffer (possibly a part of the transaction before
134            // failing), so the first `old_buffer_len` bytes are still initialized
135            unsafe {
136                self.buffer.set_len(old_buffer_len);
137            }
138            return Err(error.into());
139        }
140
141        if !align_to_16_bytes_with_padding(&mut self.buffer) {
142            self.dec_transaction_count();
143            // SAFETY: Length was obtained from the same buffer before the last write
144            unsafe {
145                self.buffer.set_len(old_buffer_len);
146            }
147            return Err(AddTransactionError::BlockBodyIsTooLarge);
148        }
149
150        Ok(())
151    }
152
153    /// Finish building block body
154    #[inline(always)]
155    fn finish(self) -> OwnedAlignedBuffer {
156        self.buffer
157    }
158
159    /// Increase the number of stored transactions and return the new value
160    #[inline(always)]
161    fn inc_transaction_count(&mut self) -> Result<u32, AddTransactionError> {
162        // SAFETY: Constructor ensures the offset is valid and has space for `u32` (but not
163        // necessarily aligned)
164        unsafe {
165            let num_transactions_ptr = self
166                .buffer
167                .as_mut_ptr()
168                .byte_add(self.num_transactions_offset)
169                .cast::<u32>();
170            let num_transactions = num_transactions_ptr.read_unaligned();
171            let num_transactions = num_transactions
172                .checked_add(1)
173                .ok_or(AddTransactionError::TooManyTransactions)?;
174            num_transactions_ptr.write_unaligned(num_transactions);
175            Ok(num_transactions)
176        }
177    }
178
179    /// Decrease the number of stored transactions
180    #[inline(always)]
181    fn dec_transaction_count(&mut self) {
182        // SAFETY: Constructor ensures the offset is valid and has space for `u32` (but not
183        // necessarily aligned)
184        unsafe {
185            let num_transactions_ptr = self
186                .buffer
187                .as_mut_ptr()
188                .byte_add(self.num_transactions_offset)
189                .cast::<u32>();
190            let num_transactions = num_transactions_ptr.read_unaligned();
191            let num_transactions = num_transactions.saturating_sub(1);
192            num_transactions_ptr.write_unaligned(num_transactions);
193        }
194    }
195}
196
197/// Errors for [`OwnedBeaconChainBody`]
198#[derive(Debug, thiserror::Error)]
199pub enum OwnedBeaconChainBodyError {
200    /// Too many PoT checkpoints
201    #[error("Too many PoT checkpoints: {actual}")]
202    TooManyPotCheckpoints {
203        /// Actual number of PoT checkpoints
204        actual: usize,
205    },
206    /// Too many own segment roots
207    #[error("Too many own segment roots: {actual}")]
208    TooManyOwnSegmentRoots {
209        /// Actual number of own segment roots
210        actual: usize,
211    },
212    /// Too many intermediate shard blocks
213    #[error("Too many intermediate shard blocks: {actual}")]
214    TooManyIntermediateShardBlocks {
215        /// Actual number of intermediate shard blocks
216        actual: usize,
217    },
218    /// Too many intermediate shard own segment roots
219    #[error("Too many intermediate shard own segment roots: {actual}")]
220    TooManyIntermediateShardOwnSegmentRoots {
221        /// Actual number of own segment roots
222        actual: usize,
223    },
224    /// Too many leaf shard blocks with segments
225    #[error("Too many leaf shard blocks with segments: {actual}")]
226    TooManyLeafShardBlocksWithSegments {
227        /// Actual number of shard blocks with segments
228        actual: usize,
229    },
230    /// Too many leaf shard block segments
231    #[error("Too many leaf shard block segments: {actual}")]
232    TooManyLeafShardBlockSegments {
233        /// Actual number of shard block segments
234        actual: usize,
235    },
236    /// Too many intermediate shard child segment roots
237    #[error("Too many intermediate shard child segment roots: {actual}")]
238    TooManyIntermediateShardChildSegmentRoots {
239        /// Actual number of child segment roots
240        actual: usize,
241    },
242    /// Failed to intermediate shard header
243    #[error("Failed to intermediate shard header: {error}")]
244    FailedToAddIntermediateShard {
245        /// Inner error
246        #[from]
247        error: OwnedIntermediateShardHeaderError,
248    },
249    /// Block body is too large
250    #[error("Block body is too large")]
251    BlockBodyIsTooLarge,
252}
253
254/// An owned version of [`BeaconChainBody`].
255///
256/// It is correctly aligned in memory and well suited for sending and receiving over the network
257/// efficiently or storing in memory or on disk.
258#[derive(Debug, Clone)]
259pub struct OwnedBeaconChainBody {
260    inner: Arc<Yoke<BeaconChainBody<'static>, SharedAlignedBuffer>>,
261}
262
263impl GenericOwnedBlockBody for OwnedBeaconChainBody {
264    const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
265
266    type Body<'a> = BeaconChainBody<'a>;
267
268    #[inline(always)]
269    fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
270        Self::from_buffer(buffer)
271    }
272
273    #[inline(always)]
274    fn buffer(&self) -> &SharedAlignedBuffer {
275        self.buffer()
276    }
277
278    #[inline(always)]
279    fn ref_count(&self) -> usize {
280        self.ref_count()
281    }
282
283    #[inline(always)]
284    fn body(&self) -> &Self::Body<'_> {
285        self.body()
286    }
287}
288
289impl OwnedBeaconChainBody {
290    // TODO: Verify order of headers and segments?
291    /// Create a new instance
292    pub fn new<'a, OS, ISB>(
293        own_segments: OS,
294        intermediate_shard_blocks: ISB,
295        pot_checkpoints: &[PotCheckpoints],
296    ) -> Result<Self, OwnedBeaconChainBodyError>
297    where
298        OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
299        // TODO: This is probably the wrong type to use here, especially because it doesn't have a
300        //  public constructor right now
301        ISB: TrustedLen<Item = IntermediateShardBlockInfo<'a>> + 'a,
302    {
303        let num_pot_checkpoints = pot_checkpoints.len();
304        let num_pot_checkpoints = u32::try_from(num_pot_checkpoints).map_err(|_error| {
305            OwnedBeaconChainBodyError::TooManyPotCheckpoints {
306                actual: num_pot_checkpoints,
307            }
308        })?;
309        let num_own_segment_roots = own_segments.size_hint().0;
310        let num_own_segment_roots = u8::try_from(num_own_segment_roots).map_err(|_error| {
311            OwnedBeaconChainBodyError::TooManyOwnSegmentRoots {
312                actual: num_own_segment_roots,
313            }
314        })?;
315        let num_blocks = intermediate_shard_blocks.size_hint().0;
316        let num_blocks = u16::try_from(num_blocks).map_err(|_error| {
317            OwnedBeaconChainBodyError::TooManyIntermediateShardBlocks { actual: num_blocks }
318        })?;
319
320        let mut buffer = OwnedAlignedBuffer::with_capacity(
321            u32::SIZE
322                + u8::SIZE
323                + if num_own_segment_roots > 0 { LocalSegmentIndex::SIZE } else { 0 }
324                + u32::from(num_own_segment_roots) * SegmentRoot::SIZE as u32
325                + u16::SIZE
326                // This is only an estimate to get in the ballpark where reallocation should not be
327                // necessary in many cases
328                + u32::from(num_blocks) * OwnedIntermediateShardHeader::max_allocation_for(&[]) * 2,
329        );
330
331        let true = buffer.append(&num_pot_checkpoints.to_le_bytes()) else {
332            unreachable!("Fixed size data structures that are guaranteed to fit; qed");
333        };
334
335        let true = buffer.append(&[num_own_segment_roots]) else {
336            unreachable!("Fixed size data structures that are guaranteed to fit; qed");
337        };
338        let mut own_segments = own_segments.peekable();
339        if let Some((first_local_segment_index, _own_segment_root)) = own_segments.peek() {
340            let true = buffer.append(first_local_segment_index.as_bytes()) else {
341                unreachable!("Checked size above; qed");
342            };
343        }
344        for (_segment_index, own_segment_root) in own_segments {
345            let true = buffer.append(own_segment_root.as_ref()) else {
346                unreachable!("Checked size above; qed");
347            };
348        }
349        // TODO: Would be nice for `IntermediateShardBlocksInfo` to have API to write this by itself
350        {
351            let true = buffer.append(&num_blocks.to_le_bytes()) else {
352                unreachable!("Fixed size data structures that are guaranteed to fit; qed");
353            };
354            let mut segments_roots_num_cursor = buffer.len() as usize;
355            for _ in 0..num_blocks {
356                // Placeholder values for the number of own segment roots and number of leaf shard
357                // blocks with segments
358                let true = buffer.append(&[0, 0]) else {
359                    unreachable!("Checked size above; qed");
360                };
361            }
362            let true = align_to_8_with_padding(&mut buffer) else {
363                unreachable!("Checked size above; qed");
364            };
365            for intermediate_shard_block in intermediate_shard_blocks {
366                let num_own_segment_roots = intermediate_shard_block
367                    .own_segments
368                    .as_ref()
369                    .map(|own_segments| own_segments.segment_roots.len())
370                    .unwrap_or_default();
371                let num_own_segment_roots =
372                    u8::try_from(num_own_segment_roots).map_err(|_error| {
373                        OwnedBeaconChainBodyError::TooManyIntermediateShardOwnSegmentRoots {
374                            actual: num_own_segment_roots,
375                        }
376                    })?;
377                let num_leaf_shard_blocks_with_segments = intermediate_shard_block
378                    .leaf_shards_segments()
379                    .size_hint()
380                    .0;
381                let num_leaf_shard_blocks_with_segments =
382                    u8::try_from(num_leaf_shard_blocks_with_segments).map_err(|_error| {
383                        OwnedBeaconChainBodyError::TooManyLeafShardBlocksWithSegments {
384                            actual: num_leaf_shard_blocks_with_segments,
385                        }
386                    })?;
387
388                buffer.as_mut_slice()[segments_roots_num_cursor..][..2]
389                    .copy_from_slice(&[num_own_segment_roots, num_leaf_shard_blocks_with_segments]);
390                segments_roots_num_cursor += 2;
391
392                OwnedIntermediateShardHeader::from_parts_into(
393                    intermediate_shard_block.header.prefix,
394                    intermediate_shard_block.header.result,
395                    intermediate_shard_block.header.consensus_info,
396                    intermediate_shard_block.header.beacon_chain_info(),
397                    intermediate_shard_block.header.child_shard_blocks(),
398                    &mut buffer,
399                )?;
400
401                if let Some(segments_proof) = &intermediate_shard_block.segments_proof
402                    && !buffer.append(*segments_proof)
403                {
404                    return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
405                }
406
407                if let Some(own_segments) = &intermediate_shard_block.own_segments {
408                    if !buffer.append(own_segments.first_local_segment_index.as_bytes()) {
409                        return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
410                    }
411                    if !buffer.append(
412                        SegmentRoot::repr_from_slice(own_segments.segment_roots).as_flattened(),
413                    ) {
414                        return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
415                    }
416                }
417
418                for (_shard_index, own_segments) in intermediate_shard_block.leaf_shards_segments()
419                {
420                    let num_own_segment_roots = own_segments.segment_roots.len();
421                    let num_own_segment_roots =
422                        u8::try_from(num_own_segment_roots).map_err(|_error| {
423                            OwnedBeaconChainBodyError::TooManyLeafShardBlockSegments {
424                                actual: num_own_segment_roots,
425                            }
426                        })?;
427                    if !buffer.append(&[num_own_segment_roots]) {
428                        return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
429                    }
430                }
431                for (shard_index, own_segments) in intermediate_shard_block.leaf_shards_segments() {
432                    if !buffer.append(shard_index.as_bytes()) {
433                        return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
434                    }
435                    if !buffer.append(own_segments.first_local_segment_index.as_bytes()) {
436                        return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
437                    }
438                    if !buffer.append(
439                        SegmentRoot::repr_from_slice(own_segments.segment_roots).as_flattened(),
440                    ) {
441                        return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
442                    }
443                }
444
445                if !align_to_8_with_padding(&mut buffer) {
446                    return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
447                }
448            }
449        }
450
451        let true = buffer.append(PotCheckpoints::bytes_from_slice(pot_checkpoints).as_flattened())
452        else {
453            return Err(OwnedBeaconChainBodyError::BlockBodyIsTooLarge);
454        };
455
456        // TODO: Avoid extra parsing here or at least go through unchecked version
457        Ok(Self::from_buffer(buffer.into_shared()).expect("Known to be created correctly; qed"))
458    }
459
460    /// Create an owned body from a buffer
461    #[inline]
462    pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
463        // TODO: Cloning is cheap, but will not be necessary if/when this is resolved:
464        //  https://github.com/unicode-org/icu4x/issues/6665
465        let inner = Yoke::try_attach_to_cart(buffer.clone(), |buffer| {
466            let Some((body, extra_bytes)) = BeaconChainBody::try_from_bytes(buffer) else {
467                return Err(());
468            };
469            if !extra_bytes.is_empty() {
470                return Err(());
471            }
472
473            Ok(body)
474        })
475        .map_err(move |()| buffer)?;
476
477        Ok(Self {
478            inner: Arc::new(inner),
479        })
480    }
481
482    /// Inner buffer with block body contents
483    #[inline(always)]
484    pub fn buffer(&self) -> &SharedAlignedBuffer {
485        self.inner.backing_cart()
486    }
487
488    /// Number of clones in memory
489    #[inline(always)]
490    pub fn ref_count(&self) -> usize {
491        Arc::count(&self.inner)
492    }
493
494    /// Get [`BeaconChainBody`] out of [`OwnedBeaconChainBody`]
495    #[inline(always)]
496    pub fn body(&self) -> &BeaconChainBody<'_> {
497        self.inner.get()
498    }
499}
500
501/// Errors for [`OwnedIntermediateShardBody`]
502#[derive(Debug, thiserror::Error)]
503pub enum OwnedIntermediateShardBodyError {
504    /// Too many own segment roots
505    #[error("Too many own segment roots: {actual}")]
506    TooManyOwnSegmentRoots {
507        /// Actual number of own segment roots
508        actual: usize,
509    },
510    /// Too many leaf shard blocks
511    #[error("Too many leaf shard blocks: {actual}")]
512    TooManyLeafShardBlocks {
513        /// Actual number of leaf shard blocks
514        actual: usize,
515    },
516    /// Too many leaf shard own segment roots
517    #[error("Too many leaf shard own segment roots: {actual}")]
518    TooManyLeafShardOwnSegmentRoots {
519        /// Actual number of own segment roots
520        actual: usize,
521    },
522}
523
524/// An owned version of [`IntermediateShardBody`].
525///
526/// It is correctly aligned in memory and well suited for sending and receiving over the network
527/// efficiently or storing in memory or on disk.
528#[derive(Debug, Clone)]
529pub struct OwnedIntermediateShardBody {
530    inner: Arc<Yoke<IntermediateShardBody<'static>, SharedAlignedBuffer>>,
531}
532
533impl GenericOwnedBlockBody for OwnedIntermediateShardBody {
534    const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
535
536    type Body<'a> = IntermediateShardBody<'a>;
537
538    #[inline(always)]
539    fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
540        Self::from_buffer(buffer)
541    }
542
543    #[inline(always)]
544    fn buffer(&self) -> &SharedAlignedBuffer {
545        self.buffer()
546    }
547
548    #[inline(always)]
549    fn ref_count(&self) -> usize {
550        self.ref_count()
551    }
552
553    #[inline(always)]
554    fn body(&self) -> &Self::Body<'_> {
555        self.body()
556    }
557}
558
559impl OwnedIntermediateShardBody {
560    // TODO: Verify order of headers and segments?
561    /// Create a new instance
562    pub fn new<'a, OS, LSB>(
563        own_segments: OS,
564        leaf_shard_blocks: LSB,
565    ) -> Result<Self, OwnedIntermediateShardBodyError>
566    where
567        OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
568        LSB: TrustedLen<Item = LeafShardBlockInfo<'a>> + 'a,
569    {
570        let num_own_segment_roots = own_segments.size_hint().0;
571        let num_own_segment_roots = u8::try_from(num_own_segment_roots).map_err(|_error| {
572            OwnedIntermediateShardBodyError::TooManyOwnSegmentRoots {
573                actual: num_own_segment_roots,
574            }
575        })?;
576        let num_blocks = leaf_shard_blocks.size_hint().0;
577        let num_blocks = u8::try_from(num_blocks).map_err(|_error| {
578            OwnedIntermediateShardBodyError::TooManyLeafShardBlocks { actual: num_blocks }
579        })?;
580
581        let mut buffer = OwnedAlignedBuffer::with_capacity(
582            u8::SIZE
583                + if num_own_segment_roots > 0 { LocalSegmentIndex::SIZE } else { 0 }
584                + u32::from(num_own_segment_roots) * SegmentRoot::SIZE as u32
585                // This is only an estimate to get in the ballpark where reallocation should not be
586                // necessary
587                + u32::from(num_blocks) * OwnedLeafShardHeader::MAX_ALLOCATION * 2,
588        );
589
590        let true = buffer.append(&[num_own_segment_roots]) else {
591            unreachable!("Fixed size data structures that are guaranteed to fit; qed");
592        };
593        let mut own_segments = own_segments.peekable();
594        if let Some((first_local_segment_index, _own_segment_root)) = own_segments.peek() {
595            let true = buffer.append(first_local_segment_index.as_bytes()) else {
596                unreachable!("Checked size above; qed");
597            };
598        }
599        for (_segment_index, own_segment_root) in own_segments {
600            let true = buffer.append(own_segment_root.as_ref()) else {
601                unreachable!("Checked size above; qed");
602            };
603        }
604        // TODO: Would be nice for `LeafShardBlocksInfo` to have API to write this by itself
605        {
606            let true = buffer.append(&num_blocks.to_le_bytes()) else {
607                unreachable!("Fixed size data structures that are guaranteed to fit; qed");
608            };
609            let mut own_segments_roots_num_cursor = buffer.len() as usize;
610            for _ in 0..num_blocks {
611                let true = buffer.append(&[0]) else {
612                    unreachable!("Checked size above; qed");
613                };
614            }
615            let true = align_to_8_with_padding(&mut buffer) else {
616                unreachable!("Checked size above; qed");
617            };
618            for leaf_shard_block in leaf_shard_blocks {
619                if let Some(segments) = &leaf_shard_block.segments {
620                    let num_own_segment_roots = segments.own_segments.segment_roots.len();
621                    let num_own_segment_roots =
622                        u8::try_from(num_own_segment_roots).map_err(|_error| {
623                            OwnedIntermediateShardBodyError::TooManyLeafShardOwnSegmentRoots {
624                                actual: num_own_segment_roots,
625                            }
626                        })?;
627                    buffer.as_mut_slice()[own_segments_roots_num_cursor] = num_own_segment_roots;
628                }
629                own_segments_roots_num_cursor += 1;
630
631                OwnedLeafShardHeader::from_parts_into(
632                    leaf_shard_block.header.prefix,
633                    leaf_shard_block.header.result,
634                    leaf_shard_block.header.consensus_info,
635                    leaf_shard_block.header.beacon_chain_info(),
636                    &mut buffer,
637                );
638                let true = align_to_8_with_padding(&mut buffer) else {
639                    unreachable!("Checked size above; qed");
640                };
641
642                if let Some(segments) = &leaf_shard_block.segments {
643                    let true =
644                        buffer.append(segments.own_segments.first_local_segment_index.as_bytes())
645                    else {
646                        unreachable!("Checked size above; qed");
647                    };
648                    let true = buffer.append(segments.segment_roots_proof) else {
649                        unreachable!("Checked size above; qed");
650                    };
651                    let true = buffer.append(
652                        SegmentRoot::repr_from_slice(segments.own_segments.segment_roots)
653                            .as_flattened(),
654                    ) else {
655                        unreachable!("Checked size above; qed");
656                    };
657                }
658            }
659        }
660
661        // TODO: Avoid extra parsing here or at least go through unchecked version
662        Ok(Self::from_buffer(buffer.into_shared()).expect("Known to be created correctly; qed"))
663    }
664
665    /// Create an owned body from a buffer
666    #[inline]
667    pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
668        // TODO: Cloning is cheap, but will not be necessary if/when this is resolved:
669        //  https://github.com/unicode-org/icu4x/issues/6665
670        let inner = Yoke::try_attach_to_cart(buffer.clone(), |buffer| {
671            let Some((body, extra_bytes)) = IntermediateShardBody::try_from_bytes(buffer) else {
672                return Err(());
673            };
674            if !extra_bytes.is_empty() {
675                return Err(());
676            }
677
678            Ok(body)
679        })
680        .map_err(move |()| buffer)?;
681
682        Ok(Self {
683            inner: Arc::new(inner),
684        })
685    }
686
687    /// Inner buffer with block body contents
688    #[inline(always)]
689    pub fn buffer(&self) -> &SharedAlignedBuffer {
690        self.inner.backing_cart()
691    }
692
693    /// Number of clones in memory
694    #[inline(always)]
695    pub fn ref_count(&self) -> usize {
696        Arc::count(&self.inner)
697    }
698
699    /// Get [`IntermediateShardBody`] out of [`OwnedIntermediateShardBody`]
700    #[inline(always)]
701    pub fn body(&self) -> &IntermediateShardBody<'_> {
702        self.inner.get()
703    }
704}
705
706/// Errors for [`OwnedLeafShardBody`]
707#[derive(Debug, thiserror::Error)]
708pub enum OwnedLeafShardBodyError {
709    /// Too many own segment roots
710    #[error("Too many own segment roots: {actual}")]
711    TooManyOwnSegmentRoots {
712        /// Actual number of own segment roots
713        actual: usize,
714    },
715    /// Block body is too large
716    #[error("Block body is too large")]
717    BlockBodyIsTooLarge,
718    /// Too many transactions
719    #[error("Too many transactions")]
720    TooManyTransactions,
721    /// Failed to add transaction
722    #[error("Failed to add transaction: {error}")]
723    FailedToAddTransaction {
724        /// Inner error
725        error: OwnedTransactionError,
726    },
727}
728
729impl From<AddTransactionError> for OwnedLeafShardBodyError {
730    fn from(value: AddTransactionError) -> Self {
731        match value {
732            AddTransactionError::BlockBodyIsTooLarge => {
733                OwnedLeafShardBodyError::BlockBodyIsTooLarge
734            }
735            AddTransactionError::TooManyTransactions => {
736                OwnedLeafShardBodyError::TooManyTransactions
737            }
738            AddTransactionError::FailedToAddTransaction { error } => {
739                OwnedLeafShardBodyError::FailedToAddTransaction { error }
740            }
741        }
742    }
743}
744
745/// An owned version of [`LeafShardBody`].
746///
747/// It is correctly aligned in memory and well suited for sending and receiving over the network
748/// efficiently or storing in memory or on disk.
749#[derive(Debug, Clone)]
750pub struct OwnedLeafShardBody {
751    inner: Arc<Yoke<LeafShardBody<'static>, SharedAlignedBuffer>>,
752}
753
754impl GenericOwnedBlockBody for OwnedLeafShardBody {
755    const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
756
757    type Body<'a> = LeafShardBody<'a>;
758
759    #[inline(always)]
760    fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
761        Self::from_buffer(buffer)
762    }
763
764    #[inline(always)]
765    fn buffer(&self) -> &SharedAlignedBuffer {
766        self.buffer()
767    }
768
769    #[inline(always)]
770    fn ref_count(&self) -> usize {
771        self.ref_count()
772    }
773
774    #[inline(always)]
775    fn body(&self) -> &Self::Body<'_> {
776        self.body()
777    }
778}
779
780impl OwnedLeafShardBody {
781    // TODO: Verify order of segments?
782    /// Initialize building of [`OwnedLeafShardBody`]
783    pub fn init<OS>(
784        own_segments: OS,
785    ) -> Result<OwnedLeafShardBlockBodyBuilder, OwnedLeafShardBodyError>
786    where
787        OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
788    {
789        let num_own_segment_roots = own_segments.size_hint().0;
790        let num_own_segment_roots = u8::try_from(num_own_segment_roots).map_err(|_error| {
791            OwnedLeafShardBodyError::TooManyOwnSegmentRoots {
792                actual: num_own_segment_roots,
793            }
794        })?;
795
796        let mut buffer = OwnedAlignedBuffer::with_capacity(
797            u8::SIZE
798                + if num_own_segment_roots > 0 {
799                    LocalSegmentIndex::SIZE
800                } else {
801                    0
802                }
803                + u32::from(num_own_segment_roots) * SegmentRoot::SIZE as u32,
804        );
805
806        let true = buffer.append(&[num_own_segment_roots]) else {
807            unreachable!("Fixed size data structures that are guaranteed to fit; qed");
808        };
809        let mut own_segments = own_segments.peekable();
810        if let Some((first_local_segment_index, _own_segment_root)) = own_segments.peek() {
811            let true = buffer.append(first_local_segment_index.as_bytes()) else {
812                unreachable!("Checked size above; qed");
813            };
814        }
815        for (_segment_index, own_segment_root) in own_segments {
816            let true = buffer.append(own_segment_root.as_ref()) else {
817                unreachable!("Checked size above; qed");
818            };
819        }
820
821        let num_transactions_offset = buffer.len() as usize;
822        let true = buffer.append(&0u32.to_le_bytes()) else {
823            unreachable!("Checked size above; qed");
824        };
825
826        Ok(OwnedLeafShardBlockBodyBuilder {
827            transaction_builder: TransactionBuilder::new(num_transactions_offset, buffer),
828        })
829    }
830
831    /// Create an owned body from a buffer
832    #[inline]
833    pub fn from_buffer(buffer: SharedAlignedBuffer) -> Result<Self, SharedAlignedBuffer> {
834        // TODO: Cloning is cheap, but will not be necessary if/when this is resolved:
835        //  https://github.com/unicode-org/icu4x/issues/6665
836        let inner = Yoke::try_attach_to_cart(buffer.clone(), |buffer| {
837            let Some((body, extra_bytes)) = LeafShardBody::try_from_bytes(buffer) else {
838                return Err(());
839            };
840            if !extra_bytes.is_empty() {
841                return Err(());
842            }
843
844            Ok(body)
845        })
846        .map_err(move |()| buffer)?;
847
848        Ok(Self {
849            inner: Arc::new(inner),
850        })
851    }
852
853    /// Inner buffer with block body contents
854    #[inline(always)]
855    pub fn buffer(&self) -> &SharedAlignedBuffer {
856        self.inner.backing_cart()
857    }
858
859    /// Number of clones in memory
860    #[inline(always)]
861    pub fn ref_count(&self) -> usize {
862        Arc::count(&self.inner)
863    }
864
865    /// Get [`LeafShardBody`] out of [`OwnedLeafShardBody`]
866    #[inline(always)]
867    pub fn body(&self) -> &LeafShardBody<'_> {
868        self.inner.get()
869    }
870}
871
872/// Builder for [`OwnedLeafShardBody`] that allows to add more transactions
873#[derive(Debug, Clone)]
874pub struct OwnedLeafShardBlockBodyBuilder {
875    transaction_builder: TransactionBuilder,
876}
877
878impl OwnedLeafShardBlockBodyBuilder {
879    /// Add transaction to the body
880    #[inline(always)]
881    pub fn add_transaction<T>(&mut self, transaction: T) -> Result<(), OwnedLeafShardBodyError>
882    where
883        T: WritableBodyTransaction,
884    {
885        self.transaction_builder.add_transaction(transaction)?;
886
887        Ok(())
888    }
889
890    /// Finish building block body
891    pub fn finish(self) -> OwnedLeafShardBody {
892        // TODO: Avoid extra parsing here or at least go through unchecked version
893        OwnedLeafShardBody::from_buffer(self.transaction_builder.finish().into_shared())
894            .expect("Known to be created correctly; qed")
895    }
896}
897
898/// An owned version of [`BlockBody`].
899///
900/// It is correctly aligned in memory and well suited for sending and receiving over the network
901/// efficiently or storing in memory or on disk.
902#[derive(Debug, Clone, From)]
903pub enum OwnedBlockBody {
904    /// Block body corresponds to the beacon chain
905    BeaconChain(OwnedBeaconChainBody),
906    /// Block body corresponds to an intermediate shard
907    IntermediateShard(OwnedIntermediateShardBody),
908    /// Block body corresponds to a leaf shard
909    LeafShard(OwnedLeafShardBody),
910}
911
912impl OwnedBlockBody {
913    /// Create an owned body from a buffer
914    #[inline]
915    pub fn from_buffer(
916        buffer: SharedAlignedBuffer,
917        shard_kind: RealShardKind,
918    ) -> Result<Self, SharedAlignedBuffer> {
919        Ok(match shard_kind {
920            RealShardKind::BeaconChain => {
921                Self::BeaconChain(OwnedBeaconChainBody::from_buffer(buffer)?)
922            }
923            RealShardKind::IntermediateShard => {
924                Self::IntermediateShard(OwnedIntermediateShardBody::from_buffer(buffer)?)
925            }
926            RealShardKind::LeafShard => Self::LeafShard(OwnedLeafShardBody::from_buffer(buffer)?),
927        })
928    }
929
930    /// Inner buffer block body contents
931    #[inline]
932    pub fn buffer(&self) -> &SharedAlignedBuffer {
933        match self {
934            Self::BeaconChain(owned_body) => owned_body.buffer(),
935            Self::IntermediateShard(owned_body) => owned_body.buffer(),
936            Self::LeafShard(owned_body) => owned_body.buffer(),
937        }
938    }
939
940    /// Number of clones in memory
941    #[inline(always)]
942    pub fn ref_count(&self) -> usize {
943        match self {
944            Self::BeaconChain(owned_body) => owned_body.ref_count(),
945            Self::IntermediateShard(owned_body) => owned_body.ref_count(),
946            Self::LeafShard(owned_body) => owned_body.ref_count(),
947        }
948    }
949
950    /// Get [`BlockBody`] out of [`OwnedBlockBody`]
951    #[inline]
952    pub fn body(&self) -> BlockBody<'_> {
953        match self {
954            Self::BeaconChain(owned_body) => BlockBody::BeaconChain(*owned_body.body()),
955            Self::IntermediateShard(owned_body) => BlockBody::IntermediateShard(*owned_body.body()),
956            Self::LeafShard(owned_body) => BlockBody::LeafShard(*owned_body.body()),
957        }
958    }
959}
960
961/// Aligns buffer to 8 bytes by adding necessary padding zero bytes.
962///
963/// Returns `false` if buffer becomes too long.
964#[inline(always)]
965#[must_use]
966fn align_to_8_with_padding(buffer: &mut OwnedAlignedBuffer) -> bool {
967    let alignment = align_of::<u64>();
968    let unaligned_by = buffer.len() as usize % alignment;
969    if unaligned_by > 0 {
970        // SAFETY: Subtracted value is always smaller than alignment
971        let padding_bytes = unsafe { alignment.unchecked_sub(unaligned_by) };
972
973        if !buffer.append(&0u64.to_le_bytes()[..padding_bytes]) {
974            return false;
975        }
976    }
977
978    true
979}
980
981/// Aligns buffer to 16 bytes by adding necessary padding zero bytes.
982///
983/// Returns `false` if buffer becomes too long.
984#[inline(always)]
985#[must_use]
986fn align_to_16_bytes_with_padding(buffer: &mut OwnedAlignedBuffer) -> bool {
987    let alignment = usize::from(Alignment::MAX.get());
988    let unaligned_by = buffer.len() as usize % alignment;
989    if unaligned_by > 0 {
990        // SAFETY: Subtracted value is always smaller than alignment
991        let padding_bytes = unsafe { alignment.unchecked_sub(unaligned_by) };
992
993        if !buffer.append(&0u128.to_le_bytes()[..padding_bytes]) {
994            return false;
995        }
996    }
997
998    true
999}