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