Skip to main content

ab_core_primitives/block/
header.rs

1//! Block header primitives
2
3#[cfg(feature = "alloc")]
4pub mod owned;
5
6#[cfg(feature = "alloc")]
7use crate::block::header::owned::{
8    GenericOwnedBlockHeader, OwnedBeaconChainHeader, OwnedBlockHeader,
9    OwnedIntermediateShardHeader, OwnedLeafShardHeader,
10};
11use crate::block::{BlockNumber, BlockRoot, BlockTimestamp};
12use crate::ed25519::{Ed25519PublicKey, Ed25519Signature};
13use crate::hashes::Blake3Hash;
14use crate::pot::{PotOutput, PotParametersChange, SlotNumber};
15use crate::segments::SuperSegmentRoot;
16use crate::shard::{NumShards, NumShardsUnchecked, RealShardKind, ShardIndex, ShardKind};
17use crate::solutions::{Solution, SolutionRange};
18use ab_blake3::{BLOCK_LEN, single_block_hash, single_chunk_hash};
19use ab_io_type::trivial_type::TrivialType;
20use ab_merkle_tree::unbalanced::UnbalancedMerkleTree;
21use blake3::CHUNK_LEN;
22use core::num::NonZeroU32;
23use core::ops::Deref;
24use core::{fmt, slice};
25use derive_more::{Deref, From};
26#[cfg(feature = "scale-codec")]
27use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
28#[cfg(feature = "serde")]
29use serde::{Deserialize, Serialize};
30use yoke::Yokeable;
31
32/// Generic block header
33pub trait GenericBlockHeader<'a>
34where
35    Self: Clone
36        + fmt::Debug
37        + Deref<Target = SharedBlockHeader<'a>>
38        + Into<BlockHeader<'a>>
39        + Send
40        + Sync,
41{
42    /// Shard kind
43    const SHARD_KIND: RealShardKind;
44
45    /// Owned block header
46    #[cfg(feature = "alloc")]
47    type Owned: GenericOwnedBlockHeader<Header<'a> = Self>
48    where
49        Self: 'a;
50
51    /// Turn into an owned version
52    #[cfg(feature = "alloc")]
53    fn to_owned(self) -> Self::Owned;
54
55    /// Compute block root out of this header.
56    ///
57    /// Block root is a Merkle Tree Root. The leaves are derived from individual fields in
58    /// [`SharedBlockHeader`] and other fields of this enum in the declaration order.
59    ///
60    /// Note that this method does a bunch of hashing and if root is often needed, should be cached.
61    fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync;
62
63    /// Hash of the block before seal is applied to it
64    fn pre_seal_hash(&self) -> Blake3Hash;
65}
66
67/// Block header prefix.
68///
69/// The prefix contains generic information known about the block before block creation starts.
70#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
71#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
74#[repr(C)]
75pub struct BlockHeaderPrefix {
76    /// Block number
77    pub number: BlockNumber,
78    /// Shard index
79    pub shard_index: ShardIndex,
80    /// Padding for data structure alignment, contents must be all zeroes
81    pub padding_0: [u8; 4],
82    /// Block timestamp
83    pub timestamp: BlockTimestamp,
84    /// Root of the parent block
85    pub parent_root: BlockRoot,
86    /// MMR root of all block roots, including `parent_root`
87    // TODO: New type?
88    pub mmr_root: Blake3Hash,
89}
90
91impl BlockHeaderPrefix {
92    /// Hash of the block header prefix, part of the eventual block root
93    pub fn hash(&self) -> Blake3Hash {
94        const {
95            assert!(size_of::<Self>() <= CHUNK_LEN);
96        }
97        // TODO: Keyed hash
98        Blake3Hash::new(
99            single_chunk_hash(self.as_bytes())
100                .expect("Less than a single chunk worth of bytes; qed"),
101        )
102    }
103}
104
105/// Consensus information in the block header
106#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
107#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
108#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
109#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
110#[repr(C)]
111pub struct BlockHeaderConsensusInfo {
112    /// Slot number
113    pub slot: SlotNumber,
114    /// Proof of time for this slot
115    pub proof_of_time: PotOutput,
116    /// Future proof of time
117    pub future_proof_of_time: PotOutput,
118    /// Solution
119    pub solution: Solution,
120}
121
122impl BlockHeaderConsensusInfo {
123    /// Hash of the consensus info, part of the eventual block root
124    pub fn hash(&self) -> Blake3Hash {
125        // TODO: Keyed hash
126        Blake3Hash::from(blake3::hash(self.as_bytes()))
127    }
128}
129
130/// Beacon chain info
131#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, TrivialType)]
132#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
135#[repr(C)]
136pub struct BlockHeaderBeaconChainInfo {
137    /// Beacon chain block number
138    pub number: BlockNumber,
139    /// Beacon chain block root
140    pub root: BlockRoot,
141}
142
143impl BlockHeaderBeaconChainInfo {
144    /// Hash of the beacon chain info, part of the eventual block root
145    pub fn hash(&self) -> Blake3Hash {
146        const {
147            assert!(size_of::<Self>() <= BLOCK_LEN);
148        }
149        // TODO: Keyed hash
150        Blake3Hash::new(
151            single_block_hash(self.as_bytes())
152                .expect("Less than a single block worth of bytes; qed"),
153        )
154    }
155}
156
157/// Consensus parameters (on the beacon chain)
158#[derive(Debug, Copy, Clone, Eq, PartialEq)]
159#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
160#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
161#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
162pub struct BlockHeaderFixedConsensusParameters {
163    /// Solution range for this block/interval
164    pub solution_range: SolutionRange,
165    /// The number of iterations for proof of time per slot.
166    ///
167    /// Corresponds to the slot that is right after the parent block's slot.
168    /// It can change before the slot of this block (see [`PotParametersChange`]).
169    pub slot_iterations: NonZeroU32,
170    /// Number of shards in the network
171    pub num_shards: NumShards,
172}
173
174impl BlockHeaderFixedConsensusParameters {
175    /// Create an instance from provided bytes.
176    ///
177    /// `bytes` do not need to be aligned.
178    ///
179    /// Returns an instance and remaining bytes on success.
180    #[inline]
181    pub fn try_from_bytes(mut bytes: &[u8]) -> Option<(Self, &[u8])> {
182        // Layout here is as follows:
183        // * solution range: SolutionRange as unaligned bytes
184        // * PoT slot iterations: NonZeroU32 as unaligned little-endian bytes
185        // * number of shards: NumShards as unaligned little-endian bytes
186
187        let solution_range = bytes.split_off(..size_of::<SolutionRange>())?;
188        let solution_range = SolutionRange::from_bytes([
189            solution_range[0],
190            solution_range[1],
191            solution_range[2],
192            solution_range[3],
193            solution_range[4],
194            solution_range[5],
195            solution_range[6],
196            solution_range[7],
197        ]);
198
199        let pot_slot_iterations = bytes.split_off(..size_of::<u32>())?;
200        let slot_iterations = u32::from_le_bytes([
201            pot_slot_iterations[0],
202            pot_slot_iterations[1],
203            pot_slot_iterations[2],
204            pot_slot_iterations[3],
205        ]);
206        let slot_iterations = NonZeroU32::new(slot_iterations)?;
207        // SAFETY: All bit patterns are valid
208        let num_shards = unsafe {
209            bytes
210                .split_off(..size_of::<NumShardsUnchecked>())?
211                .as_ptr()
212                .cast::<NumShardsUnchecked>()
213                .read_unaligned()
214        };
215        let num_shards = NumShards::try_from(num_shards).ok()?;
216
217        Some((
218            Self {
219                solution_range,
220                slot_iterations,
221                num_shards,
222            },
223            bytes,
224        ))
225    }
226}
227
228/// A mirror of [`PotParametersChange`] for block header purposes.
229///
230/// Use [`From`] or [`Into`] for converting into [`PotParametersChange`] before use.
231#[derive(Debug, Copy, Clone, Eq, PartialEq)]
232#[repr(C, packed)]
233pub struct BlockHeaderPotParametersChange {
234    // TODO: Reduce this to `u16` or even `u8` since it is always an offset relatively to current
235    //  block's slot number
236    /// At which slot change of parameters takes effect
237    slot: SlotNumber,
238    /// New number of slot iterations
239    slot_iterations: NonZeroU32,
240    /// Entropy that should be injected at this time
241    entropy: Blake3Hash,
242}
243
244impl From<BlockHeaderPotParametersChange> for PotParametersChange {
245    #[inline(always)]
246    fn from(value: BlockHeaderPotParametersChange) -> Self {
247        let BlockHeaderPotParametersChange {
248            slot,
249            slot_iterations,
250            entropy,
251        } = value;
252
253        PotParametersChange {
254            slot,
255            slot_iterations,
256            entropy,
257        }
258    }
259}
260
261impl From<PotParametersChange> for BlockHeaderPotParametersChange {
262    #[inline(always)]
263    fn from(value: PotParametersChange) -> Self {
264        let PotParametersChange {
265            slot,
266            slot_iterations,
267            entropy,
268        } = value;
269
270        BlockHeaderPotParametersChange {
271            slot,
272            slot_iterations,
273            entropy,
274        }
275    }
276}
277
278impl BlockHeaderPotParametersChange {
279    /// Get instance reference from provided bytes.
280    ///
281    /// `bytes` do not need to be aligned.
282    ///
283    /// Returns an instance and remaining bytes on success.
284    #[inline]
285    pub fn try_from_bytes(mut bytes: &[u8]) -> Option<(&Self, &[u8])> {
286        // Layout here is as follows:
287        // * slot number: SlotNumber as unaligned bytes
288        // * slot iterations: NonZeroU32 as unaligned little-endian bytes
289        // * entropy: Blake3Hash
290
291        let pot_parameters_change_ptr = bytes.as_ptr().cast::<Self>();
292
293        let _slot = bytes.split_off(..size_of::<SlotNumber>())?;
294
295        let slot_iterations = bytes.split_off(..size_of::<u32>())?;
296        if slot_iterations == [0, 0, 0, 0] {
297            return None;
298        }
299        let _entropy = bytes.split_off(..size_of::<Blake3Hash>())?;
300
301        // SAFETY: Not null, packed, bit pattern for `NonZeroU32` checked above
302        let pot_parameters_change = unsafe { pot_parameters_change_ptr.as_ref_unchecked() };
303
304        Some((pot_parameters_change, bytes))
305    }
306}
307
308/// Owned version of [`BlockHeaderConsensusParameters`]
309#[derive(Debug, Copy, Clone)]
310pub struct OwnedBlockHeaderConsensusParameters {
311    /// Consensus parameters that are always present
312    pub fixed_parameters: BlockHeaderFixedConsensusParameters,
313    /// Super segment root
314    pub super_segment_root: Option<SuperSegmentRoot>,
315    /// Solution range for the next block/interval (if any)
316    pub next_solution_range: Option<SolutionRange>,
317    /// Change of parameters to apply to the proof of time chain (if any)
318    pub pot_parameters_change: Option<BlockHeaderPotParametersChange>,
319}
320
321impl OwnedBlockHeaderConsensusParameters {
322    /// Get a reference out of the owned version
323    #[inline]
324    pub fn as_ref(&self) -> BlockHeaderConsensusParameters<'_> {
325        BlockHeaderConsensusParameters {
326            fixed_parameters: self.fixed_parameters,
327            super_segment_root: self.super_segment_root.as_ref(),
328            next_solution_range: self.next_solution_range,
329            pot_parameters_change: self.pot_parameters_change.as_ref(),
330        }
331    }
332}
333
334/// Consensus parameters (on the beacon chain)
335#[derive(Debug, Copy, Clone, Eq, PartialEq)]
336pub struct BlockHeaderConsensusParameters<'a> {
337    /// Consensus parameters that are always present
338    pub fixed_parameters: BlockHeaderFixedConsensusParameters,
339    /// Super segment root
340    pub super_segment_root: Option<&'a SuperSegmentRoot>,
341    /// Solution range for the next block/interval (if any)
342    pub next_solution_range: Option<SolutionRange>,
343    /// Change of parameters to apply to the proof of time chain (if any)
344    pub pot_parameters_change: Option<&'a BlockHeaderPotParametersChange>,
345}
346
347impl<'a> BlockHeaderConsensusParameters<'a> {
348    /// Max size of the allocation necessary for this data structure
349    pub const MAX_SIZE: u32 = size_of::<BlockHeaderFixedConsensusParameters>() as u32
350        + u8::SIZE
351        + <SuperSegmentRoot as TrivialType>::SIZE
352        + <SolutionRange as TrivialType>::SIZE
353        + <NumShardsUnchecked as TrivialType>::SIZE
354        + size_of::<BlockHeaderPotParametersChange>() as u32;
355    /// Bitmask for presence of `super_segment_root` field
356    pub const SUPER_SEGMENT_ROOT_MASK: u8 = 0b0000_0001;
357    /// Bitmask for presence of `next_solution_range` field
358    pub const NEXT_SOLUTION_RANGE_MASK: u8 = 0b0000_0010;
359    /// Bitmask for presence of `pot_parameters_change` field
360    pub const POT_PARAMETERS_CHANGE_MASK: u8 = 0b0000_0100;
361    /// All supported bitmask variants
362    pub const MASK_ALL: u8 = Self::SUPER_SEGMENT_ROOT_MASK
363        | Self::NEXT_SOLUTION_RANGE_MASK
364        | Self::POT_PARAMETERS_CHANGE_MASK;
365
366    /// Create an instance from provided bytes.
367    ///
368    /// `bytes` do not need to be aligned.
369    ///
370    /// Returns an instance and remaining bytes on success.
371    #[inline]
372    pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
373        // Layout here is as follows:
374        // * fixed parameters: BlockHeaderFixedConsensusParameters
375        // * bitflags: u8
376        // * (optional, depends on bitflags) super segment root: SuperSegmentRoot
377        // * (optional, depends on bitflags) next solution range: SolutionRange as unaligned bytes
378        // * (optional, depends on bitflags) PoT parameters change: BlockHeaderPotParametersChange
379
380        let (fixed_parameters, mut remainder) =
381            BlockHeaderFixedConsensusParameters::try_from_bytes(bytes)?;
382
383        let bitflags = remainder.split_off(..size_of::<u8>())?;
384        let bitflags = bitflags[0];
385
386        if (bitflags & Self::MASK_ALL) != bitflags {
387            // Unexpected bitflags were set
388            return None;
389        }
390
391        let super_segment_root = if bitflags & Self::SUPER_SEGMENT_ROOT_MASK != 0 {
392            let super_segment_root = remainder.split_off(..size_of::<SuperSegmentRoot>())?;
393            // SAFETY: All bit patterns are valid
394            let super_segment_root = unsafe { SuperSegmentRoot::from_bytes(super_segment_root) }?;
395
396            Some(super_segment_root)
397        } else {
398            None
399        };
400
401        let next_solution_range = if bitflags & Self::NEXT_SOLUTION_RANGE_MASK != 0 {
402            let next_solution_range = remainder.split_off(..size_of::<SolutionRange>())?;
403            // Not guaranteed to be aligned
404            let next_solution_range = SolutionRange::from_bytes([
405                next_solution_range[0],
406                next_solution_range[1],
407                next_solution_range[2],
408                next_solution_range[3],
409                next_solution_range[4],
410                next_solution_range[5],
411                next_solution_range[6],
412                next_solution_range[7],
413            ]);
414
415            Some(next_solution_range)
416        } else {
417            None
418        };
419
420        let pot_parameters_change = if bitflags & Self::POT_PARAMETERS_CHANGE_MASK != 0 {
421            let pot_parameters_change;
422            (pot_parameters_change, remainder) =
423                BlockHeaderPotParametersChange::try_from_bytes(remainder)?;
424
425            Some(pot_parameters_change)
426        } else {
427            None
428        };
429
430        Some((
431            Self {
432                fixed_parameters,
433                super_segment_root,
434                next_solution_range,
435                pot_parameters_change,
436            },
437            remainder,
438        ))
439    }
440
441    /// Hash of the block consensus parameters, part of the eventual block root
442    pub fn hash(&self) -> Blake3Hash {
443        let Self {
444            super_segment_root,
445            fixed_parameters,
446            next_solution_range,
447            pot_parameters_change,
448        } = self;
449        let BlockHeaderFixedConsensusParameters {
450            solution_range,
451            slot_iterations,
452            num_shards,
453        } = fixed_parameters;
454
455        // TODO: Keyed hash
456        let mut hasher = blake3::Hasher::new();
457        hasher.update(solution_range.as_bytes());
458        hasher.update(&slot_iterations.get().to_le_bytes());
459        hasher.update(NumShardsUnchecked::from(*num_shards).as_bytes());
460
461        if let Some(super_segment_root) = super_segment_root {
462            hasher.update(super_segment_root.as_bytes());
463        }
464        if let Some(next_solution_range) = next_solution_range {
465            hasher.update(next_solution_range.as_bytes());
466        }
467        if let Some(pot_parameters_change) = pot_parameters_change.copied() {
468            let BlockHeaderPotParametersChange {
469                slot,
470                slot_iterations,
471                entropy,
472            } = pot_parameters_change;
473            hasher.update(slot.as_bytes());
474            hasher.update(&slot_iterations.get().to_le_bytes());
475            hasher.update(entropy.as_bytes());
476        }
477
478        Blake3Hash::from(hasher.finalize())
479    }
480}
481
482/// Information about child shard blocks
483#[derive(Debug, Copy, Clone, Deref)]
484pub struct BlockHeaderChildShardBlocks<'a> {
485    /// Child shards blocks
486    pub child_shard_blocks: &'a [BlockRoot],
487}
488
489impl<'a> BlockHeaderChildShardBlocks<'a> {
490    /// Create an instance from provided correctly aligned bytes.
491    ///
492    /// `bytes` should be 2-bytes aligned.
493    ///
494    /// Returns an instance and remaining bytes on success.
495    #[inline]
496    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
497        // Layout here is as follows:
498        // * number of blocks: u16 as aligned little-endian bytes
499        // * for each block:
500        //   * child shard block: BlockHash
501
502        let length = bytes.split_off(..size_of::<u16>())?;
503        // SAFETY: All bit patterns are valid
504        let num_blocks = usize::from(*unsafe { <u16 as TrivialType>::from_bytes(length) }?);
505
506        let padding = bytes.split_off(..size_of::<[u8; 2]>())?;
507
508        // Padding must be zero
509        if padding != [0, 0] {
510            return None;
511        }
512
513        let child_shard_blocks = bytes.split_off(..num_blocks * BlockRoot::SIZE)?;
514        // SAFETY: Valid pointer and size, no alignment requirements
515        let child_shard_blocks = unsafe {
516            slice::from_raw_parts(
517                child_shard_blocks
518                    .as_ptr()
519                    .cast::<[u8; const { BlockRoot::SIZE }]>(),
520                num_blocks,
521            )
522        };
523        let child_shard_blocks = BlockRoot::slice_from_repr(child_shard_blocks);
524
525        Some((Self { child_shard_blocks }, bytes))
526    }
527
528    /// Compute Merkle Tree with child shard blocks, part of the eventual block root.
529    ///
530    /// `None` is returned if there are no child shard blocks.
531    pub fn root(&self) -> Option<Blake3Hash> {
532        let root =
533            UnbalancedMerkleTree::compute_root_only::<'_, const { u64::from(u32::MAX) }, _, _>(
534                // TODO: Keyed hash
535                self.child_shard_blocks
536                    .iter()
537                    .map(|child_shard_block_root| {
538                        // Hash the root again so we can prove it, otherwise headers root is
539                        // indistinguishable from individual block roots and can be used to confuse
540                        // verifier
541                        single_block_hash(child_shard_block_root.as_ref())
542                            .expect("Less than a single block worth of bytes; qed")
543                    }),
544            )?;
545        Some(Blake3Hash::new(root))
546    }
547}
548
549/// Block header result.
550///
551/// The result contains information that can only be computed after the block was created.
552#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
553#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
554#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
555#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
556#[repr(C)]
557pub struct BlockHeaderResult {
558    /// Root of the block body
559    // TODO: New type
560    pub body_root: Blake3Hash,
561    /// Root of the state tree
562    // TODO: New type?
563    pub state_root: Blake3Hash,
564}
565
566impl BlockHeaderResult {
567    /// Hash of the block header result, part of the eventual block root
568    pub fn hash(&self) -> Blake3Hash {
569        const {
570            assert!(size_of::<Self>() <= BLOCK_LEN);
571        }
572        // TODO: Keyed hash
573        Blake3Hash::new(
574            single_block_hash(self.as_bytes())
575                .expect("Less than a single block worth of bytes; qed"),
576        )
577    }
578}
579
580/// Block header seal type
581#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
582#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
583#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
584#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
585#[repr(u8)]
586#[non_exhaustive]
587pub enum BlockHeaderSealType {
588    /// Ed25519 signature
589    #[cfg_attr(feature = "scale-codec", codec(index = 0))]
590    Ed25519 = 0,
591}
592
593impl BlockHeaderSealType {
594    /// Create an instance from bytes if valid
595    #[inline(always)]
596    pub const fn try_from_byte(byte: u8) -> Option<Self> {
597        if byte == Self::Ed25519 as u8 {
598            Some(Self::Ed25519)
599        } else {
600            None
601        }
602    }
603}
604
605/// Ed25519 seal
606#[derive(Debug, Copy, Clone, Eq, PartialEq, TrivialType)]
607#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
608#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
609#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
610#[repr(C)]
611pub struct BlockHeaderEd25519Seal {
612    /// Ed25519 public key
613    pub public_key: Ed25519PublicKey,
614    /// Ed25519 signature
615    pub signature: Ed25519Signature,
616}
617
618/// Owned version of [`BlockHeaderSeal`]
619#[derive(Debug, Copy, Clone)]
620#[cfg_attr(feature = "scale-codec", derive(Encode, Decode, MaxEncodedLen))]
621#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
622#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
623#[non_exhaustive]
624pub enum OwnedBlockHeaderSeal {
625    /// Ed25519 seal
626    Ed25519(BlockHeaderEd25519Seal),
627}
628
629impl OwnedBlockHeaderSeal {
630    /// Get a reference out of owned version
631    #[inline(always)]
632    pub fn as_ref(&self) -> BlockHeaderSeal<'_> {
633        match self {
634            Self::Ed25519(seal) => BlockHeaderSeal::Ed25519(seal),
635        }
636    }
637}
638
639/// Block header seal
640#[derive(Debug, Copy, Clone)]
641#[non_exhaustive]
642pub enum BlockHeaderSeal<'a> {
643    /// Ed25519 seal
644    Ed25519(&'a BlockHeaderEd25519Seal),
645}
646
647impl<'a> BlockHeaderSeal<'a> {
648    /// Max size of the allocation necessary for this data structure
649    pub const MAX_SIZE: u32 = 1 + BlockHeaderEd25519Seal::SIZE;
650    /// Create an instance from provided bytes.
651    ///
652    /// `bytes` do not need to be aligned.
653    ///
654    /// Returns an instance and remaining bytes on success.
655    #[inline]
656    pub fn try_from_bytes(mut bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
657        // The layout here is as follows:
658        // * seal type: u8
659        // * seal (depends on a seal type): BlockHeaderEd25519Seal
660
661        let seal_type = bytes.split_off(..size_of::<u8>())?;
662        let seal_type = BlockHeaderSealType::try_from_byte(seal_type[0])?;
663
664        match seal_type {
665            BlockHeaderSealType::Ed25519 => {
666                let seal = bytes.split_off(..size_of::<BlockHeaderEd25519Seal>())?;
667                // SAFETY: All bit patterns are valid
668                let seal = unsafe { BlockHeaderEd25519Seal::from_bytes(seal) }?;
669                Some((Self::Ed25519(seal), bytes))
670            }
671        }
672    }
673
674    /// Verify seal against [`BlockHeader::pre_seal_hash()`]
675    #[inline]
676    pub fn is_seal_valid(&self, pre_seal_hash: &Blake3Hash) -> bool {
677        match self {
678            BlockHeaderSeal::Ed25519(seal) => seal
679                .public_key
680                .verify(&seal.signature, pre_seal_hash.as_bytes())
681                .is_ok(),
682        }
683    }
684
685    /// Derive public key hash from this seal
686    #[inline]
687    pub fn public_key_hash(&self) -> Blake3Hash {
688        match self {
689            BlockHeaderSeal::Ed25519(seal) => seal.public_key.hash(),
690        }
691    }
692
693    /// Hash of the block header seal, part of the eventual block root
694    #[inline]
695    pub fn hash(&self) -> Blake3Hash {
696        match self {
697            BlockHeaderSeal::Ed25519(seal) => {
698                // TODO: Keyed hash
699                let mut hasher = blake3::Hasher::new();
700                hasher.update(&[BlockHeaderSealType::Ed25519 as u8]);
701                hasher.update(seal.as_bytes());
702
703                Blake3Hash::from(hasher.finalize())
704            }
705        }
706    }
707}
708
709/// Part of the block header, shared for different kinds of shards
710#[derive(Debug, Copy, Clone)]
711pub struct SharedBlockHeader<'a> {
712    /// Block header prefix
713    pub prefix: &'a BlockHeaderPrefix,
714    /// Block header result
715    pub result: &'a BlockHeaderResult,
716    /// Consensus information
717    pub consensus_info: &'a BlockHeaderConsensusInfo,
718    /// Block header seal
719    pub seal: BlockHeaderSeal<'a>,
720}
721
722/// Block header that corresponds to the beacon chain
723#[derive(Debug, Clone, Yokeable)]
724// Prevent creation of potentially broken invariants externally
725#[non_exhaustive]
726pub struct BeaconChainHeader<'a> {
727    /// Shared block header
728    shared: SharedBlockHeader<'a>,
729    /// Information about child shard blocks
730    child_shard_blocks: BlockHeaderChildShardBlocks<'a>,
731    /// Consensus parameters (on the beacon chain)
732    consensus_parameters: BlockHeaderConsensusParameters<'a>,
733    /// All bytes of the header except the seal
734    pre_seal_bytes: &'a [u8],
735    #[cfg(all(feature = "alloc", any(target_os = "none", target_os = "unknown")))]
736    cached_block_root: rclite::Arc<once_cell::race::OnceBox<BlockRoot>>,
737    #[cfg(not(any(target_os = "none", target_os = "unknown")))]
738    cached_block_root: rclite::Arc<std::sync::OnceLock<BlockRoot>>,
739}
740
741impl<'a> Deref for BeaconChainHeader<'a> {
742    type Target = SharedBlockHeader<'a>;
743
744    #[inline(always)]
745    fn deref(&self) -> &Self::Target {
746        &self.shared
747    }
748}
749
750impl<'a> GenericBlockHeader<'a> for BeaconChainHeader<'a> {
751    const SHARD_KIND: RealShardKind = RealShardKind::BeaconChain;
752
753    #[cfg(feature = "alloc")]
754    type Owned = OwnedBeaconChainHeader;
755
756    #[cfg(feature = "alloc")]
757    #[inline(always)]
758    fn to_owned(self) -> Self::Owned {
759        self.to_owned()
760    }
761
762    #[inline(always)]
763    fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
764        self.root()
765    }
766
767    #[inline(always)]
768    fn pre_seal_hash(&self) -> Blake3Hash {
769        self.pre_seal_hash()
770    }
771}
772
773impl<'a> BeaconChainHeader<'a> {
774    /// Try to create a new instance from provided bytes.
775    ///
776    /// `bytes` should be 8-bytes aligned.
777    ///
778    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
779    /// bytes are not properly aligned or input is otherwise invalid.
780    #[inline]
781    pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
782        // The layout here is as follows:
783        // * block header prefix: BlockHeaderPrefix
784        // * block header result: BlockHeaderResult
785        // * consensus info: BlockHeaderConsensusInfo
786        // * child shard blocks: BlockHeaderChildShardBlocks
787        // * beacon chain parameters: BlockHeaderBeaconChainParameters
788        // * block header seal: BlockHeaderSeal
789
790        let (prefix, consensus_info, result, remainder) =
791            BlockHeader::try_from_bytes_shared(bytes)?;
792
793        if prefix.shard_index.shard_kind() != Some(ShardKind::BeaconChain) {
794            return None;
795        }
796
797        let (child_shard_blocks, remainder) =
798            BlockHeaderChildShardBlocks::try_from_bytes(remainder)?;
799
800        let (consensus_parameters, remainder) =
801            BlockHeaderConsensusParameters::try_from_bytes(remainder)?;
802
803        let pre_seal_bytes = &bytes[..bytes.len() - remainder.len()];
804
805        let (seal, remainder) = BlockHeaderSeal::try_from_bytes(remainder)?;
806
807        let shared = SharedBlockHeader {
808            prefix,
809            result,
810            consensus_info,
811            seal,
812        };
813
814        let header = Self {
815            shared,
816            child_shard_blocks,
817            consensus_parameters,
818            pre_seal_bytes,
819            #[cfg(any(feature = "alloc", not(any(target_os = "none", target_os = "unknown"))))]
820            cached_block_root: rclite::Arc::default(),
821        };
822
823        if !header.is_internally_consistent() {
824            return None;
825        }
826
827        Some((header, remainder))
828    }
829
830    /// Check block header's internal consistency.
831    ///
832    /// This is usually not necessary to be called explicitly since internal consistency is checked
833    /// by [`Self::try_from_bytes()`] internally.
834    #[inline]
835    pub fn is_internally_consistent(&self) -> bool {
836        let public_key_hash = match self.seal {
837            BlockHeaderSeal::Ed25519(seal) => seal.public_key.hash(),
838        };
839        public_key_hash == self.shared.consensus_info.solution.public_key_hash
840    }
841
842    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
843    /// checks
844    #[inline]
845    pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
846        // The layout here is as follows:
847        // * block header prefix: BlockHeaderPrefix
848        // * block header result: BlockHeaderResult
849        // * consensus info: BlockHeaderConsensusInfo
850        // * child shard blocks: BlockHeaderChildShardBlocks
851        // * beacon chain parameters: BlockHeaderBeaconChainParameters
852        // * block header seal: BlockHeaderSeal
853
854        let (prefix, consensus_info, result, remainder) =
855            BlockHeader::try_from_bytes_shared(bytes)?;
856
857        if prefix.shard_index.shard_kind() != Some(ShardKind::BeaconChain) {
858            return None;
859        }
860
861        let (child_shard_blocks, remainder) =
862            BlockHeaderChildShardBlocks::try_from_bytes(remainder)?;
863
864        let (consensus_parameters, remainder) =
865            BlockHeaderConsensusParameters::try_from_bytes(remainder)?;
866
867        let pre_seal_bytes = &bytes[..bytes.len() - remainder.len()];
868
869        let (seal, remainder) = BlockHeaderSeal::try_from_bytes(remainder)?;
870
871        let shared = SharedBlockHeader {
872            prefix,
873            result,
874            consensus_info,
875            seal,
876        };
877
878        Some((
879            Self {
880                shared,
881                child_shard_blocks,
882                consensus_parameters,
883                pre_seal_bytes,
884                #[cfg(any(
885                    feature = "alloc",
886                    not(any(target_os = "none", target_os = "unknown"))
887                ))]
888                cached_block_root: rclite::Arc::default(),
889            },
890            remainder,
891        ))
892    }
893
894    /// Create an owned version of this header
895    #[cfg(feature = "alloc")]
896    #[inline(always)]
897    pub fn to_owned(self) -> OwnedBeaconChainHeader {
898        let unsealed = OwnedBeaconChainHeader::from_parts(
899            self.shared.prefix,
900            self.shared.result,
901            self.shared.consensus_info,
902            &self.child_shard_blocks,
903            &self.consensus_parameters,
904        )
905        .expect("`self` is always a valid invariant; qed");
906
907        unsealed.with_seal(self.shared.seal)
908    }
909
910    /// Shared block header
911    #[inline(always)]
912    pub fn shared(&self) -> &SharedBlockHeader<'a> {
913        &self.shared
914    }
915
916    /// Information about child shard blocks
917    #[inline(always)]
918    pub fn child_shard_blocks(&self) -> &BlockHeaderChildShardBlocks<'a> {
919        &self.child_shard_blocks
920    }
921
922    /// Consensus parameters (on the beacon chain)
923    #[inline(always)]
924    pub fn consensus_parameters(&self) -> &BlockHeaderConsensusParameters<'a> {
925        &self.consensus_parameters
926    }
927
928    /// Hash of the block before seal is applied to it
929    #[inline]
930    pub fn pre_seal_hash(&self) -> Blake3Hash {
931        // TODO: Keyed hash with `block_header_seal` as a key
932        Blake3Hash::from(blake3::hash(self.pre_seal_bytes))
933    }
934
935    /// Verify seal against [`BeaconChainHeader::pre_seal_hash()`] and check that its public key
936    /// hash corresponds to the solution
937    #[inline]
938    pub fn is_sealed_correctly(&self) -> bool {
939        self.consensus_info.solution.public_key_hash == self.seal.public_key_hash()
940            && self.seal.is_seal_valid(&self.pre_seal_hash())
941    }
942
943    /// Compute block root out of this header.
944    ///
945    /// Block root is a Merkle Tree Root. The leaves are derived from individual fields in
946    /// [`SharedBlockHeader`] and other fields of this enum in the declaration order.
947    ///
948    /// Note that this method computes root by doing a bunch of hashing. The result is then cached
949    /// if `alloc` feature is enabled or when compiled for OS target that is not `none`.
950    #[inline]
951    pub fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
952        let Self {
953            shared,
954            child_shard_blocks,
955            consensus_parameters,
956            pre_seal_bytes: _,
957            #[cfg(any(feature = "alloc", not(any(target_os = "none", target_os = "unknown"))))]
958            cached_block_root,
959        } = self;
960
961        let compute_root = || {
962            let SharedBlockHeader {
963                prefix,
964                result,
965                consensus_info,
966                seal,
967            } = shared;
968
969            let block_root = UnbalancedMerkleTree::compute_root_only_array(&[
970                prefix.hash(),
971                result.hash(),
972                consensus_info.hash(),
973                seal.hash(),
974                child_shard_blocks.root().unwrap_or_default(),
975                consensus_parameters.hash(),
976            ]);
977
978            BlockRoot::new(Blake3Hash::new(block_root))
979        };
980
981        #[cfg(not(any(target_os = "none", target_os = "unknown")))]
982        {
983            cached_block_root.get_or_init(compute_root)
984        }
985        #[cfg(all(feature = "alloc", any(target_os = "none", target_os = "unknown")))]
986        {
987            cached_block_root.get_or_init(|| alloc::boxed::Box::new(compute_root()))
988        }
989        #[cfg(all(not(feature = "alloc"), any(target_os = "none", target_os = "unknown")))]
990        {
991            struct Wrapper(BlockRoot);
992
993            impl Deref for Wrapper {
994                type Target = BlockRoot;
995
996                #[inline(always)]
997                fn deref(&self) -> &Self::Target {
998                    &self.0
999                }
1000            }
1001
1002            Wrapper(compute_root())
1003        }
1004    }
1005}
1006
1007/// Block header that corresponds to an intermediate shard
1008#[derive(Debug, Clone, Yokeable)]
1009// Prevent creation of potentially broken invariants externally
1010#[non_exhaustive]
1011pub struct IntermediateShardHeader<'a> {
1012    /// Shared block header
1013    shared: SharedBlockHeader<'a>,
1014    /// Beacon chain info
1015    beacon_chain_info: &'a BlockHeaderBeaconChainInfo,
1016    /// Information about child shard blocks
1017    child_shard_blocks: BlockHeaderChildShardBlocks<'a>,
1018    /// All bytes of the header except the seal
1019    pre_seal_bytes: &'a [u8],
1020    #[cfg(all(feature = "alloc", any(target_os = "none", target_os = "unknown")))]
1021    cached_block_root: rclite::Arc<once_cell::race::OnceBox<BlockRoot>>,
1022    #[cfg(not(any(target_os = "none", target_os = "unknown")))]
1023    cached_block_root: rclite::Arc<std::sync::OnceLock<BlockRoot>>,
1024}
1025
1026impl<'a> Deref for IntermediateShardHeader<'a> {
1027    type Target = SharedBlockHeader<'a>;
1028
1029    #[inline(always)]
1030    fn deref(&self) -> &Self::Target {
1031        &self.shared
1032    }
1033}
1034
1035impl<'a> GenericBlockHeader<'a> for IntermediateShardHeader<'a> {
1036    const SHARD_KIND: RealShardKind = RealShardKind::IntermediateShard;
1037
1038    #[cfg(feature = "alloc")]
1039    type Owned = OwnedIntermediateShardHeader;
1040
1041    #[cfg(feature = "alloc")]
1042    #[inline(always)]
1043    fn to_owned(self) -> Self::Owned {
1044        self.to_owned()
1045    }
1046
1047    #[inline(always)]
1048    fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
1049        self.root()
1050    }
1051
1052    #[inline(always)]
1053    fn pre_seal_hash(&self) -> Blake3Hash {
1054        self.pre_seal_hash()
1055    }
1056}
1057
1058impl<'a> IntermediateShardHeader<'a> {
1059    /// Try to create a new instance from provided bytes.
1060    ///
1061    /// `bytes` should be 8-bytes aligned.
1062    ///
1063    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
1064    /// bytes are not properly aligned or input is otherwise invalid.
1065    #[inline]
1066    pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1067        // The layout here is as follows:
1068        // * block header prefix: BlockHeaderPrefix
1069        // * block header result: BlockHeaderResult
1070        // * consensus info: BlockHeaderConsensusInfo
1071        // * beacon chain: BlockHeaderBeaconChainInfo
1072        // * child shard blocks: BlockHeaderBeaconChainInfo
1073        // * block header seal: BlockHeaderSeal
1074
1075        let (prefix, consensus_info, result, mut remainder) =
1076            BlockHeader::try_from_bytes_shared(bytes)?;
1077
1078        if prefix.shard_index.shard_kind() != Some(ShardKind::IntermediateShard) {
1079            return None;
1080        }
1081
1082        let beacon_chain_info = remainder.split_off(..size_of::<BlockHeaderBeaconChainInfo>())?;
1083        // SAFETY: All bit patterns are valid
1084        let beacon_chain_info =
1085            unsafe { BlockHeaderBeaconChainInfo::from_bytes(beacon_chain_info) }?;
1086
1087        let (child_shard_blocks, remainder) =
1088            BlockHeaderChildShardBlocks::try_from_bytes(remainder)?;
1089
1090        let pre_seal_bytes = &bytes[..bytes.len() - remainder.len()];
1091
1092        let (seal, remainder) = BlockHeaderSeal::try_from_bytes(remainder)?;
1093
1094        let shared = SharedBlockHeader {
1095            prefix,
1096            result,
1097            consensus_info,
1098            seal,
1099        };
1100
1101        let header = Self {
1102            shared,
1103            beacon_chain_info,
1104            child_shard_blocks,
1105            pre_seal_bytes,
1106            #[cfg(any(feature = "alloc", not(any(target_os = "none", target_os = "unknown"))))]
1107            cached_block_root: rclite::Arc::default(),
1108        };
1109
1110        if !header.is_internally_consistent() {
1111            return None;
1112        }
1113
1114        Some((header, remainder))
1115    }
1116
1117    /// Check block header's internal consistency.
1118    ///
1119    /// This is usually not necessary to be called explicitly since internal consistency is checked
1120    /// by [`Self::try_from_bytes()`] internally.
1121    #[inline]
1122    pub fn is_internally_consistent(&self) -> bool {
1123        let public_key_hash = match self.seal {
1124            BlockHeaderSeal::Ed25519(seal) => seal.public_key.hash(),
1125        };
1126        public_key_hash == self.shared.consensus_info.solution.public_key_hash
1127    }
1128
1129    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1130    /// checks
1131    #[inline]
1132    pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1133        // The layout here is as follows:
1134        // * block header prefix: BlockHeaderPrefix
1135        // * block header result: BlockHeaderResult
1136        // * consensus info: BlockHeaderConsensusInfo
1137        // * beacon chain: BlockHeaderBeaconChainInfo
1138        // * child shard blocks: BlockHeaderBeaconChainInfo
1139        // * block header seal: BlockHeaderSeal
1140
1141        let (prefix, consensus_info, result, mut remainder) =
1142            BlockHeader::try_from_bytes_shared(bytes)?;
1143
1144        if prefix.shard_index.shard_kind() != Some(ShardKind::IntermediateShard) {
1145            return None;
1146        }
1147
1148        let beacon_chain_info = remainder.split_off(..size_of::<BlockHeaderBeaconChainInfo>())?;
1149        // SAFETY: All bit patterns are valid
1150        let beacon_chain_info =
1151            unsafe { BlockHeaderBeaconChainInfo::from_bytes(beacon_chain_info) }?;
1152
1153        let (child_shard_blocks, remainder) =
1154            BlockHeaderChildShardBlocks::try_from_bytes(remainder)?;
1155
1156        let pre_seal_bytes = &bytes[..bytes.len() - remainder.len()];
1157
1158        let (seal, remainder) = BlockHeaderSeal::try_from_bytes(remainder)?;
1159
1160        let shared = SharedBlockHeader {
1161            prefix,
1162            result,
1163            consensus_info,
1164            seal,
1165        };
1166
1167        Some((
1168            Self {
1169                shared,
1170                beacon_chain_info,
1171                child_shard_blocks,
1172                pre_seal_bytes,
1173                #[cfg(any(
1174                    feature = "alloc",
1175                    not(any(target_os = "none", target_os = "unknown"))
1176                ))]
1177                cached_block_root: rclite::Arc::default(),
1178            },
1179            remainder,
1180        ))
1181    }
1182
1183    /// Create an owned version of this header
1184    #[cfg(feature = "alloc")]
1185    #[inline(always)]
1186    pub fn to_owned(self) -> OwnedIntermediateShardHeader {
1187        let unsealed = OwnedIntermediateShardHeader::from_parts(
1188            self.shared.prefix,
1189            self.shared.result,
1190            self.shared.consensus_info,
1191            self.beacon_chain_info,
1192            &self.child_shard_blocks,
1193        )
1194        .expect("`self` is always a valid invariant; qed");
1195
1196        unsealed.with_seal(self.shared.seal)
1197    }
1198
1199    /// Shared block header
1200    #[inline(always)]
1201    pub fn shared(&self) -> &SharedBlockHeader<'a> {
1202        &self.shared
1203    }
1204
1205    /// Beacon chain info
1206    #[inline(always)]
1207    pub fn beacon_chain_info(&self) -> &'a BlockHeaderBeaconChainInfo {
1208        self.beacon_chain_info
1209    }
1210
1211    /// Information about child shard blocks
1212    #[inline(always)]
1213    pub fn child_shard_blocks(&self) -> &BlockHeaderChildShardBlocks<'a> {
1214        &self.child_shard_blocks
1215    }
1216
1217    /// Hash of the block before seal is applied to it
1218    #[inline]
1219    pub fn pre_seal_hash(&self) -> Blake3Hash {
1220        // TODO: Keyed hash with `block_header_seal` as a key
1221        Blake3Hash::from(blake3::hash(self.pre_seal_bytes))
1222    }
1223
1224    /// Verify seal against [`IntermediateShardHeader::pre_seal_hash()`] and check that its public
1225    /// key hash corresponds to the solution
1226    #[inline]
1227    pub fn is_sealed_correctly(&self) -> bool {
1228        self.consensus_info.solution.public_key_hash == self.seal.public_key_hash()
1229            && self.seal.is_seal_valid(&self.pre_seal_hash())
1230    }
1231
1232    /// Compute block root out of this header.
1233    ///
1234    /// Block root is a Merkle Tree Root. The leaves are derived from individual fields in
1235    /// [`SharedBlockHeader`] and other fields of this enum in the declaration order.
1236    ///
1237    /// Note that this method computes root by doing a bunch of hashing. The result is then cached
1238    /// if `alloc` feature is enabled or when compiled for OS target that is not `none`.
1239    #[inline]
1240    pub fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
1241        let Self {
1242            shared,
1243            beacon_chain_info,
1244            child_shard_blocks,
1245            pre_seal_bytes: _,
1246            #[cfg(any(feature = "alloc", not(any(target_os = "none", target_os = "unknown"))))]
1247            cached_block_root,
1248        } = self;
1249
1250        let compute_root = || {
1251            let SharedBlockHeader {
1252                prefix,
1253                result,
1254                consensus_info,
1255                seal,
1256            } = shared;
1257
1258            let block_root = UnbalancedMerkleTree::compute_root_only_array(&[
1259                prefix.hash(),
1260                result.hash(),
1261                consensus_info.hash(),
1262                seal.hash(),
1263                beacon_chain_info.hash(),
1264                child_shard_blocks.root().unwrap_or_default(),
1265            ]);
1266
1267            BlockRoot::new(Blake3Hash::new(block_root))
1268        };
1269
1270        #[cfg(not(any(target_os = "none", target_os = "unknown")))]
1271        {
1272            cached_block_root.get_or_init(compute_root)
1273        }
1274        #[cfg(all(feature = "alloc", any(target_os = "none", target_os = "unknown")))]
1275        {
1276            cached_block_root.get_or_init(|| alloc::boxed::Box::new(compute_root()))
1277        }
1278        #[cfg(all(not(feature = "alloc"), any(target_os = "none", target_os = "unknown")))]
1279        {
1280            struct Wrapper(BlockRoot);
1281
1282            impl Deref for Wrapper {
1283                type Target = BlockRoot;
1284
1285                #[inline(always)]
1286                fn deref(&self) -> &Self::Target {
1287                    &self.0
1288                }
1289            }
1290
1291            Wrapper(compute_root())
1292        }
1293    }
1294}
1295
1296/// Block header that corresponds to a leaf shard
1297#[derive(Debug, Clone, Yokeable)]
1298// Prevent creation of potentially broken invariants externally
1299#[non_exhaustive]
1300pub struct LeafShardHeader<'a> {
1301    /// Shared block header
1302    shared: SharedBlockHeader<'a>,
1303    /// Beacon chain info
1304    beacon_chain_info: &'a BlockHeaderBeaconChainInfo,
1305    /// All bytes of the header except the seal
1306    pre_seal_bytes: &'a [u8],
1307    #[cfg(all(feature = "alloc", any(target_os = "none", target_os = "unknown")))]
1308    cached_block_root: rclite::Arc<once_cell::race::OnceBox<BlockRoot>>,
1309    #[cfg(not(any(target_os = "none", target_os = "unknown")))]
1310    cached_block_root: rclite::Arc<std::sync::OnceLock<BlockRoot>>,
1311}
1312
1313impl<'a> Deref for LeafShardHeader<'a> {
1314    type Target = SharedBlockHeader<'a>;
1315
1316    #[inline(always)]
1317    fn deref(&self) -> &Self::Target {
1318        &self.shared
1319    }
1320}
1321
1322impl<'a> GenericBlockHeader<'a> for LeafShardHeader<'a> {
1323    const SHARD_KIND: RealShardKind = RealShardKind::LeafShard;
1324
1325    #[cfg(feature = "alloc")]
1326    type Owned = OwnedLeafShardHeader;
1327
1328    #[cfg(feature = "alloc")]
1329    #[inline(always)]
1330    fn to_owned(self) -> Self::Owned {
1331        self.to_owned()
1332    }
1333
1334    #[inline(always)]
1335    fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
1336        self.root()
1337    }
1338
1339    #[inline(always)]
1340    fn pre_seal_hash(&self) -> Blake3Hash {
1341        self.pre_seal_hash()
1342    }
1343}
1344
1345impl<'a> LeafShardHeader<'a> {
1346    /// Try to create a new instance from provided bytes.
1347    ///
1348    /// `bytes` should be 8-bytes aligned.
1349    ///
1350    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
1351    /// bytes are not properly aligned or input is otherwise invalid.
1352    #[inline]
1353    pub fn try_from_bytes(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1354        // The layout here is as follows:
1355        // * block header result: BlockHeaderResult
1356        // * block header prefix: BlockHeaderPrefix
1357        // * consensus info: BlockHeaderConsensusInfo
1358        // * beacon chain: BlockHeaderBeaconChainInfo
1359        // * block header seal: BlockHeaderSeal
1360
1361        let (prefix, consensus_info, result, mut remainder) =
1362            BlockHeader::try_from_bytes_shared(bytes)?;
1363
1364        if prefix.shard_index.shard_kind() != Some(ShardKind::LeafShard) {
1365            return None;
1366        }
1367
1368        let beacon_chain_info = remainder.split_off(..size_of::<BlockHeaderBeaconChainInfo>())?;
1369        // SAFETY: All bit patterns are valid
1370        let beacon_chain_info =
1371            unsafe { BlockHeaderBeaconChainInfo::from_bytes(beacon_chain_info) }?;
1372
1373        let pre_seal_bytes = &bytes[..bytes.len() - remainder.len()];
1374
1375        let (seal, remainder) = BlockHeaderSeal::try_from_bytes(remainder)?;
1376
1377        let shared = SharedBlockHeader {
1378            prefix,
1379            result,
1380            consensus_info,
1381            seal,
1382        };
1383
1384        let header = Self {
1385            shared,
1386            beacon_chain_info,
1387            pre_seal_bytes,
1388            #[cfg(any(feature = "alloc", not(any(target_os = "none", target_os = "unknown"))))]
1389            cached_block_root: rclite::Arc::default(),
1390        };
1391
1392        if !header.is_internally_consistent() {
1393            return None;
1394        }
1395
1396        Some((header, remainder))
1397    }
1398
1399    /// Check block header's internal consistency.
1400    ///
1401    /// This is usually not necessary to be called explicitly since internal consistency is checked
1402    /// by [`Self::try_from_bytes()`] internally.
1403    #[inline]
1404    pub fn is_internally_consistent(&self) -> bool {
1405        let public_key_hash = match self.seal {
1406            BlockHeaderSeal::Ed25519(seal) => seal.public_key.hash(),
1407        };
1408        public_key_hash == self.shared.consensus_info.solution.public_key_hash
1409    }
1410
1411    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1412    /// checks
1413    #[inline]
1414    pub fn try_from_bytes_unchecked(bytes: &'a [u8]) -> Option<(Self, &'a [u8])> {
1415        // The layout here is as follows:
1416        // * block header result: BlockHeaderResult
1417        // * block header prefix: BlockHeaderPrefix
1418        // * consensus info: BlockHeaderConsensusInfo
1419        // * beacon chain: BlockHeaderBeaconChainInfo
1420        // * block header seal: BlockHeaderSeal
1421
1422        let (prefix, consensus_info, result, mut remainder) =
1423            BlockHeader::try_from_bytes_shared(bytes)?;
1424
1425        if prefix.shard_index.shard_kind() != Some(ShardKind::LeafShard) {
1426            return None;
1427        }
1428
1429        let beacon_chain_info = remainder.split_off(..size_of::<BlockHeaderBeaconChainInfo>())?;
1430        // SAFETY: All bit patterns are valid
1431        let beacon_chain_info =
1432            unsafe { BlockHeaderBeaconChainInfo::from_bytes(beacon_chain_info) }?;
1433
1434        let pre_seal_bytes = &bytes[..bytes.len() - remainder.len()];
1435
1436        let (seal, remainder) = BlockHeaderSeal::try_from_bytes(remainder)?;
1437
1438        let shared = SharedBlockHeader {
1439            prefix,
1440            result,
1441            consensus_info,
1442            seal,
1443        };
1444
1445        Some((
1446            Self {
1447                shared,
1448                beacon_chain_info,
1449                pre_seal_bytes,
1450                #[cfg(any(
1451                    feature = "alloc",
1452                    not(any(target_os = "none", target_os = "unknown"))
1453                ))]
1454                cached_block_root: rclite::Arc::default(),
1455            },
1456            remainder,
1457        ))
1458    }
1459
1460    /// Create an owned version of this header
1461    #[cfg(feature = "alloc")]
1462    #[inline(always)]
1463    pub fn to_owned(self) -> OwnedLeafShardHeader {
1464        let unsealed = OwnedLeafShardHeader::from_parts(
1465            self.shared.prefix,
1466            self.shared.result,
1467            self.shared.consensus_info,
1468            self.beacon_chain_info,
1469        );
1470
1471        unsealed.with_seal(self.shared.seal)
1472    }
1473
1474    /// Shared block header
1475    #[inline(always)]
1476    pub fn shared(&self) -> &SharedBlockHeader<'a> {
1477        &self.shared
1478    }
1479
1480    /// Beacon chain info
1481    #[inline(always)]
1482    pub fn beacon_chain_info(&self) -> &'a BlockHeaderBeaconChainInfo {
1483        self.beacon_chain_info
1484    }
1485
1486    /// Hash of the block before seal is applied to it
1487    #[inline]
1488    pub fn pre_seal_hash(&self) -> Blake3Hash {
1489        // TODO: Keyed hash with `block_header_seal` as a key
1490        Blake3Hash::from(blake3::hash(self.pre_seal_bytes))
1491    }
1492
1493    /// Verify seal against [`LeafShardHeader::pre_seal_hash()`] and check that its public key hash
1494    /// corresponds to the solution
1495    #[inline]
1496    pub fn is_sealed_correctly(&self) -> bool {
1497        self.consensus_info.solution.public_key_hash == self.seal.public_key_hash()
1498            && self.seal.is_seal_valid(&self.pre_seal_hash())
1499    }
1500
1501    /// Compute block root out of this header.
1502    ///
1503    /// Block root is a Merkle Tree Root. The leaves are derived from individual fields in
1504    /// [`SharedBlockHeader`] and other fields of this enum in the declaration order.
1505    ///
1506    /// Note that this method computes root by doing a bunch of hashing. The result is then cached
1507    /// if `alloc` feature is enabled or when compiled for OS target that is not `none`.
1508    #[inline]
1509    pub fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
1510        let Self {
1511            shared,
1512            beacon_chain_info,
1513            pre_seal_bytes: _,
1514            #[cfg(any(feature = "alloc", not(any(target_os = "none", target_os = "unknown"))))]
1515            cached_block_root,
1516        } = self;
1517
1518        let compute_root = || {
1519            let SharedBlockHeader {
1520                prefix,
1521                result,
1522                consensus_info,
1523                seal,
1524            } = shared;
1525
1526            let block_root = UnbalancedMerkleTree::compute_root_only_array(&[
1527                prefix.hash(),
1528                result.hash(),
1529                consensus_info.hash(),
1530                seal.hash(),
1531                beacon_chain_info.hash(),
1532            ]);
1533
1534            BlockRoot::new(Blake3Hash::new(block_root))
1535        };
1536
1537        #[cfg(not(any(target_os = "none", target_os = "unknown")))]
1538        {
1539            cached_block_root.get_or_init(compute_root)
1540        }
1541        #[cfg(all(feature = "alloc", any(target_os = "none", target_os = "unknown")))]
1542        {
1543            cached_block_root.get_or_init(|| alloc::boxed::Box::new(compute_root()))
1544        }
1545        #[cfg(all(not(feature = "alloc"), any(target_os = "none", target_os = "unknown")))]
1546        {
1547            struct Wrapper(BlockRoot);
1548
1549            impl Deref for Wrapper {
1550                type Target = BlockRoot;
1551
1552                #[inline(always)]
1553                fn deref(&self) -> &Self::Target {
1554                    &self.0
1555                }
1556            }
1557
1558            Wrapper(compute_root())
1559        }
1560    }
1561}
1562
1563/// Block header that together with [`BlockBody`] form a [`Block`]
1564///
1565/// [`BlockBody`]: crate::block::body::BlockBody
1566/// [`Block`]: crate::block::Block
1567#[derive(Debug, Clone, From)]
1568pub enum BlockHeader<'a> {
1569    /// Block header corresponds to the beacon chain
1570    BeaconChain(BeaconChainHeader<'a>),
1571    /// Block header corresponds to an intermediate shard
1572    IntermediateShard(IntermediateShardHeader<'a>),
1573    /// Block header corresponds to a leaf shard
1574    LeafShard(LeafShardHeader<'a>),
1575}
1576
1577impl<'a> Deref for BlockHeader<'a> {
1578    type Target = SharedBlockHeader<'a>;
1579
1580    #[inline(always)]
1581    fn deref(&self) -> &Self::Target {
1582        match self {
1583            Self::BeaconChain(header) => header,
1584            Self::IntermediateShard(header) => header,
1585            Self::LeafShard(header) => header,
1586        }
1587    }
1588}
1589
1590impl<'a> BlockHeader<'a> {
1591    /// Try to create a new instance from provided bytes for provided shard index.
1592    ///
1593    /// `bytes` should be 8-bytes aligned.
1594    ///
1595    /// Returns an instance and remaining bytes on success, `None` if too few bytes were given,
1596    /// bytes are not properly aligned or input is otherwise invalid.
1597    #[inline]
1598    pub fn try_from_bytes(bytes: &'a [u8], shard_kind: RealShardKind) -> Option<(Self, &'a [u8])> {
1599        match shard_kind {
1600            RealShardKind::BeaconChain => {
1601                let (header, remainder) = BeaconChainHeader::try_from_bytes(bytes)?;
1602                Some((Self::BeaconChain(header), remainder))
1603            }
1604            RealShardKind::IntermediateShard => {
1605                let (header, remainder) = IntermediateShardHeader::try_from_bytes(bytes)?;
1606                Some((Self::IntermediateShard(header), remainder))
1607            }
1608            RealShardKind::LeafShard => {
1609                let (header, remainder) = LeafShardHeader::try_from_bytes(bytes)?;
1610                Some((Self::LeafShard(header), remainder))
1611            }
1612        }
1613    }
1614
1615    /// Check block header's internal consistency.
1616    ///
1617    /// This is usually not necessary to be called explicitly since internal consistency is checked
1618    /// by [`Self::try_from_bytes()`] internally.
1619    #[inline]
1620    pub fn is_internally_consistent(&self) -> bool {
1621        match self {
1622            Self::BeaconChain(header) => header.is_internally_consistent(),
1623            Self::IntermediateShard(header) => header.is_internally_consistent(),
1624            Self::LeafShard(header) => header.is_internally_consistent(),
1625        }
1626    }
1627
1628    /// The same as [`Self::try_from_bytes()`], but for trusted input that skips some consistency
1629    /// checks
1630    #[inline]
1631    pub fn try_from_bytes_unchecked(
1632        bytes: &'a [u8],
1633        shard_kind: RealShardKind,
1634    ) -> Option<(Self, &'a [u8])> {
1635        match shard_kind {
1636            RealShardKind::BeaconChain => {
1637                let (header, remainder) = BeaconChainHeader::try_from_bytes_unchecked(bytes)?;
1638                Some((Self::BeaconChain(header), remainder))
1639            }
1640            RealShardKind::IntermediateShard => {
1641                let (header, remainder) = IntermediateShardHeader::try_from_bytes_unchecked(bytes)?;
1642                Some((Self::IntermediateShard(header), remainder))
1643            }
1644            RealShardKind::LeafShard => {
1645                let (header, remainder) = LeafShardHeader::try_from_bytes_unchecked(bytes)?;
1646                Some((Self::LeafShard(header), remainder))
1647            }
1648        }
1649    }
1650
1651    #[inline]
1652    fn try_from_bytes_shared(
1653        mut bytes: &'a [u8],
1654    ) -> Option<(
1655        &'a BlockHeaderPrefix,
1656        &'a BlockHeaderConsensusInfo,
1657        &'a BlockHeaderResult,
1658        &'a [u8],
1659    )> {
1660        let prefix = bytes.split_off(..size_of::<BlockHeaderPrefix>())?;
1661        // SAFETY: All bit patterns are valid
1662        let prefix = unsafe { BlockHeaderPrefix::from_bytes(prefix) }?;
1663
1664        if !(prefix.padding_0 == [0; _]
1665            && u32::from(prefix.shard_index) <= ShardIndex::MAX_SHARD_INDEX)
1666        {
1667            return None;
1668        }
1669
1670        let result = bytes.split_off(..size_of::<BlockHeaderResult>())?;
1671        // SAFETY: All bit patterns are valid
1672        let result = unsafe { BlockHeaderResult::from_bytes(result) }?;
1673
1674        let consensus_info = bytes.split_off(..size_of::<BlockHeaderConsensusInfo>())?;
1675        // SAFETY: All bit patterns are valid
1676        let consensus_info = unsafe { BlockHeaderConsensusInfo::from_bytes(consensus_info) }?;
1677
1678        if consensus_info.solution.padding != [0; _] {
1679            return None;
1680        }
1681
1682        Some((prefix, consensus_info, result, bytes))
1683    }
1684
1685    /// Create an owned version of this header
1686    #[cfg(feature = "alloc")]
1687    #[inline(always)]
1688    pub fn to_owned(self) -> OwnedBlockHeader {
1689        match self {
1690            Self::BeaconChain(header) => header.to_owned().into(),
1691            Self::IntermediateShard(header) => header.to_owned().into(),
1692            Self::LeafShard(header) => header.to_owned().into(),
1693        }
1694    }
1695
1696    /// Hash of the block before seal is applied to it
1697    #[inline]
1698    pub fn pre_seal_hash(&self) -> Blake3Hash {
1699        match self {
1700            Self::BeaconChain(header) => header.pre_seal_hash(),
1701            Self::IntermediateShard(header) => header.pre_seal_hash(),
1702            Self::LeafShard(header) => header.pre_seal_hash(),
1703        }
1704    }
1705
1706    /// Verify seal against [`BlockHeader::pre_seal_hash()`] and check that its public key hash
1707    /// corresponds to the solution
1708    #[inline]
1709    pub fn is_sealed_correctly(&self) -> bool {
1710        match self {
1711            Self::BeaconChain(header) => header.is_sealed_correctly(),
1712            Self::IntermediateShard(header) => header.is_sealed_correctly(),
1713            Self::LeafShard(header) => header.is_sealed_correctly(),
1714        }
1715    }
1716
1717    /// Compute block root out of this header.
1718    ///
1719    /// Block root is a Merkle Tree Root. The leaves are derived from individual fields in
1720    /// [`SharedBlockHeader`] and other fields of this enum in the declaration order.
1721    ///
1722    /// Note that this method computes root by doing a bunch of hashing. The result is then cached
1723    /// if `alloc` feature is enabled.
1724    #[inline]
1725    pub fn root(&self) -> impl Deref<Target = BlockRoot> + Send + Sync {
1726        enum Wrapper<B, I, L> {
1727            BeaconChain(B),
1728            IntermediateShard(I),
1729            LeafShard(L),
1730        }
1731
1732        impl<B, I, L> Deref for Wrapper<B, I, L>
1733        where
1734            B: Deref<Target = BlockRoot>,
1735            I: Deref<Target = BlockRoot>,
1736            L: Deref<Target = BlockRoot>,
1737        {
1738            type Target = BlockRoot;
1739
1740            #[inline(always)]
1741            fn deref(&self) -> &Self::Target {
1742                match self {
1743                    Wrapper::BeaconChain(block_root) => block_root,
1744                    Wrapper::IntermediateShard(block_root) => block_root,
1745                    Wrapper::LeafShard(block_root) => block_root,
1746                }
1747            }
1748        }
1749
1750        // TODO: Should unique keyed hash be used for different kinds of shards?
1751        match self {
1752            Self::BeaconChain(header) => Wrapper::BeaconChain(header.root()),
1753            Self::IntermediateShard(header) => Wrapper::IntermediateShard(header.root()),
1754            Self::LeafShard(header) => Wrapper::LeafShard(header.root()),
1755        }
1756    }
1757}