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