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