reth_primitives_traits/header/
sealed.rs

1use crate::InMemorySize;
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/// A [`Header`] that is sealed at a precalculated hash, use [`SealedHeader::unseal()`] if you want
12/// to modify header.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))]
16pub struct SealedHeader<H = Header> {
17    /// Locked Header hash.
18    hash: BlockHash,
19    /// Locked Header fields.
20    #[as_ref]
21    #[deref]
22    header: H,
23}
24
25impl<H> SealedHeader<H> {
26    /// Creates the sealed header with the corresponding block hash.
27    #[inline]
28    pub const fn new(header: H, hash: BlockHash) -> Self {
29        Self { header, hash }
30    }
31
32    /// Returns the sealed Header fields.
33    #[inline]
34    pub const fn header(&self) -> &H {
35        &self.header
36    }
37
38    /// Returns header/block hash.
39    #[inline]
40    pub const fn hash(&self) -> BlockHash {
41        self.hash
42    }
43
44    /// Extract raw header that can be modified.
45    pub fn unseal(self) -> H {
46        self.header
47    }
48
49    /// This is the inverse of [`Header::seal_slow`] which returns the raw header and hash.
50    pub fn split(self) -> (H, BlockHash) {
51        (self.header, self.hash)
52    }
53}
54
55impl<H: Sealable> SealedHeader<H> {
56    /// Hashes the header and creates a sealed header.
57    pub fn seal(header: H) -> Self {
58        let hash = header.hash_slow();
59        Self::new(header, hash)
60    }
61}
62
63impl<H: alloy_consensus::BlockHeader> SealedHeader<H> {
64    /// Return the number hash tuple.
65    pub fn num_hash(&self) -> BlockNumHash {
66        BlockNumHash::new(self.number(), self.hash)
67    }
68
69    /// Return a [`BlockWithParent`] for this header.
70    pub fn block_with_parent(&self) -> BlockWithParent {
71        BlockWithParent { parent: self.parent_hash(), block: self.num_hash() }
72    }
73}
74
75impl<H: InMemorySize> InMemorySize for SealedHeader<H> {
76    /// Calculates a heuristic for the in-memory size of the [`SealedHeader`].
77    #[inline]
78    fn size(&self) -> usize {
79        self.header.size() + mem::size_of::<BlockHash>()
80    }
81}
82
83impl<H: Sealable + Default> Default for SealedHeader<H> {
84    fn default() -> Self {
85        Self::seal(H::default())
86    }
87}
88
89impl Encodable for SealedHeader {
90    fn encode(&self, out: &mut dyn BufMut) {
91        self.header.encode(out);
92    }
93}
94
95impl Decodable for SealedHeader {
96    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
97        let b = &mut &**buf;
98        let started_len = buf.len();
99
100        // decode the header from temp buffer
101        let header = Header::decode(b)?;
102
103        // hash the consumed bytes, the rlp encoded header
104        let consumed = started_len - b.len();
105        let hash = keccak256(&buf[..consumed]);
106
107        // update original buffer
108        *buf = *b;
109
110        Ok(Self { header, hash })
111    }
112}
113
114#[cfg(any(test, feature = "test-utils"))]
115impl SealedHeader {
116    /// Updates the block header.
117    pub fn set_header(&mut self, header: Header) {
118        self.header = header
119    }
120
121    /// Updates the block hash.
122    pub fn set_hash(&mut self, hash: BlockHash) {
123        self.hash = hash
124    }
125
126    /// Updates the parent block hash.
127    pub fn set_parent_hash(&mut self, hash: BlockHash) {
128        self.header.parent_hash = hash
129    }
130
131    /// Updates the block number.
132    pub fn set_block_number(&mut self, number: alloy_primitives::BlockNumber) {
133        self.header.number = number;
134    }
135
136    /// Updates the block state root.
137    pub fn set_state_root(&mut self, state_root: alloy_primitives::B256) {
138        self.header.state_root = state_root;
139    }
140
141    /// Updates the block difficulty.
142    pub fn set_difficulty(&mut self, difficulty: alloy_primitives::U256) {
143        self.header.difficulty = difficulty;
144    }
145}
146
147impl<H> From<SealedHeader<H>> for Sealed<H> {
148    fn from(value: SealedHeader<H>) -> Self {
149        Self::new_unchecked(value.header, value.hash)
150    }
151}
152
153#[cfg(any(test, feature = "arbitrary"))]
154impl<'a, H> arbitrary::Arbitrary<'a> for SealedHeader<H>
155where
156    H: for<'b> arbitrary::Arbitrary<'b> + Sealable,
157{
158    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
159        let header = H::arbitrary(u)?;
160
161        Ok(Self::seal(header))
162    }
163}
164
165/// Bincode-compatible [`SealedHeader`] serde implementation.
166#[cfg(feature = "serde-bincode-compat")]
167pub(super) mod serde_bincode_compat {
168    use alloy_primitives::BlockHash;
169    use serde::{Deserialize, Deserializer, Serialize, Serializer};
170    use serde_with::{DeserializeAs, SerializeAs};
171
172    use crate::serde_bincode_compat::SerdeBincodeCompat;
173
174    /// Bincode-compatible [`super::SealedHeader`] serde implementation.
175    ///
176    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
177    /// ```rust
178    /// use reth_primitives_traits::{serde_bincode_compat, SealedHeader};
179    /// use serde::{Deserialize, Serialize};
180    /// use serde_with::serde_as;
181    ///
182    /// #[serde_as]
183    /// #[derive(Serialize, Deserialize)]
184    /// struct Data {
185    ///     #[serde_as(as = "serde_bincode_compat::SealedHeader")]
186    ///     header: SealedHeader,
187    /// }
188    /// ```
189    #[derive(derive_more::Debug, Serialize, Deserialize)]
190    #[debug(bound(H::BincodeRepr<'a>: core::fmt::Debug))]
191    pub struct SealedHeader<'a, H: SerdeBincodeCompat = super::Header> {
192        hash: BlockHash,
193        header: H::BincodeRepr<'a>,
194    }
195
196    impl<'a, H: SerdeBincodeCompat> From<&'a super::SealedHeader<H>> for SealedHeader<'a, H> {
197        fn from(value: &'a super::SealedHeader<H>) -> Self {
198            Self { hash: value.hash, header: (&value.header).into() }
199        }
200    }
201
202    impl<'a, H: SerdeBincodeCompat> From<SealedHeader<'a, H>> for super::SealedHeader<H> {
203        fn from(value: SealedHeader<'a, H>) -> Self {
204            Self { hash: value.hash, header: value.header.into() }
205        }
206    }
207
208    impl SerializeAs<super::SealedHeader> for SealedHeader<'_> {
209        fn serialize_as<S>(source: &super::SealedHeader, serializer: S) -> Result<S::Ok, S::Error>
210        where
211            S: Serializer,
212        {
213            SealedHeader::from(source).serialize(serializer)
214        }
215    }
216
217    impl<'de> DeserializeAs<'de, super::SealedHeader> for SealedHeader<'de> {
218        fn deserialize_as<D>(deserializer: D) -> Result<super::SealedHeader, D::Error>
219        where
220            D: Deserializer<'de>,
221        {
222            SealedHeader::deserialize(deserializer).map(Into::into)
223        }
224    }
225
226    impl<H: SerdeBincodeCompat> SerdeBincodeCompat for super::SealedHeader<H> {
227        type BincodeRepr<'a> = SealedHeader<'a, H>;
228    }
229    #[cfg(test)]
230    mod tests {
231        use super::super::{serde_bincode_compat, SealedHeader};
232        use arbitrary::Arbitrary;
233        use rand::Rng;
234        use serde::{Deserialize, Serialize};
235        use serde_with::serde_as;
236
237        #[test]
238        fn test_sealed_header_bincode_roundtrip() {
239            #[serde_as]
240            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
241            struct Data {
242                #[serde_as(as = "serde_bincode_compat::SealedHeader")]
243                transaction: SealedHeader,
244            }
245
246            let mut bytes = [0u8; 1024];
247            rand::thread_rng().fill(&mut bytes[..]);
248            let data = Data {
249                transaction: SealedHeader::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
250                    .unwrap(),
251            };
252
253            let encoded = bincode::serialize(&data).unwrap();
254            let decoded: Data = bincode::deserialize(&encoded).unwrap();
255            assert_eq!(decoded, data);
256        }
257    }
258}