1use alloy_consensus::{BlockHeader, Header};
2use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, 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 pub fn try_into_header(self) -> Result<Header, Self> {
110 if self.nonce.is_none() || self.mix_hash.is_none() {
111 return Err(self);
112 }
113
114 let Self {
115 parent_hash,
116 ommers_hash,
117 beneficiary,
118 state_root,
119 transactions_root,
120 receipts_root,
121 logs_bloom,
122 difficulty,
123 number,
124 gas_limit,
125 gas_used,
126 timestamp,
127 extra_data,
128 mix_hash,
129 nonce,
130 base_fee_per_gas,
131 withdrawals_root,
132 blob_gas_used,
133 excess_blob_gas,
134 parent_beacon_block_root,
135 requests_hash,
136 } = self;
137
138 Ok(Header {
139 parent_hash,
140 ommers_hash,
141 beneficiary,
142 state_root,
143 transactions_root,
144 receipts_root,
145 logs_bloom,
146 difficulty,
147 number,
148 gas_limit,
149 gas_used,
150 timestamp,
151 extra_data,
152 mix_hash: mix_hash.unwrap(),
153 nonce: nonce.unwrap(),
154 base_fee_per_gas,
155 withdrawals_root,
156 blob_gas_used,
157 excess_blob_gas,
158 parent_beacon_block_root,
159 requests_hash,
160 })
161 }
162
163 pub fn into_header_with_defaults(self) -> Header {
167 let Self {
168 parent_hash,
169 ommers_hash,
170 beneficiary,
171 state_root,
172 transactions_root,
173 receipts_root,
174 logs_bloom,
175 difficulty,
176 number,
177 gas_limit,
178 gas_used,
179 timestamp,
180 extra_data,
181 mix_hash,
182 nonce,
183 base_fee_per_gas,
184 withdrawals_root,
185 blob_gas_used,
186 excess_blob_gas,
187 parent_beacon_block_root,
188 requests_hash,
189 } = self;
190
191 Header {
192 parent_hash,
193 ommers_hash,
194 beneficiary,
195 state_root,
196 transactions_root,
197 receipts_root,
198 logs_bloom,
199 difficulty,
200 number,
201 gas_limit,
202 gas_used,
203 timestamp,
204 extra_data,
205 mix_hash: mix_hash.unwrap_or_default(),
206 nonce: nonce.unwrap_or_default(),
207 base_fee_per_gas,
208 withdrawals_root,
209 blob_gas_used,
210 excess_blob_gas,
211 parent_beacon_block_root,
212 requests_hash,
213 }
214 }
215}
216
217impl BlockHeader for AnyHeader {
218 fn parent_hash(&self) -> B256 {
219 self.parent_hash
220 }
221
222 fn ommers_hash(&self) -> B256 {
223 self.ommers_hash
224 }
225
226 fn beneficiary(&self) -> Address {
227 self.beneficiary
228 }
229
230 fn state_root(&self) -> B256 {
231 self.state_root
232 }
233
234 fn transactions_root(&self) -> B256 {
235 self.transactions_root
236 }
237
238 fn receipts_root(&self) -> B256 {
239 self.receipts_root
240 }
241
242 fn withdrawals_root(&self) -> Option<B256> {
243 self.withdrawals_root
244 }
245
246 fn logs_bloom(&self) -> Bloom {
247 self.logs_bloom
248 }
249
250 fn difficulty(&self) -> U256 {
251 self.difficulty
252 }
253
254 fn number(&self) -> BlockNumber {
255 self.number
256 }
257
258 fn gas_limit(&self) -> u64 {
259 self.gas_limit
260 }
261
262 fn gas_used(&self) -> u64 {
263 self.gas_used
264 }
265
266 fn timestamp(&self) -> u64 {
267 self.timestamp
268 }
269
270 fn mix_hash(&self) -> Option<B256> {
271 self.mix_hash
272 }
273
274 fn nonce(&self) -> Option<B64> {
275 self.nonce
276 }
277
278 fn base_fee_per_gas(&self) -> Option<u64> {
279 self.base_fee_per_gas
280 }
281
282 fn blob_gas_used(&self) -> Option<u64> {
283 self.blob_gas_used
284 }
285
286 fn excess_blob_gas(&self) -> Option<u64> {
287 self.excess_blob_gas
288 }
289
290 fn parent_beacon_block_root(&self) -> Option<B256> {
291 self.parent_beacon_block_root
292 }
293
294 fn requests_hash(&self) -> Option<B256> {
295 self.requests_hash
296 }
297
298 fn extra_data(&self) -> &Bytes {
299 &self.extra_data
300 }
301}
302
303impl From<Header> for AnyHeader {
304 fn from(value: Header) -> Self {
305 let Header {
306 parent_hash,
307 ommers_hash,
308 beneficiary,
309 state_root,
310 transactions_root,
311 receipts_root,
312 logs_bloom,
313 difficulty,
314 number,
315 gas_limit,
316 gas_used,
317 timestamp,
318 extra_data,
319 mix_hash,
320 nonce,
321 base_fee_per_gas,
322 withdrawals_root,
323 blob_gas_used,
324 excess_blob_gas,
325 parent_beacon_block_root,
326 requests_hash,
327 } = value;
328
329 Self {
330 parent_hash,
331 ommers_hash,
332 beneficiary,
333 state_root,
334 transactions_root,
335 receipts_root,
336 logs_bloom,
337 difficulty,
338 number,
339 gas_limit,
340 gas_used,
341 timestamp,
342 extra_data,
343 mix_hash: Some(mix_hash),
344 nonce: Some(nonce),
345 base_fee_per_gas,
346 withdrawals_root,
347 blob_gas_used,
348 excess_blob_gas,
349 parent_beacon_block_root,
350 requests_hash,
351 }
352 }
353}
354
355impl TryFrom<AnyHeader> for Header {
356 type Error = AnyHeader;
357
358 fn try_from(value: AnyHeader) -> Result<Self, Self::Error> {
359 value.try_into_header()
360 }
361}
362
363#[cfg(feature = "serde")]
367fn lenient_state_root<'de, D>(deserializer: D) -> Result<B256, D::Error>
368where
369 D: serde::de::Deserializer<'de>,
370{
371 use alloc::string::String;
372 use core::str::FromStr;
373 use serde::de::Error;
374
375 let s: String = serde::de::Deserialize::deserialize(deserializer)?;
376 let s = s.trim();
377
378 if s == "0x" || s.is_empty() {
379 return Ok(B256::ZERO);
380 }
381
382 B256::from_str(s).map_err(D::Error::custom)
383}
384
385#[cfg(test)]
386mod tests {
387
388 #[test]
390 #[cfg(feature = "serde")]
391 fn deserializes_tron_state_root_in_header() {
392 use super::*;
393 use alloy_primitives::B256;
394
395 let s = r#"{
396 "baseFeePerGas": "0x0",
397 "difficulty": "0x0",
398 "extraData": "0x",
399 "gasLimit": "0x160227b88",
400 "gasUsed": "0x360d92",
401 "hash": "0x00000000040a0687e0fc7194aabd024a4786ce94ad63855774f8d48896d8750b",
402 "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
403 "miner": "0x9a96c8003a1e3a6866c08acff9f629e2a6ef062b",
404 "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
405 "nonce": "0x0000000000000000",
406 "number": "0x40a0687",
407 "parentHash": "0x00000000040a068652c581a982a0d17976201ad44aa28eb4e24881e82f99ee04",
408 "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
409 "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000",
410 "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
411 "size": "0xba05",
412 "stateRoot": "0x",
413 "timestamp": "0x6759f2f1",
414 "totalDifficulty": "0x0"
415}"#;
416
417 let header: AnyHeader = serde_json::from_str(s).unwrap();
418 assert_eq!(header.state_root, B256::ZERO);
419 }
420}