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 [`Header::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 state root.
232    pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
233        self.header.set_state_root(state_root);
234    }
235
236    /// Updates the block difficulty.
237    pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
238        self.header.set_difficulty(difficulty);
239    }
240}
241
242/// Bincode-compatible [`SealedHeader`] serde implementation.
243#[cfg(feature = "serde-bincode-compat")]
244pub(super) mod serde_bincode_compat {
245    use crate::serde_bincode_compat::SerdeBincodeCompat;
246    use alloy_primitives::{BlockHash, Sealable};
247    use serde::{Deserialize, Deserializer, Serialize, Serializer};
248    use serde_with::{DeserializeAs, SerializeAs};
249
250    /// Bincode-compatible [`super::SealedHeader`] serde implementation.
251    ///
252    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
253    /// ```rust
254    /// use reth_primitives_traits::{serde_bincode_compat, SealedHeader};
255    /// use serde::{Deserialize, Serialize};
256    /// use serde_with::serde_as;
257    ///
258    /// #[serde_as]
259    /// #[derive(Serialize, Deserialize)]
260    /// struct Data {
261    ///     #[serde_as(as = "serde_bincode_compat::SealedHeader")]
262    ///     header: SealedHeader,
263    /// }
264    /// ```
265    #[derive(derive_more::Debug, Serialize, Deserialize)]
266    #[debug(bound(H::BincodeRepr<'a>: core::fmt::Debug))]
267    pub struct SealedHeader<'a, H: Sealable + SerdeBincodeCompat = super::Header> {
268        hash: BlockHash,
269        header: H::BincodeRepr<'a>,
270    }
271
272    impl<'a, H: Sealable + SerdeBincodeCompat> From<&'a super::SealedHeader<H>>
273        for SealedHeader<'a, H>
274    {
275        fn from(value: &'a super::SealedHeader<H>) -> Self {
276            Self { hash: value.hash(), header: value.header.as_repr() }
277        }
278    }
279
280    impl<'a, H: Sealable + SerdeBincodeCompat> From<SealedHeader<'a, H>> for super::SealedHeader<H> {
281        fn from(value: SealedHeader<'a, H>) -> Self {
282            Self::new(SerdeBincodeCompat::from_repr(value.header), value.hash)
283        }
284    }
285
286    impl SerializeAs<super::SealedHeader> for SealedHeader<'_> {
287        fn serialize_as<S>(source: &super::SealedHeader, serializer: S) -> Result<S::Ok, S::Error>
288        where
289            S: Serializer,
290        {
291            SealedHeader::from(source).serialize(serializer)
292        }
293    }
294
295    impl<'de> DeserializeAs<'de, super::SealedHeader> for SealedHeader<'de> {
296        fn deserialize_as<D>(deserializer: D) -> Result<super::SealedHeader, D::Error>
297        where
298            D: Deserializer<'de>,
299        {
300            SealedHeader::deserialize(deserializer).map(Into::into)
301        }
302    }
303
304    impl<H: Sealable + SerdeBincodeCompat> SerdeBincodeCompat for super::SealedHeader<H> {
305        type BincodeRepr<'a> = SealedHeader<'a, H>;
306        fn as_repr(&self) -> Self::BincodeRepr<'_> {
307            self.into()
308        }
309
310        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
311            repr.into()
312        }
313    }
314
315    #[cfg(test)]
316    mod tests {
317        use super::super::{serde_bincode_compat, SealedHeader};
318        use arbitrary::Arbitrary;
319        use rand::Rng;
320        use serde::{Deserialize, Serialize};
321        use serde_with::serde_as;
322
323        #[test]
324        fn test_sealed_header_bincode_roundtrip() {
325            #[serde_as]
326            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
327            struct Data {
328                #[serde_as(as = "serde_bincode_compat::SealedHeader")]
329                transaction: SealedHeader,
330            }
331
332            let mut bytes = [0u8; 1024];
333            rand::rng().fill(&mut bytes[..]);
334            let data = Data {
335                transaction: SealedHeader::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
336                    .unwrap(),
337            };
338
339            let encoded = bincode::serialize(&data).unwrap();
340            let decoded: Data = bincode::deserialize(&encoded).unwrap();
341            assert_eq!(decoded, data);
342        }
343    }
344}