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
104impl<H: Sealable> SealedHeader<&H> {
105    /// Maps a `SealedHeader<&H>` to a `SealedHeader<H>` by cloning the header.
106    pub fn cloned(self) -> SealedHeader<H>
107    where
108        H: Clone,
109    {
110        let Self { hash, header } = self;
111        SealedHeader { hash, header: header.clone() }
112    }
113}
114
115impl<H: alloy_consensus::BlockHeader + Sealable> SealedHeader<H> {
116    /// Return the number hash tuple.
117    pub fn num_hash(&self) -> BlockNumHash {
118        BlockNumHash::new(self.number(), self.hash())
119    }
120
121    /// Return a [`BlockWithParent`] for this header.
122    pub fn block_with_parent(&self) -> BlockWithParent {
123        BlockWithParent { parent: self.parent_hash(), block: self.num_hash() }
124    }
125}
126
127impl<H: Sealable> Eq for SealedHeader<H> {}
128
129impl<H: Sealable> PartialEq for SealedHeader<H> {
130    fn eq(&self, other: &Self) -> bool {
131        self.hash() == other.hash()
132    }
133}
134
135impl<H: Sealable> core::hash::Hash for SealedHeader<H> {
136    fn hash<Ha: core::hash::Hasher>(&self, state: &mut Ha) {
137        self.hash().hash(state)
138    }
139}
140
141impl<H: InMemorySize> InMemorySize for SealedHeader<H> {
142    /// Calculates a heuristic for the in-memory size of the [`SealedHeader`].
143    #[inline]
144    fn size(&self) -> usize {
145        self.header.size() + mem::size_of::<BlockHash>()
146    }
147}
148
149impl<H: Sealable + Default> Default for SealedHeader<H> {
150    fn default() -> Self {
151        Self::seal_slow(H::default())
152    }
153}
154
155impl Encodable for SealedHeader {
156    fn encode(&self, out: &mut dyn BufMut) {
157        self.header.encode(out);
158    }
159}
160
161impl Decodable for SealedHeader {
162    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
163        let b = &mut &**buf;
164        let started_len = buf.len();
165
166        // decode the header from temp buffer
167        let header = Header::decode(b)?;
168
169        // hash the consumed bytes, the rlp encoded header
170        let consumed = started_len - b.len();
171        let hash = keccak256(&buf[..consumed]);
172
173        // update original buffer
174        *buf = *b;
175
176        Ok(Self::new(header, hash))
177    }
178}
179
180impl<H: Sealable> From<SealedHeader<H>> for Sealed<H> {
181    fn from(value: SealedHeader<H>) -> Self {
182        let (header, hash) = value.split();
183        Self::new_unchecked(header, hash)
184    }
185}
186
187#[cfg(any(test, feature = "arbitrary"))]
188impl<'a, H> arbitrary::Arbitrary<'a> for SealedHeader<H>
189where
190    H: for<'b> arbitrary::Arbitrary<'b> + Sealable,
191{
192    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
193        let header = H::arbitrary(u)?;
194
195        Ok(Self::seal_slow(header))
196    }
197}
198
199#[cfg(any(test, feature = "test-utils"))]
200impl<H: crate::test_utils::TestHeader> SealedHeader<H> {
201    /// Updates the block header.
202    pub fn set_header(&mut self, header: H) {
203        self.header = header
204    }
205
206    /// Updates the block hash.
207    pub fn set_hash(&mut self, hash: BlockHash) {
208        self.hash = hash.into()
209    }
210
211    /// Returns a mutable reference to the header.
212    pub fn header_mut(&mut self) -> &mut H {
213        &mut self.header
214    }
215
216    /// Updates the parent block hash.
217    pub fn set_parent_hash(&mut self, hash: BlockHash) {
218        self.header.set_parent_hash(hash);
219    }
220
221    /// Updates the block number.
222    pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
223        self.header.set_block_number(number);
224    }
225
226    /// Updates the block state root.
227    pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
228        self.header.set_state_root(state_root);
229    }
230
231    /// Updates the block difficulty.
232    pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
233        self.header.set_difficulty(difficulty);
234    }
235}
236
237/// Bincode-compatible [`SealedHeader`] serde implementation.
238#[cfg(feature = "serde-bincode-compat")]
239pub(super) mod serde_bincode_compat {
240    use crate::serde_bincode_compat::SerdeBincodeCompat;
241    use alloy_primitives::{BlockHash, Sealable};
242    use serde::{Deserialize, Deserializer, Serialize, Serializer};
243    use serde_with::{DeserializeAs, SerializeAs};
244
245    /// Bincode-compatible [`super::SealedHeader`] serde implementation.
246    ///
247    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
248    /// ```rust
249    /// use reth_primitives_traits::{serde_bincode_compat, SealedHeader};
250    /// use serde::{Deserialize, Serialize};
251    /// use serde_with::serde_as;
252    ///
253    /// #[serde_as]
254    /// #[derive(Serialize, Deserialize)]
255    /// struct Data {
256    ///     #[serde_as(as = "serde_bincode_compat::SealedHeader")]
257    ///     header: SealedHeader,
258    /// }
259    /// ```
260    #[derive(derive_more::Debug, Serialize, Deserialize)]
261    #[debug(bound(H::BincodeRepr<'a>: core::fmt::Debug))]
262    pub struct SealedHeader<'a, H: Sealable + SerdeBincodeCompat = super::Header> {
263        hash: BlockHash,
264        header: H::BincodeRepr<'a>,
265    }
266
267    impl<'a, H: Sealable + SerdeBincodeCompat> From<&'a super::SealedHeader<H>>
268        for SealedHeader<'a, H>
269    {
270        fn from(value: &'a super::SealedHeader<H>) -> Self {
271            Self { hash: value.hash(), header: value.header.as_repr() }
272        }
273    }
274
275    impl<'a, H: Sealable + SerdeBincodeCompat> From<SealedHeader<'a, H>> for super::SealedHeader<H> {
276        fn from(value: SealedHeader<'a, H>) -> Self {
277            Self::new(SerdeBincodeCompat::from_repr(value.header), value.hash)
278        }
279    }
280
281    impl SerializeAs<super::SealedHeader> for SealedHeader<'_> {
282        fn serialize_as<S>(source: &super::SealedHeader, serializer: S) -> Result<S::Ok, S::Error>
283        where
284            S: Serializer,
285        {
286            SealedHeader::from(source).serialize(serializer)
287        }
288    }
289
290    impl<'de> DeserializeAs<'de, super::SealedHeader> for SealedHeader<'de> {
291        fn deserialize_as<D>(deserializer: D) -> Result<super::SealedHeader, D::Error>
292        where
293            D: Deserializer<'de>,
294        {
295            SealedHeader::deserialize(deserializer).map(Into::into)
296        }
297    }
298
299    impl<H: Sealable + SerdeBincodeCompat> SerdeBincodeCompat for super::SealedHeader<H> {
300        type BincodeRepr<'a> = SealedHeader<'a, H>;
301        fn as_repr(&self) -> Self::BincodeRepr<'_> {
302            self.into()
303        }
304
305        fn from_repr(repr: Self::BincodeRepr<'_>) -> Self {
306            repr.into()
307        }
308    }
309
310    #[cfg(test)]
311    mod tests {
312        use super::super::{serde_bincode_compat, SealedHeader};
313        use arbitrary::Arbitrary;
314        use rand::Rng;
315        use serde::{Deserialize, Serialize};
316        use serde_with::serde_as;
317
318        #[test]
319        fn test_sealed_header_bincode_roundtrip() {
320            #[serde_as]
321            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
322            struct Data {
323                #[serde_as(as = "serde_bincode_compat::SealedHeader")]
324                transaction: SealedHeader,
325            }
326
327            let mut bytes = [0u8; 1024];
328            rand::thread_rng().fill(&mut bytes[..]);
329            let data = Data {
330                transaction: SealedHeader::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
331                    .unwrap(),
332            };
333
334            let encoded = bincode::serialize(&data).unwrap();
335            let decoded: Data = bincode::deserialize(&encoded).unwrap();
336            assert_eq!(decoded, data);
337        }
338    }
339}