use crate::{sync::OnceLock, InMemorySize, NodePrimitives};
pub use alloy_consensus::Header;
use alloy_consensus::Sealed;
use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
use alloy_primitives::{keccak256, BlockHash, Sealable};
use alloy_rlp::{Decodable, Encodable};
use bytes::BufMut;
use core::mem;
use derive_more::{AsRef, Deref};
pub type SealedHeaderFor<N> = SealedHeader<<N as NodePrimitives>::BlockHeader>;
#[derive(Debug, Clone, AsRef, Deref)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "reth-codec", reth_codecs::add_arbitrary_tests(rlp))]
pub struct SealedHeader<H = Header> {
#[cfg_attr(feature = "serde", serde(skip))]
hash: OnceLock<BlockHash>,
#[as_ref]
#[deref]
header: H,
}
impl<H> SealedHeader<H> {
#[inline]
pub fn new_unhashed(header: H) -> Self {
Self { header, hash: Default::default() }
}
#[inline]
pub fn new(header: H, hash: BlockHash) -> Self {
Self { header, hash: hash.into() }
}
#[inline]
pub const fn header(&self) -> &H {
&self.header
}
pub fn clone_header(&self) -> H
where
H: Clone,
{
self.header.clone()
}
pub fn into_header(self) -> H {
self.header
}
pub fn unseal(self) -> H {
self.header
}
pub fn sealed_ref(&self) -> SealedHeader<&H> {
SealedHeader { hash: self.hash.clone(), header: &self.header }
}
}
impl<H: Sealable> SealedHeader<H> {
pub fn seal_slow(header: H) -> Self {
let hash = header.hash_slow();
Self::new(header, hash)
}
pub fn hash_ref(&self) -> &BlockHash {
self.hash.get_or_init(|| self.header.hash_slow())
}
pub fn hash(&self) -> BlockHash {
*self.hash_ref()
}
pub fn split(self) -> (H, BlockHash) {
let hash = self.hash();
(self.header, hash)
}
pub fn split_ref(&self) -> (&H, &BlockHash) {
(self.header(), self.hash_ref())
}
}
impl<H: Sealable> SealedHeader<&H> {
pub fn cloned(self) -> SealedHeader<H>
where
H: Clone,
{
let Self { hash, header } = self;
SealedHeader { hash, header: header.clone() }
}
}
impl<H: alloy_consensus::BlockHeader + Sealable> SealedHeader<H> {
pub fn num_hash(&self) -> BlockNumHash {
BlockNumHash::new(self.number(), self.hash())
}
pub fn block_with_parent(&self) -> BlockWithParent {
BlockWithParent { parent: self.parent_hash(), block: self.num_hash() }
}
}
impl<H: Sealable> Eq for SealedHeader<H> {}
impl<H: Sealable> PartialEq for SealedHeader<H> {
fn eq(&self, other: &Self) -> bool {
self.hash() == other.hash()
}
}
impl<H: Sealable> core::hash::Hash for SealedHeader<H> {
fn hash<Ha: core::hash::Hasher>(&self, state: &mut Ha) {
self.hash().hash(state)
}
}
impl<H: InMemorySize> InMemorySize for SealedHeader<H> {
#[inline]
fn size(&self) -> usize {
self.header.size() + mem::size_of::<BlockHash>()
}
}
impl<H: Sealable + Default> Default for SealedHeader<H> {
fn default() -> Self {
Self::seal_slow(H::default())
}
}
impl Encodable for SealedHeader {
fn encode(&self, out: &mut dyn BufMut) {
self.header.encode(out);
}
}
impl Decodable for SealedHeader {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let b = &mut &**buf;
let started_len = buf.len();
let header = Header::decode(b)?;
let consumed = started_len - b.len();
let hash = keccak256(&buf[..consumed]);
*buf = *b;
Ok(Self::new(header, hash))
}
}
impl<H: Sealable> From<SealedHeader<H>> for Sealed<H> {
fn from(value: SealedHeader<H>) -> Self {
let (header, hash) = value.split();
Self::new_unchecked(header, hash)
}
}
#[cfg(any(test, feature = "arbitrary"))]
impl<'a, H> arbitrary::Arbitrary<'a> for SealedHeader<H>
where
H: for<'b> arbitrary::Arbitrary<'b> + Sealable,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let header = H::arbitrary(u)?;
Ok(Self::seal_slow(header))
}
}
#[cfg(any(test, feature = "test-utils"))]
impl<H: crate::test_utils::TestHeader> SealedHeader<H> {
pub fn set_header(&mut self, header: H) {
self.header = header
}
pub fn set_hash(&mut self, hash: BlockHash) {
self.hash = hash.into()
}
pub const fn header_mut(&mut self) -> &mut H {
&mut self.header
}
pub fn set_parent_hash(&mut self, hash: BlockHash) {
self.header.set_parent_hash(hash);
}
pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
self.header.set_block_number(number);
}
pub fn set_timestamp(&mut self, timestamp: u64) {
self.header.set_timestamp(timestamp);
}
pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
self.header.set_state_root(state_root);
}
pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
self.header.set_difficulty(difficulty);
}
}
#[cfg(feature = "rpc-compat")]
mod rpc_compat {
use super::*;
impl<H> SealedHeader<H> {
pub fn into_rpc_header(self) -> alloy_rpc_types_eth::Header<H>
where
H: Sealable,
{
alloy_rpc_types_eth::Header::from_sealed(self.into())
}
pub fn from_rpc_header(header: alloy_rpc_types_eth::Header<H>) -> Self {
Self::new(header.inner, header.hash)
}
}
impl<H> From<alloy_rpc_types_eth::Header<H>> for SealedHeader<H> {
fn from(value: alloy_rpc_types_eth::Header<H>) -> Self {
Self::from_rpc_header(value)
}
}
}