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(any(test, 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}
274
275/// Bincode-compatible [`SealedHeader`] serde implementation.
276#[cfg(feature = "serde-bincode-compat")]
277pub(super) mod serde_bincode_compat {
278    use crate::serde_bincode_compat::SerdeBincodeCompat;
279    use alloy_primitives::{BlockHash, Sealable};
280    use serde::{Deserialize, Deserializer, Serialize, Serializer};
281    use serde_with::{DeserializeAs, SerializeAs};
282
283    /// Bincode-compatible [`super::SealedHeader`] serde implementation.
284    ///
285    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
286    /// ```rust
287    /// use reth_primitives_traits::{serde_bincode_compat, SealedHeader};
288    /// use serde::{Deserialize, Serialize};
289    /// use serde_with::serde_as;
290    ///
291    /// #[serde_as]
292    /// #[derive(Serialize, Deserialize)]
293    /// struct Data {
294    ///     #[serde_as(as = "serde_bincode_compat::SealedHeader")]
295    ///     header: SealedHeader,
296    /// }
297    /// ```
298    #[derive(derive_more::Debug, Serialize, Deserialize)]
299    #[debug(bound(H::BincodeRepr<'a>: core::fmt::Debug))]
300    pub struct SealedHeader<'a, H: Sealable + SerdeBincodeCompat = super::Header> {
301        hash: BlockHash,
302        header: H::BincodeRepr<'a>,
303    }
304
305    impl<'a, H: Sealable + SerdeBincodeCompat> From<&'a super::SealedHeader<H>>
306        for SealedHeader<'a, H>
307    {
308        fn from(value: &'a super::SealedHeader<H>) -> Self {
309            Self { hash: value.hash(), header: value.header.as_repr() }
310        }
311    }
312
313    impl<'a, H: Sealable + SerdeBincodeCompat> From<SealedHeader<'a, H>> for super::SealedHeader<H> {
314        fn from(value: SealedHeader<'a, H>) -> Self {
315            Self::new(SerdeBincodeCompat::from_repr(value.header), value.hash)
316        }
317    }
318
319    impl SerializeAs<super::SealedHeader> for SealedHeader<'_> {
320        fn serialize_as<S>(source: &super::SealedHeader, serializer: S) -> Result<S::Ok, S::Error>
321        where
322            S: Serializer,
323        {
324            SealedHeader::from(source).serialize(serializer)
325        }
326    }
327
328    impl<'de> DeserializeAs<'de, super::SealedHeader> for SealedHeader<'de> {
329        fn deserialize_as<D>(deserializer: D) -> Result<super::SealedHeader, D::Error>
330        where
331            D: Deserializer<'de>,
332        {
333            SealedHeader::deserialize(deserializer).map(Into::into)
334        }
335    }
336
337    impl<H: Sealable + SerdeBincodeCompat> SerdeBincodeCompat for super::SealedHeader<H> {
338        type BincodeRepr<'a> = SealedHeader<'a, H>;
339        fn as_repr(&self) -> Self::BincodeRepr<'_> {
340            self.into()
341        }
342
343        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
344            repr.into()
345        }
346    }
347
348    #[cfg(test)]
349    mod tests {
350        use super::super::{serde_bincode_compat, SealedHeader};
351        use arbitrary::Arbitrary;
352        use rand::Rng;
353        use serde::{Deserialize, Serialize};
354        use serde_with::serde_as;
355
356        #[test]
357        fn test_sealed_header_bincode_roundtrip() {
358            #[serde_as]
359            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
360            struct Data {
361                #[serde_as(as = "serde_bincode_compat::SealedHeader")]
362                transaction: SealedHeader,
363            }
364
365            let mut bytes = [0u8; 1024];
366            rand::rng().fill(&mut bytes[..]);
367            let data = Data {
368                transaction: SealedHeader::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
369                    .unwrap(),
370            };
371
372            let encoded = bincode::serialize(&data).unwrap();
373            let decoded: Data = bincode::deserialize(&encoded).unwrap();
374            assert_eq!(decoded, data);
375        }
376    }
377}