1use alloy_consensus::{error::ValueError, BlockHeader, Header};
2use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, Sealed, B256, B64, U256};
3
4#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
7#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
10pub struct AnyHeader {
11 pub parent_hash: B256,
13 #[cfg_attr(feature = "serde", serde(rename = "sha3Uncles"))]
15 pub ommers_hash: B256,
16 #[cfg_attr(feature = "serde", serde(rename = "miner"))]
18 pub beneficiary: Address,
19 #[cfg_attr(feature = "serde", serde(deserialize_with = "lenient_state_root"))]
21 pub state_root: B256,
22 pub transactions_root: B256,
24 pub receipts_root: B256,
26 pub logs_bloom: Bloom,
28 pub difficulty: U256,
30 #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))]
32 pub number: u64,
33 #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity"))]
35 pub gas_limit: u64,
36 #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity"))]
38 pub gas_used: u64,
39 #[cfg_attr(feature = "serde", serde(default, with = "alloy_serde::quantity"))]
41 pub timestamp: u64,
42 pub extra_data: Bytes,
44 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
56 pub mix_hash: Option<B256>,
57 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
59 pub nonce: Option<B64>,
60 #[cfg_attr(
62 feature = "serde",
63 serde(
64 default,
65 skip_serializing_if = "Option::is_none",
66 with = "alloy_serde::quantity::opt"
67 )
68 )]
69 pub base_fee_per_gas: Option<u64>,
70 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
72 pub withdrawals_root: Option<B256>,
73 #[cfg_attr(
75 feature = "serde",
76 serde(
77 default,
78 skip_serializing_if = "Option::is_none",
79 with = "alloy_serde::quantity::opt"
80 )
81 )]
82 pub blob_gas_used: Option<u64>,
83 #[cfg_attr(
85 feature = "serde",
86 serde(
87 default,
88 skip_serializing_if = "Option::is_none",
89 with = "alloy_serde::quantity::opt"
90 )
91 )]
92 pub excess_blob_gas: Option<u64>,
93 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
95 pub parent_beacon_block_root: Option<B256>,
96 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
98 pub requests_hash: Option<B256>,
99}
100
101impl AnyHeader {
102 #[inline]
106 pub const fn seal(self, hash: B256) -> Sealed<Self> {
107 Sealed::new_unchecked(self, hash)
108 }
109
110 pub fn try_into_header(self) -> Result<Header, ValueError<Self>> {
118 if self.nonce.is_none() {
119 return Err(ValueError::new(self, "missing nonce field"));
120 }
121 if self.mix_hash.is_none() {
122 return Err(ValueError::new(self, "missing mix hash field"));
123 }
124
125 let Self {
126 parent_hash,
127 ommers_hash,
128 beneficiary,
129 state_root,
130 transactions_root,
131 receipts_root,
132 logs_bloom,
133 difficulty,
134 number,
135 gas_limit,
136 gas_used,
137 timestamp,
138 extra_data,
139 mix_hash,
140 nonce,
141 base_fee_per_gas,
142 withdrawals_root,
143 blob_gas_used,
144 excess_blob_gas,
145 parent_beacon_block_root,
146 requests_hash,
147 } = self;
148
149 Ok(Header {
150 parent_hash,
151 ommers_hash,
152 beneficiary,
153 state_root,
154 transactions_root,
155 receipts_root,
156 logs_bloom,
157 difficulty,
158 number,
159 gas_limit,
160 gas_used,
161 timestamp,
162 extra_data,
163 mix_hash: mix_hash.unwrap(),
164 nonce: nonce.unwrap(),
165 base_fee_per_gas,
166 withdrawals_root,
167 blob_gas_used,
168 excess_blob_gas,
169 parent_beacon_block_root,
170 requests_hash,
171 })
172 }
173
174 pub fn into_header_with_defaults(self) -> Header {
178 let Self {
179 parent_hash,
180 ommers_hash,
181 beneficiary,
182 state_root,
183 transactions_root,
184 receipts_root,
185 logs_bloom,
186 difficulty,
187 number,
188 gas_limit,
189 gas_used,
190 timestamp,
191 extra_data,
192 mix_hash,
193 nonce,
194 base_fee_per_gas,
195 withdrawals_root,
196 blob_gas_used,
197 excess_blob_gas,
198 parent_beacon_block_root,
199 requests_hash,
200 } = self;
201
202 Header {
203 parent_hash,
204 ommers_hash,
205 beneficiary,
206 state_root,
207 transactions_root,
208 receipts_root,
209 logs_bloom,
210 difficulty,
211 number,
212 gas_limit,
213 gas_used,
214 timestamp,
215 extra_data,
216 mix_hash: mix_hash.unwrap_or_default(),
217 nonce: nonce.unwrap_or_default(),
218 base_fee_per_gas,
219 withdrawals_root,
220 blob_gas_used,
221 excess_blob_gas,
222 parent_beacon_block_root,
223 requests_hash,
224 }
225 }
226}
227
228impl BlockHeader for AnyHeader {
229 fn parent_hash(&self) -> B256 {
230 self.parent_hash
231 }
232
233 fn ommers_hash(&self) -> B256 {
234 self.ommers_hash
235 }
236
237 fn beneficiary(&self) -> Address {
238 self.beneficiary
239 }
240
241 fn state_root(&self) -> B256 {
242 self.state_root
243 }
244
245 fn transactions_root(&self) -> B256 {
246 self.transactions_root
247 }
248
249 fn receipts_root(&self) -> B256 {
250 self.receipts_root
251 }
252
253 fn withdrawals_root(&self) -> Option<B256> {
254 self.withdrawals_root
255 }
256
257 fn logs_bloom(&self) -> Bloom {
258 self.logs_bloom
259 }
260
261 fn difficulty(&self) -> U256 {
262 self.difficulty
263 }
264
265 fn number(&self) -> BlockNumber {
266 self.number
267 }
268
269 fn gas_limit(&self) -> u64 {
270 self.gas_limit
271 }
272
273 fn gas_used(&self) -> u64 {
274 self.gas_used
275 }
276
277 fn timestamp(&self) -> u64 {
278 self.timestamp
279 }
280
281 fn mix_hash(&self) -> Option<B256> {
282 self.mix_hash
283 }
284
285 fn nonce(&self) -> Option<B64> {
286 self.nonce
287 }
288
289 fn base_fee_per_gas(&self) -> Option<u64> {
290 self.base_fee_per_gas
291 }
292
293 fn blob_gas_used(&self) -> Option<u64> {
294 self.blob_gas_used
295 }
296
297 fn excess_blob_gas(&self) -> Option<u64> {
298 self.excess_blob_gas
299 }
300
301 fn parent_beacon_block_root(&self) -> Option<B256> {
302 self.parent_beacon_block_root
303 }
304
305 fn requests_hash(&self) -> Option<B256> {
306 self.requests_hash
307 }
308
309 fn extra_data(&self) -> &Bytes {
310 &self.extra_data
311 }
312}
313
314impl From<Header> for AnyHeader {
315 fn from(value: Header) -> Self {
316 let Header {
317 parent_hash,
318 ommers_hash,
319 beneficiary,
320 state_root,
321 transactions_root,
322 receipts_root,
323 logs_bloom,
324 difficulty,
325 number,
326 gas_limit,
327 gas_used,
328 timestamp,
329 extra_data,
330 mix_hash,
331 nonce,
332 base_fee_per_gas,
333 withdrawals_root,
334 blob_gas_used,
335 excess_blob_gas,
336 parent_beacon_block_root,
337 requests_hash,
338 } = value;
339
340 Self {
341 parent_hash,
342 ommers_hash,
343 beneficiary,
344 state_root,
345 transactions_root,
346 receipts_root,
347 logs_bloom,
348 difficulty,
349 number,
350 gas_limit,
351 gas_used,
352 timestamp,
353 extra_data,
354 mix_hash: Some(mix_hash),
355 nonce: Some(nonce),
356 base_fee_per_gas,
357 withdrawals_root,
358 blob_gas_used,
359 excess_blob_gas,
360 parent_beacon_block_root,
361 requests_hash,
362 }
363 }
364}
365
366impl TryFrom<AnyHeader> for Header {
367 type Error = ValueError<AnyHeader>;
368
369 fn try_from(value: AnyHeader) -> Result<Self, Self::Error> {
370 value.try_into_header()
371 }
372}
373
374#[cfg(feature = "serde")]
378fn lenient_state_root<'de, D>(deserializer: D) -> Result<B256, D::Error>
379where
380 D: serde::de::Deserializer<'de>,
381{
382 use alloc::string::String;
383 use core::str::FromStr;
384 use serde::de::Error;
385
386 let s: String = serde::de::Deserialize::deserialize(deserializer)?;
387 let s = s.trim();
388
389 if s == "0x" || s.is_empty() {
390 return Ok(B256::ZERO);
391 }
392
393 B256::from_str(s).map_err(D::Error::custom)
394}
395
396#[cfg(test)]
397mod tests {
398
399 #[test]
401 #[cfg(feature = "serde")]
402 fn deserializes_tron_state_root_in_header() {
403 use super::*;
404 use alloy_primitives::B256;
405
406 let s = r#"{
407 "baseFeePerGas": "0x0",
408 "difficulty": "0x0",
409 "extraData": "0x",
410 "gasLimit": "0x160227b88",
411 "gasUsed": "0x360d92",
412 "hash": "0x00000000040a0687e0fc7194aabd024a4786ce94ad63855774f8d48896d8750b",
413 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
414 "miner": "0x9a96c8003a1e3a6866c08acff9f629e2a6ef062b",
415 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
416 "nonce": "0x0000000000000000",
417 "number": "0x40a0687",
418 "parentHash": "0x00000000040a068652c581a982a0d17976201ad44aa28eb4e24881e82f99ee04",
419 "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
420 "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000",
421 "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
422 "size": "0xba05",
423 "stateRoot": "0x",
424 "timestamp": "0x6759f2f1",
425 "totalDifficulty": "0x0"
426}"#;
427
428 let header: AnyHeader = serde_json::from_str(s).unwrap();
429 assert_eq!(header.state_root, B256::ZERO);
430 }
431}