Skip to main content

reth_primitives_traits/header/
sealed.rs

1use crate::{sync::OnceLock, InMemorySize, NodePrimitives};
2pub use alloy_consensus::Header;
3use alloy_consensus::Sealed;
4use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
5use alloy_primitives::{keccak256, BlockHash, Sealable};
6use alloy_rlp::{Decodable, Encodable};
7use bytes::BufMut;
8use core::mem;
9use derive_more::{AsRef, Deref};
10
11/// Type alias for [`SealedHeader`] generic over the `BlockHeader` type of [`NodePrimitives`].
12pub type SealedHeaderFor<N> = SealedHeader<<N as NodePrimitives>::BlockHeader>;
13
14/// Seals the header with the block hash.
15///
16/// This type uses lazy sealing to avoid hashing the header until it is needed:
17///
18/// [`SealedHeader::new_unhashed`] creates a sealed header without hashing the header.
19/// [`SealedHeader::new`] creates a sealed header with the corresponding block hash.
20/// [`SealedHeader::hash`] computes the hash if it has not been computed yet.
21#[derive(Debug, Clone, AsRef, Deref)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "reth-codec", reth_codecs::add_arbitrary_tests(rlp))]
24pub struct SealedHeader<H = Header> {
25    /// Block hash
26    #[cfg_attr(feature = "serde", serde(skip))]
27    hash: OnceLock<BlockHash>,
28    /// Locked Header fields.
29    #[as_ref]
30    #[deref]
31    header: H,
32}
33
34impl<H> SealedHeader<H> {
35    /// Creates the sealed header without hashing the header.
36    #[inline]
37    pub fn new_unhashed(header: H) -> Self {
38        Self { header, hash: Default::default() }
39    }
40
41    /// Creates the sealed header with the corresponding block hash.
42    #[inline]
43    pub fn new(header: H, hash: BlockHash) -> Self {
44        Self { header, hash: hash.into() }
45    }
46
47    /// Returns the sealed Header fields.
48    #[inline]
49    pub const fn header(&self) -> &H {
50        &self.header
51    }
52
53    /// Clone the header.
54    pub fn clone_header(&self) -> H
55    where
56        H: Clone,
57    {
58        self.header.clone()
59    }
60
61    /// Consumes the type and returns the wrapped header.
62    pub fn into_header(self) -> H {
63        self.header
64    }
65
66    /// Consumes the type and returns the wrapped header.
67    pub fn unseal(self) -> H {
68        self.header
69    }
70
71    /// Converts from &`SealedHeader<H>` to `SealedHeader<&H>`.
72    pub fn sealed_ref(&self) -> SealedHeader<&H> {
73        SealedHeader { hash: self.hash.clone(), header: &self.header }
74    }
75}
76
77impl<H: Sealable> SealedHeader<H> {
78    /// Hashes the header and creates a sealed header.
79    pub fn seal_slow(header: H) -> Self {
80        let hash = header.hash_slow();
81        Self::new(header, hash)
82    }
83
84    /// Returns the block hash.
85    ///
86    /// Note: if the hash has not been computed yet, this will compute the hash:
87    /// [`Sealable::hash_slow`].
88    pub fn hash_ref(&self) -> &BlockHash {
89        self.hash.get_or_init(|| self.header.hash_slow())
90    }
91
92    /// Returns a copy of the block hash.
93    pub fn hash(&self) -> BlockHash {
94        *self.hash_ref()
95    }
96
97    /// This is the inverse of [`Self::seal_slow`] which returns the raw header and hash.
98    pub fn split(self) -> (H, BlockHash) {
99        let hash = self.hash();
100        (self.header, hash)
101    }
102
103    /// Returns references to both the header and hash without taking ownership.
104    pub fn split_ref(&self) -> (&H, &BlockHash) {
105        (self.header(), self.hash_ref())
106    }
107}
108
109impl<H: Sealable> SealedHeader<&H> {
110    /// Maps a `SealedHeader<&H>` to a `SealedHeader<H>` by cloning the header.
111    pub fn cloned(self) -> SealedHeader<H>
112    where
113        H: Clone,
114    {
115        let Self { hash, header } = self;
116        SealedHeader { hash, header: header.clone() }
117    }
118}
119
120impl<H: alloy_consensus::BlockHeader + Sealable> SealedHeader<H> {
121    /// Return the number hash tuple.
122    pub fn num_hash(&self) -> BlockNumHash {
123        BlockNumHash::new(self.number(), self.hash())
124    }
125
126    /// Return a [`BlockWithParent`] for this header.
127    pub fn block_with_parent(&self) -> BlockWithParent {
128        BlockWithParent { parent: self.parent_hash(), block: self.num_hash() }
129    }
130}
131
132impl<H: Sealable> Eq for SealedHeader<H> {}
133
134impl<H: Sealable> PartialEq for SealedHeader<H> {
135    fn eq(&self, other: &Self) -> bool {
136        self.hash() == other.hash()
137    }
138}
139
140impl<H: Sealable> core::hash::Hash for SealedHeader<H> {
141    fn hash<Ha: core::hash::Hasher>(&self, state: &mut Ha) {
142        self.hash().hash(state)
143    }
144}
145
146impl<H: InMemorySize> InMemorySize for SealedHeader<H> {
147    /// Calculates a heuristic for the in-memory size of the [`SealedHeader`].
148    #[inline]
149    fn size(&self) -> usize {
150        self.header.size() + mem::size_of::<BlockHash>()
151    }
152}
153
154impl<H: Sealable + Default> Default for SealedHeader<H> {
155    fn default() -> Self {
156        Self::seal_slow(H::default())
157    }
158}
159
160impl Encodable for SealedHeader {
161    fn encode(&self, out: &mut dyn BufMut) {
162        self.header.encode(out);
163    }
164}
165
166impl Decodable for SealedHeader {
167    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
168        let b = &mut &**buf;
169        let started_len = buf.len();
170
171        // decode the header from temp buffer
172        let header = Header::decode(b)?;
173
174        // hash the consumed bytes, the rlp encoded header
175        let consumed = started_len - b.len();
176        let hash = keccak256(&buf[..consumed]);
177
178        // update original buffer
179        *buf = *b;
180
181        Ok(Self::new(header, hash))
182    }
183}
184
185impl<H: Sealable> From<SealedHeader<H>> for Sealed<H> {
186    fn from(value: SealedHeader<H>) -> Self {
187        let (header, hash) = value.split();
188        Self::new_unchecked(header, hash)
189    }
190}
191
192#[cfg(any(test, feature = "arbitrary"))]
193impl<'a, H> arbitrary::Arbitrary<'a> for SealedHeader<H>
194where
195    H: for<'b> arbitrary::Arbitrary<'b> + Sealable,
196{
197    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
198        let header = H::arbitrary(u)?;
199
200        Ok(Self::seal_slow(header))
201    }
202}
203
204#[cfg(any(test, feature = "test-utils"))]
205impl<H: crate::test_utils::TestHeader> SealedHeader<H> {
206    /// Updates the block header.
207    pub fn set_header(&mut self, header: H) {
208        self.header = header
209    }
210
211    /// Updates the block hash.
212    pub fn set_hash(&mut self, hash: BlockHash) {
213        self.hash = hash.into()
214    }
215
216    /// Returns a mutable reference to the header.
217    pub const fn header_mut(&mut self) -> &mut H {
218        &mut self.header
219    }
220
221    /// Updates the parent block hash.
222    pub fn set_parent_hash(&mut self, hash: BlockHash) {
223        self.header.set_parent_hash(hash);
224    }
225
226    /// Updates the block number.
227    pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
228        self.header.set_block_number(number);
229    }
230
231    /// Updates the block timestamp.
232    pub fn set_timestamp(&mut self, timestamp: u64) {
233        self.header.set_timestamp(timestamp);
234    }
235
236    /// Updates the block state root.
237    pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
238        self.header.set_state_root(state_root);
239    }
240
241    /// Updates the block difficulty.
242    pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
243        self.header.set_difficulty(difficulty);
244    }
245}
246
247#[cfg(feature = "rpc-compat")]
248mod rpc_compat {
249    use super::*;
250
251    impl<H> SealedHeader<H> {
252        /// Converts this header into `alloy_rpc_types_eth::Header<H>`.
253        ///
254        /// Note: This does not set the total difficulty or size of the block.
255        pub fn into_rpc_header(self) -> alloy_rpc_types_eth::Header<H>
256        where
257            H: Sealable,
258        {
259            alloy_rpc_types_eth::Header::from_sealed(self.into())
260        }
261
262        /// Converts an `alloy_rpc_types_eth::Header<H>` into a `SealedHeader<H>`.
263        pub fn from_rpc_header(header: alloy_rpc_types_eth::Header<H>) -> Self {
264            Self::new(header.inner, header.hash)
265        }
266    }
267
268    impl<H> From<alloy_rpc_types_eth::Header<H>> for SealedHeader<H> {
269        fn from(value: alloy_rpc_types_eth::Header<H>) -> Self {
270            Self::from_rpc_header(value)
271        }
272    }
273}