Skip to main content

ab_core_primitives/block/
owned.rs

1//! Data structures related to the owned version of [`Block`]
2
3// Slow under Miri and these features don't change what the tests exercise, so under Miri they only
4// run with just `alloc` or with all features (like when testing the whole workspace)
5#[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::owned::{
26    GenericOwnedBlockBody, OwnedBeaconChainBody, OwnedBeaconChainBodyError, OwnedBlockBody,
27    OwnedIntermediateShardBody, OwnedIntermediateShardBodyError, OwnedLeafShardBlockBodyBuilder,
28    OwnedLeafShardBody, OwnedLeafShardBodyError, WritableBodyTransaction,
29};
30use crate::block::body::{BlockBody, IntermediateShardBlockInfo, LeafShardBlockInfo};
31use crate::block::header::owned::{
32    GenericOwnedBlockHeader, OwnedBeaconChainHeader, OwnedBeaconChainHeaderUnsealed,
33    OwnedBlockHeader, OwnedIntermediateShardHeader, OwnedIntermediateShardHeaderUnsealed,
34    OwnedLeafShardHeader, OwnedLeafShardHeaderUnsealed,
35};
36use crate::block::header::{
37    BlockHeader, BlockHeaderBeaconChainInfo, BlockHeaderConsensusInfo,
38    BlockHeaderConsensusParameters, BlockHeaderConsistencyError, BlockHeaderPrefix,
39    BlockHeaderResult, BlockHeaderSeal,
40};
41use crate::block::{BeaconChainBlock, Block, GenericBlock, IntermediateShardBlock, LeafShardBlock};
42use crate::hashes::Blake3Hash;
43use crate::pot::PotCheckpoints;
44use crate::segments::{LocalSegmentIndex, SegmentRoot};
45use crate::shard::{RealShardKind, ShardIndex};
46use ab_aligned_buffer::SharedAlignedBuffer;
47use core::any::Any;
48use core::fmt;
49use core::iter::TrustedLen;
50use derive_more::From;
51
52/// Generic owned block.
53///
54/// Can't be implemented outside of this module, so that code generic over owned blocks can rely on
55/// the header corresponding to the body, like with concrete owned blocks.
56pub impl(self) trait GenericOwnedBlock:
57    Clone + fmt::Debug + Send + Sync + Into<OwnedBlock> + Any + 'static
58{
59    /// Shard kind
60    const SHARD_KIND: RealShardKind;
61
62    /// Block header type
63    type Header: GenericOwnedBlockHeader;
64    /// Block body type
65    type Body: GenericOwnedBlockBody;
66    /// Block
67    type Block<'a>: GenericBlock<'a>
68    where
69        Self: 'a;
70
71    /// Split into header and body
72    fn split(self) -> (Self::Header, Self::Body);
73
74    /// Block header
75    fn header(&self) -> &Self::Header;
76
77    /// Block body
78    fn body(&self) -> &Self::Body;
79
80    /// Create owned block from buffers
81    fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self>;
82
83    /// Get regular block out of the owned version
84    fn block(&self) -> Self::Block<'_>;
85}
86
87/// An owned version of [`BeaconChainBlock`].
88///
89/// It is correctly aligned in memory and well suited for sending and receiving over the network
90/// efficiently or storing in memory or on disk.
91///
92/// Fields can't be modified after creation, so that the header always corresponds to the body,
93/// which constructors ensure.
94#[derive(Debug, Clone)]
95pub struct OwnedBeaconChainBlock {
96    /// Block header
97    pub mut(self) header: OwnedBeaconChainHeader,
98    /// Block body
99    pub mut(self) body: OwnedBeaconChainBody,
100}
101
102impl GenericOwnedBlock for OwnedBeaconChainBlock {
103    const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
104
105    type Header = OwnedBeaconChainHeader;
106    type Body = OwnedBeaconChainBody;
107    type Block<'a> = BeaconChainBlock<'a>;
108
109    #[inline(always)]
110    fn split(self) -> (Self::Header, Self::Body) {
111        (self.header, self.body)
112    }
113
114    #[inline(always)]
115    fn header(&self) -> &Self::Header {
116        &self.header
117    }
118
119    #[inline(always)]
120    fn body(&self) -> &Self::Body {
121        &self.body
122    }
123
124    #[inline(always)]
125    fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self> {
126        Self::from_buffers(header, body)
127    }
128
129    #[inline(always)]
130    fn block(&self) -> Self::Block<'_> {
131        self.block()
132    }
133}
134
135impl OwnedBeaconChainBlock {
136    /// Initialize building of [`OwnedBeaconChainBlock`]
137    pub fn init<'a, OS, ISB>(
138        own_segments: OS,
139        intermediate_shard_blocks: ISB,
140        pot_checkpoints: &[PotCheckpoints],
141    ) -> Result<OwnedBeaconChainBlockBuilder, OwnedBeaconChainBodyError>
142    where
143        OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
144        ISB: TrustedLen<Item = IntermediateShardBlockInfo<'a>> + Clone + 'a,
145    {
146        Ok(OwnedBeaconChainBlockBuilder {
147            body: OwnedBeaconChainBody::new(
148                own_segments,
149                intermediate_shard_blocks,
150                pot_checkpoints,
151            )?,
152        })
153    }
154
155    /// Create owned block from buffers
156    #[inline]
157    pub fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self> {
158        let block = Self {
159            header: OwnedBeaconChainHeader::from_buffer(header).ok()?,
160            body: OwnedBeaconChainBody::from_buffer(body).ok()?,
161        };
162
163        if !block.block().is_internally_consistent() {
164            return None;
165        }
166
167        Some(block)
168    }
169
170    /// Create an owned version of a borrowed block, whose header corresponds to the body: borrowed
171    /// blocks are only created by the parser with all checks or out of owned blocks
172    #[inline(always)]
173    pub(super) fn from_block(block: BeaconChainBlock<'_>) -> Self {
174        Self {
175            header: block.header.to_owned(),
176            body: block.body.to_owned(),
177        }
178    }
179
180    /// Get [`BeaconChainBlock`] out of [`OwnedBeaconChainBlock`]
181    pub fn block(&self) -> BeaconChainBlock<'_> {
182        BeaconChainBlock {
183            header: self.header.header().clone(),
184            body: *self.body.body(),
185        }
186    }
187}
188
189/// Builder for [`OwnedBeaconChainBlock`]
190#[derive(Debug, Clone)]
191pub struct OwnedBeaconChainBlockBuilder {
192    body: OwnedBeaconChainBody,
193}
194
195impl OwnedBeaconChainBlockBuilder {
196    /// Add header.
197    ///
198    /// Returns an error if the header parts are not consistent with each other or with the shard
199    /// kind.
200    pub fn with_header(
201        self,
202        prefix: &BlockHeaderPrefix,
203        state_root: Blake3Hash,
204        consensus_info: &BlockHeaderConsensusInfo,
205        consensus_parameters: &BlockHeaderConsensusParameters<'_>,
206    ) -> Result<OwnedBeaconChainBlockUnsealed, BlockHeaderConsistencyError> {
207        let body = self.body;
208        let header = OwnedBeaconChainHeader::from_parts(
209            prefix,
210            &BlockHeaderResult {
211                body_root: body.body().root(),
212                state_root,
213            },
214            consensus_info,
215            &body.body().intermediate_shard_blocks().headers_root(),
216            consensus_parameters,
217        )?;
218
219        Ok(OwnedBeaconChainBlockUnsealed { body, header })
220    }
221}
222
223/// Owned beacon chain block header, which is not sealed yet
224#[derive(Debug, Clone)]
225pub struct OwnedBeaconChainBlockUnsealed {
226    body: OwnedBeaconChainBody,
227    header: OwnedBeaconChainHeaderUnsealed,
228}
229
230impl OwnedBeaconChainBlockUnsealed {
231    /// Hash of the block before seal is applied to it
232    #[inline(always)]
233    pub fn pre_seal_hash(&self) -> Blake3Hash {
234        self.header.pre_seal_hash()
235    }
236
237    /// Add seal and return [`OwnedBeaconChainBlock`].
238    ///
239    /// Returns an error if the seal doesn't correspond to the public key hash of the solution.
240    pub fn with_seal(
241        self,
242        seal: BlockHeaderSeal<'_>,
243    ) -> Result<OwnedBeaconChainBlock, BlockHeaderConsistencyError> {
244        let header = self.header.with_seal(seal)?;
245
246        Ok(OwnedBeaconChainBlock {
247            header,
248            body: self.body,
249        })
250    }
251}
252
253/// An owned version of [`IntermediateShardBlock`].
254///
255/// It is correctly aligned in memory and well suited for sending and receiving over the network
256/// efficiently or storing in memory or on disk.
257///
258/// Fields can't be modified after creation, so that the header always corresponds to the body,
259/// which constructors ensure.
260#[derive(Debug, Clone)]
261pub struct OwnedIntermediateShardBlock {
262    /// Block header
263    pub mut(self) header: OwnedIntermediateShardHeader,
264    /// Block body
265    pub mut(self) body: OwnedIntermediateShardBody,
266}
267
268impl GenericOwnedBlock for OwnedIntermediateShardBlock {
269    const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
270
271    type Header = OwnedIntermediateShardHeader;
272    type Body = OwnedIntermediateShardBody;
273    type Block<'a> = IntermediateShardBlock<'a>;
274
275    #[inline(always)]
276    fn split(self) -> (Self::Header, Self::Body) {
277        (self.header, self.body)
278    }
279
280    #[inline(always)]
281    fn header(&self) -> &Self::Header {
282        &self.header
283    }
284
285    #[inline(always)]
286    fn body(&self) -> &Self::Body {
287        &self.body
288    }
289
290    #[inline(always)]
291    fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self> {
292        Self::from_buffers(header, body)
293    }
294
295    #[inline(always)]
296    fn block(&self) -> Self::Block<'_> {
297        self.block()
298    }
299}
300
301impl OwnedIntermediateShardBlock {
302    /// Initialize building of [`OwnedIntermediateShardBlock`]
303    pub fn init<'a, OS, LSB>(
304        own_segments: OS,
305        leaf_shard_blocks: LSB,
306    ) -> Result<OwnedIntermediateShardBlockBuilder, OwnedIntermediateShardBodyError>
307    where
308        OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
309        LSB: TrustedLen<Item = LeafShardBlockInfo<'a>> + Clone + 'a,
310    {
311        Ok(OwnedIntermediateShardBlockBuilder {
312            body: OwnedIntermediateShardBody::new(own_segments, leaf_shard_blocks)?,
313        })
314    }
315
316    /// Create owned block from buffers
317    #[inline]
318    pub fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self> {
319        let block = Self {
320            header: OwnedIntermediateShardHeader::from_buffer(header).ok()?,
321            body: OwnedIntermediateShardBody::from_buffer(body).ok()?,
322        };
323
324        if !block.block().is_internally_consistent() {
325            return None;
326        }
327
328        Some(block)
329    }
330
331    /// Create an owned version of a borrowed block, whose header corresponds to the body: borrowed
332    /// blocks are only created by the parser with all checks or out of owned blocks
333    #[inline(always)]
334    pub(super) fn from_block(block: IntermediateShardBlock<'_>) -> Self {
335        Self {
336            header: block.header.to_owned(),
337            body: block.body.to_owned(),
338        }
339    }
340
341    /// Get [`IntermediateShardBlock`] out of [`OwnedIntermediateShardBlock`]
342    pub fn block(&self) -> IntermediateShardBlock<'_> {
343        IntermediateShardBlock {
344            header: self.header.header().clone(),
345            body: *self.body.body(),
346        }
347    }
348}
349
350/// Errors for [`OwnedIntermediateShardBlockBuilder`]
351#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)]
352pub enum OwnedIntermediateShardBlockBuilderError {
353    /// Header parts are not consistent with each other or with the shard kind
354    #[error("Invalid header parts: {error}")]
355    InvalidHeaderParts {
356        /// Block header consistency error
357        #[from]
358        error: BlockHeaderConsistencyError,
359    },
360    /// Leaf shard block is not a child of the shard of the header
361    #[error("Leaf shard block of shard {leaf_shard_index} is not a child of shard {shard_index}")]
362    LeafShardBlockOfAnotherShard {
363        /// Shard index of the header
364        shard_index: ShardIndex,
365        /// Shard index of the leaf shard block
366        leaf_shard_index: ShardIndex,
367    },
368}
369
370/// Builder for [`OwnedIntermediateShardBlock`]
371#[derive(Debug, Clone)]
372pub struct OwnedIntermediateShardBlockBuilder {
373    body: OwnedIntermediateShardBody,
374}
375
376impl OwnedIntermediateShardBlockBuilder {
377    /// Add header.
378    ///
379    /// Returns an error if the header parts are not consistent with each other or with the shard
380    /// kind, or if a leaf shard block is not a child of the shard of the header.
381    pub fn with_header(
382        self,
383        prefix: &BlockHeaderPrefix,
384        state_root: Blake3Hash,
385        consensus_info: &BlockHeaderConsensusInfo,
386        beacon_chain_info: &BlockHeaderBeaconChainInfo,
387    ) -> Result<OwnedIntermediateShardBlockUnsealed, OwnedIntermediateShardBlockBuilderError> {
388        let body = self.body;
389        let header = OwnedIntermediateShardHeader::from_parts(
390            prefix,
391            &BlockHeaderResult {
392                body_root: body.body().root(),
393                state_root,
394            },
395            consensus_info,
396            beacon_chain_info,
397            &body.body().leaf_shard_blocks().headers_root(),
398        )?;
399
400        if let Some(leaf_shard_index) =
401            IntermediateShardBlock::find_leaf_shard_block_of_another_shard(
402                body.body().leaf_shard_blocks(),
403                prefix.shard_index,
404            )
405        {
406            return Err(
407                OwnedIntermediateShardBlockBuilderError::LeafShardBlockOfAnotherShard {
408                    shard_index: prefix.shard_index,
409                    leaf_shard_index,
410                },
411            );
412        }
413
414        Ok(OwnedIntermediateShardBlockUnsealed { body, header })
415    }
416}
417
418/// Owned intermediate shard block header, which is not sealed yet
419#[derive(Debug, Clone)]
420pub struct OwnedIntermediateShardBlockUnsealed {
421    body: OwnedIntermediateShardBody,
422    header: OwnedIntermediateShardHeaderUnsealed,
423}
424
425impl OwnedIntermediateShardBlockUnsealed {
426    /// Hash of the block before seal is applied to it
427    #[inline(always)]
428    pub fn pre_seal_hash(&self) -> Blake3Hash {
429        self.header.pre_seal_hash()
430    }
431
432    /// Add seal and return [`OwnedIntermediateShardBlock`].
433    ///
434    /// Returns an error if the seal doesn't correspond to the public key hash of the solution.
435    pub fn with_seal(
436        self,
437        seal: BlockHeaderSeal<'_>,
438    ) -> Result<OwnedIntermediateShardBlock, BlockHeaderConsistencyError> {
439        let header = self.header.with_seal(seal)?;
440
441        Ok(OwnedIntermediateShardBlock {
442            header,
443            body: self.body,
444        })
445    }
446}
447
448/// An owned version of [`LeafShardBlock`].
449///
450/// It is correctly aligned in memory and well suited for sending and receiving over the network
451/// efficiently or storing in memory or on disk.
452///
453/// Fields can't be modified after creation, so that the header always corresponds to the body,
454/// which constructors ensure.
455#[derive(Debug, Clone)]
456pub struct OwnedLeafShardBlock {
457    /// Block header
458    pub mut(self) header: OwnedLeafShardHeader,
459    /// Block body
460    pub mut(self) body: OwnedLeafShardBody,
461}
462
463impl GenericOwnedBlock for OwnedLeafShardBlock {
464    const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
465
466    type Header = OwnedLeafShardHeader;
467    type Body = OwnedLeafShardBody;
468    type Block<'a> = LeafShardBlock<'a>;
469
470    #[inline(always)]
471    fn split(self) -> (Self::Header, Self::Body) {
472        (self.header, self.body)
473    }
474
475    #[inline(always)]
476    fn header(&self) -> &Self::Header {
477        &self.header
478    }
479
480    #[inline(always)]
481    fn body(&self) -> &Self::Body {
482        &self.body
483    }
484
485    #[inline(always)]
486    fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self> {
487        Self::from_buffers(header, body)
488    }
489
490    #[inline(always)]
491    fn block(&self) -> Self::Block<'_> {
492        self.block()
493    }
494}
495
496impl OwnedLeafShardBlock {
497    /// Initialize building of [`OwnedLeafShardBlock`]
498    pub fn init<OS>(own_segments: OS) -> Result<OwnedLeafShardBlockBuilder, OwnedLeafShardBodyError>
499    where
500        OS: TrustedLen<Item = (LocalSegmentIndex, SegmentRoot)>,
501    {
502        Ok(OwnedLeafShardBlockBuilder {
503            body_builder: OwnedLeafShardBody::init(own_segments)?,
504        })
505    }
506
507    /// Create owned block from buffers
508    #[inline]
509    pub fn from_buffers(header: SharedAlignedBuffer, body: SharedAlignedBuffer) -> Option<Self> {
510        let block = Self {
511            header: OwnedLeafShardHeader::from_buffer(header).ok()?,
512            body: OwnedLeafShardBody::from_buffer(body).ok()?,
513        };
514
515        if !block.block().is_internally_consistent() {
516            return None;
517        }
518
519        Some(block)
520    }
521
522    /// Create an owned version of a borrowed block, whose header corresponds to the body: borrowed
523    /// blocks are only created by the parser with all checks or out of owned blocks
524    #[inline(always)]
525    pub(super) fn from_block(block: LeafShardBlock<'_>) -> Self {
526        Self {
527            header: block.header.to_owned(),
528            body: block.body.to_owned(),
529        }
530    }
531
532    /// Get [`LeafShardBlock`] out of [`OwnedLeafShardBlock`]
533    pub fn block(&self) -> LeafShardBlock<'_> {
534        LeafShardBlock {
535            header: self.header.header().clone(),
536            body: *self.body.body(),
537        }
538    }
539}
540
541/// Builder for [`OwnedLeafShardBlock`]
542#[derive(Debug, Clone)]
543pub struct OwnedLeafShardBlockBuilder {
544    body_builder: OwnedLeafShardBlockBodyBuilder,
545}
546
547impl OwnedLeafShardBlockBuilder {
548    /// Add transaction to the body
549    #[inline(always)]
550    pub fn add_transaction<T>(&mut self, transaction: T) -> Result<(), OwnedLeafShardBodyError>
551    where
552        T: WritableBodyTransaction,
553    {
554        self.body_builder.add_transaction(transaction)?;
555
556        Ok(())
557    }
558
559    /// Add header.
560    ///
561    /// Returns an error if the header parts are not consistent with each other or with the shard
562    /// kind.
563    pub fn with_header(
564        self,
565        prefix: &BlockHeaderPrefix,
566        state_root: Blake3Hash,
567        consensus_info: &BlockHeaderConsensusInfo,
568        beacon_chain_info: &BlockHeaderBeaconChainInfo,
569    ) -> Result<OwnedLeafShardBlockUnsealed, BlockHeaderConsistencyError> {
570        let body = self.body_builder.finish();
571        let header = OwnedLeafShardHeader::from_parts(
572            prefix,
573            &BlockHeaderResult {
574                body_root: body.body().root(),
575                state_root,
576            },
577            consensus_info,
578            beacon_chain_info,
579        )?;
580        Ok(OwnedLeafShardBlockUnsealed { body, header })
581    }
582}
583
584/// Owned leaf shard block header, which is not sealed yet
585#[derive(Debug, Clone)]
586pub struct OwnedLeafShardBlockUnsealed {
587    body: OwnedLeafShardBody,
588    header: OwnedLeafShardHeaderUnsealed,
589}
590
591impl OwnedLeafShardBlockUnsealed {
592    /// Hash of the block before seal is applied to it
593    #[inline(always)]
594    pub fn pre_seal_hash(&self) -> Blake3Hash {
595        self.header.pre_seal_hash()
596    }
597
598    /// Add seal and return [`OwnedLeafShardBlock`].
599    ///
600    /// Returns an error if the seal doesn't correspond to the public key hash of the solution.
601    pub fn with_seal(
602        self,
603        seal: BlockHeaderSeal<'_>,
604    ) -> Result<OwnedLeafShardBlock, BlockHeaderConsistencyError> {
605        let header = self.header.with_seal(seal)?;
606
607        Ok(OwnedLeafShardBlock {
608            header,
609            body: self.body,
610        })
611    }
612}
613
614// TODO: A variant that holds both header and body in the same allocation?
615/// An owned version of [`Block`].
616///
617/// It is correctly aligned in memory and well suited for sending and receiving over the network
618/// efficiently or storing in memory or on disk.
619#[derive(Debug, Clone, From)]
620pub enum OwnedBlock {
621    /// Block corresponds to the beacon chain
622    BeaconChain(OwnedBeaconChainBlock),
623    /// Block corresponds to an intermediate shard
624    IntermediateShard(OwnedIntermediateShardBlock),
625    /// Block corresponds to a leaf shard
626    LeafShard(OwnedLeafShardBlock),
627}
628
629impl OwnedBlock {
630    /// Split into header and body
631    #[inline]
632    pub fn split(self) -> (OwnedBlockHeader, OwnedBlockBody) {
633        match self {
634            Self::BeaconChain(block) => {
635                let (header, body) = block.split();
636                (header.into(), body.into())
637            }
638            Self::IntermediateShard(block) => {
639                let (header, body) = block.split();
640                (header.into(), body.into())
641            }
642            Self::LeafShard(block) => {
643                let (header, body) = block.split();
644                (header.into(), body.into())
645            }
646        }
647    }
648
649    /// Block header
650    #[inline(always)]
651    pub fn header(&self) -> BlockHeader<'_> {
652        match self {
653            Self::BeaconChain(block) => BlockHeader::BeaconChain(block.header.header().clone()),
654            Self::IntermediateShard(block) => {
655                BlockHeader::IntermediateShard(block.header.header().clone())
656            }
657            Self::LeafShard(block) => BlockHeader::LeafShard(block.header.header().clone()),
658        }
659    }
660
661    /// Block body
662    #[inline(always)]
663    pub fn body(&self) -> BlockBody<'_> {
664        match self {
665            Self::BeaconChain(block) => BlockBody::BeaconChain(*block.body.body()),
666            Self::IntermediateShard(block) => BlockBody::IntermediateShard(*block.body.body()),
667            Self::LeafShard(block) => BlockBody::LeafShard(*block.body.body()),
668        }
669    }
670
671    /// Create owned block from buffers
672    #[inline]
673    pub fn from_buffers(
674        header: SharedAlignedBuffer,
675        body: SharedAlignedBuffer,
676        shard_kind: RealShardKind,
677    ) -> Option<Self> {
678        Some(match shard_kind {
679            RealShardKind::BeaconChain => {
680                Self::BeaconChain(OwnedBeaconChainBlock::from_buffers(header, body)?)
681            }
682            RealShardKind::IntermediateShard => {
683                Self::IntermediateShard(OwnedIntermediateShardBlock::from_buffers(header, body)?)
684            }
685            RealShardKind::LeafShard => {
686                Self::LeafShard(OwnedLeafShardBlock::from_buffers(header, body)?)
687            }
688        })
689    }
690
691    /// Get block
692    #[inline(always)]
693    pub fn block(&self) -> Block<'_> {
694        match self {
695            Self::BeaconChain(block) => Block::BeaconChain(block.block()),
696            Self::IntermediateShard(block) => Block::IntermediateShard(block.block()),
697            Self::LeafShard(block) => Block::LeafShard(block.block()),
698        }
699    }
700}