Skip to main content

ab_core_primitives/block/
header.rs

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