alloy_rpc_types_eth/
block.rs

1//! Block RPC types.
2
3use crate::Transaction;
4use alloc::{collections::BTreeMap, vec::Vec};
5use alloy_consensus::{error::ValueError, BlockBody, BlockHeader, Sealed, TxEnvelope};
6use alloy_eips::{eip4895::Withdrawals, eip7840::BlobParams, Encodable2718};
7use alloy_network_primitives::{
8    BlockResponse, BlockTransactions, HeaderResponse, TransactionResponse,
9};
10use alloy_primitives::{Address, BlockHash, Bloom, Bytes, Sealable, B256, B64, U256};
11use alloy_rlp::Encodable;
12use core::ops::{Deref, DerefMut};
13
14pub use alloy_eips::{
15    calc_blob_gasprice, calc_excess_blob_gas, BlockHashOrNumber, BlockId, BlockNumHash,
16    BlockNumberOrTag, ForkBlock, RpcBlockHash,
17};
18
19/// Block representation for RPC.
20#[derive(Clone, Debug, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
23pub struct Block<T = Transaction<TxEnvelope>, H = Header> {
24    /// Header of the block.
25    #[cfg_attr(feature = "serde", serde(flatten))]
26    pub header: H,
27    /// Uncles' hashes.
28    #[cfg_attr(feature = "serde", serde(default))]
29    pub uncles: Vec<B256>,
30    /// Block Transactions. In the case of an uncle block, this field is not included in RPC
31    /// responses, and when deserialized, it will be set to [BlockTransactions::Uncle].
32    #[cfg_attr(
33        feature = "serde",
34        serde(
35            default = "BlockTransactions::uncle",
36            skip_serializing_if = "BlockTransactions::is_uncle"
37        )
38    )]
39    pub transactions: BlockTransactions<T>,
40    /// Withdrawals in the block.
41    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
42    pub withdrawals: Option<Withdrawals>,
43}
44
45// cannot derive, as the derive impl would constrain `where T: Default`
46impl<T, H: Default> Default for Block<T, H> {
47    fn default() -> Self {
48        Self {
49            header: Default::default(),
50            uncles: Default::default(),
51            transactions: Default::default(),
52            withdrawals: Default::default(),
53        }
54    }
55}
56
57impl<T, H> Block<T, H> {
58    /// Creates a new empty block (without transactions).
59    pub const fn empty(header: H) -> Self {
60        Self::new(header, BlockTransactions::Full(vec![]))
61    }
62
63    /// Creates a new [`Block`] with the given header and transactions.
64    ///
65    /// Note: This does not set the withdrawals for the block.
66    ///
67    /// ```
68    /// use alloy_eips::eip4895::Withdrawals;
69    /// use alloy_network_primitives::BlockTransactions;
70    /// use alloy_rpc_types_eth::{Block, Header, Transaction};
71    /// let block = Block::new(
72    ///     Header::new(alloy_consensus::Header::default()),
73    ///     BlockTransactions::<Transaction>::Full(vec![]),
74    /// )
75    /// .with_withdrawals(Some(Withdrawals::default()));
76    /// ```
77    pub const fn new(header: H, transactions: BlockTransactions<T>) -> Self {
78        Self { header, uncles: vec![], transactions, withdrawals: None }
79    }
80
81    /// Apply a function to the block, returning the modified block.
82    pub fn apply<F>(self, f: F) -> Self
83    where
84        F: FnOnce(Self) -> Self,
85    {
86        f(self)
87    }
88
89    /// Sets the transactions for the block.
90    pub fn with_transactions(mut self, transactions: BlockTransactions<T>) -> Self {
91        self.transactions = transactions;
92        self
93    }
94
95    /// Sets the withdrawals for the block.
96    pub fn with_withdrawals(mut self, withdrawals: Option<Withdrawals>) -> Self {
97        self.withdrawals = withdrawals;
98        self
99    }
100
101    /// Sets the uncles for the block.
102    pub fn with_uncles(mut self, uncles: Vec<B256>) -> Self {
103        self.uncles = uncles;
104        self
105    }
106
107    /// Tries to convert inner transactions into a vector of full transactions
108    ///
109    /// Returns an error if the block contains only transaction hashes or if it is an uncle block.
110    pub fn try_into_transactions(self) -> Result<Vec<T>, ValueError<BlockTransactions<T>>> {
111        self.transactions.try_into_transactions()
112    }
113
114    /// Consumes the type and returns the transactions as a vector.
115    ///
116    /// Note: if this is an uncle or hashes, this will return an empty vector.
117    pub fn into_transactions_vec(self) -> Vec<T> {
118        self.transactions.into_transactions_vec()
119    }
120
121    /// Converts this block into a [`BlockBody`].
122    ///
123    /// Returns an error if the transactions are not full or if the block has uncles.
124    pub fn try_into_block_body(self) -> Result<BlockBody<T, H>, ValueError<Self>> {
125        if !self.uncles.is_empty() {
126            return Err(ValueError::new_static(self, "uncles not empty"));
127        }
128        if !self.transactions.is_full() {
129            return Err(ValueError::new_static(self, "transactions not full"));
130        }
131
132        Ok(self.into_block_body_unchecked())
133    }
134
135    /// Converts this block into a [`BlockBody`]
136    ///
137    /// Caution: The body will have empty transactions unless the block's transactions are
138    /// [`BlockTransactions::Full`]. This will disregard ommers/uncles and always return an empty
139    /// ommers vec.
140    pub fn into_block_body_unchecked(self) -> BlockBody<T, H> {
141        BlockBody {
142            transactions: self.transactions.into_transactions_vec(),
143            ommers: Default::default(),
144            withdrawals: self.withdrawals,
145        }
146    }
147
148    /// Converts the block's header type by applying a function to it.
149    pub fn map_header<U>(self, f: impl FnOnce(H) -> U) -> Block<T, U> {
150        Block {
151            header: f(self.header),
152            uncles: self.uncles,
153            transactions: self.transactions,
154            withdrawals: self.withdrawals,
155        }
156    }
157
158    /// Consumes the block and only returns the rpc header.
159    ///
160    /// To obtain the underlying [`alloy_consensus::Header`] use [`Block::into_consensus_header`].
161    pub fn into_header(self) -> H {
162        self.header
163    }
164
165    /// Converts the block's header type by applying a fallible function to it.
166    pub fn try_map_header<U, E>(self, f: impl FnOnce(H) -> Result<U, E>) -> Result<Block<T, U>, E> {
167        Ok(Block {
168            header: f(self.header)?,
169            uncles: self.uncles,
170            transactions: self.transactions,
171            withdrawals: self.withdrawals,
172        })
173    }
174
175    /// Converts the block's transaction type to the given alternative that is `From<T>`
176    pub fn convert_transactions<U>(self) -> Block<U, H>
177    where
178        U: From<T>,
179    {
180        self.map_transactions(U::from)
181    }
182
183    /// Converts the block's transaction to the given alternative that is `TryFrom<T>`
184    ///
185    /// Returns the block with the new transaction type if all conversions were successful.
186    pub fn try_convert_transactions<U>(self) -> Result<Block<U, H>, U::Error>
187    where
188        U: TryFrom<T>,
189    {
190        self.try_map_transactions(U::try_from)
191    }
192
193    /// Converts the block's transaction type by applying a function to each transaction.
194    ///
195    /// Returns the block with the new transaction type.
196    pub fn map_transactions<U>(self, f: impl FnMut(T) -> U) -> Block<U, H> {
197        Block {
198            header: self.header,
199            uncles: self.uncles,
200            transactions: self.transactions.map(f),
201            withdrawals: self.withdrawals,
202        }
203    }
204
205    /// Converts the block's transaction type by applying a fallible function to each transaction.
206    ///
207    /// Returns the block with the new transaction type if all transactions were mapped
208    /// successfully.
209    pub fn try_map_transactions<U, E>(
210        self,
211        f: impl FnMut(T) -> Result<U, E>,
212    ) -> Result<Block<U, H>, E> {
213        Ok(Block {
214            header: self.header,
215            uncles: self.uncles,
216            transactions: self.transactions.try_map(f)?,
217            withdrawals: self.withdrawals,
218        })
219    }
220
221    /// Calculate the transaction root for the full transactions in this block type.
222    ///
223    /// Returns `None` if the `transactions` is not the [`BlockTransactions::Full`] variant.
224    pub fn calculate_transactions_root(&self) -> Option<B256>
225    where
226        T: Encodable2718,
227    {
228        self.transactions.calculate_transactions_root()
229    }
230}
231
232impl<T: TransactionResponse, H> Block<T, H> {
233    /// Converts a block with Tx hashes into a full block.
234    pub fn into_full_block(self, txs: Vec<T>) -> Self {
235        Self { transactions: txs.into(), ..self }
236    }
237}
238
239impl<T, H: Sealable + Encodable> Block<T, Header<H>> {
240    /// Constructs an "uncle block" from the provided header.
241    ///
242    /// This function creates a new [`Block`] structure for uncle blocks (ommer blocks),
243    /// using the provided [`alloy_consensus::Header`].
244    pub fn uncle_from_header(header: H) -> Self {
245        let block = alloy_consensus::Block::<TxEnvelope, H>::uncle(header);
246        let size = U256::from(block.length());
247        Self {
248            uncles: vec![],
249            header: Header::from_consensus(block.header.seal_slow(), None, Some(size)),
250            transactions: BlockTransactions::Uncle,
251            withdrawals: None,
252        }
253    }
254}
255
256impl<T> Block<T> {
257    /// Consumes the type and returns the sealed [`alloy_consensus::Header`].
258    pub fn into_sealed_header(self) -> Sealed<alloy_consensus::Header> {
259        self.header.into_sealed()
260    }
261
262    /// Consumes the type, strips away the rpc context from the rpc [`Header`] type and just returns
263    /// the [`alloy_consensus::Header`].
264    pub fn into_consensus_header(self) -> alloy_consensus::Header {
265        self.header.into_consensus()
266    }
267
268    /// Constructs block from a consensus block and `total_difficulty`.
269    pub fn from_consensus(block: alloy_consensus::Block<T>, total_difficulty: Option<U256>) -> Self
270    where
271        T: Encodable,
272    {
273        let size = U256::from(block.length());
274        let alloy_consensus::Block {
275            header,
276            body: alloy_consensus::BlockBody { transactions, ommers, withdrawals },
277        } = block;
278
279        Self {
280            header: Header::from_consensus(header.seal_slow(), total_difficulty, Some(size)),
281            uncles: ommers.into_iter().map(|h| h.hash_slow()).collect(),
282            transactions: BlockTransactions::Full(transactions),
283            withdrawals,
284        }
285    }
286
287    /// Consumes the block and returns the [`alloy_consensus::Block`].
288    ///
289    /// This has two caveats:
290    ///  - The returned block will always have empty uncles.
291    ///  - If the block's transaction is not [`BlockTransactions::Full`], the returned block will
292    ///    have an empty transaction vec.
293    pub fn into_consensus(self) -> alloy_consensus::Block<T> {
294        let Self { header, transactions, withdrawals, .. } = self;
295        alloy_consensus::BlockBody {
296            transactions: transactions.into_transactions_vec(),
297            ommers: vec![],
298            withdrawals,
299        }
300        .into_block(header.into_consensus())
301    }
302}
303
304/// RPC representation of block header, wrapping a consensus header.
305///
306/// This wraps the consensus header and adds additional fields for RPC.
307#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
308#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
309#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
310#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
311pub struct Header<H = alloy_consensus::Header> {
312    /// Hash of the block
313    pub hash: BlockHash,
314    /// Inner consensus header.
315    #[cfg_attr(feature = "serde", serde(flatten))]
316    pub inner: H,
317    /// Total difficulty
318    ///
319    /// Note: This field is now effectively deprecated: <https://github.com/ethereum/execution-apis/pull/570>
320    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
321    pub total_difficulty: Option<U256>,
322    /// Integer the size of this block in bytes.
323    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
324    pub size: Option<U256>,
325}
326
327impl<H> Header<H> {
328    /// Create a new [`Header`] from a consensus header.
329    ///
330    /// Note: This will compute the hash of the header.
331    pub fn new(inner: H) -> Self
332    where
333        H: Sealable,
334    {
335        Self::from_sealed(Sealed::new(inner))
336    }
337
338    /// Create a new [`Header`] from a sealed consensus header.
339    ///
340    /// Note: This does not set the total difficulty or size of the block.
341    pub fn from_sealed(header: Sealed<H>) -> Self {
342        let (inner, hash) = header.into_parts();
343        Self { hash, inner, total_difficulty: None, size: None }
344    }
345
346    /// Consumes the type and returns the [`Sealed`] header.
347    pub fn into_sealed(self) -> Sealed<H> {
348        Sealed::new_unchecked(self.inner, self.hash)
349    }
350
351    /// Consumes the type and returns the wrapped consensus header.
352    pub fn into_consensus(self) -> H {
353        self.inner
354    }
355
356    /// Create a new [`Header`] from a sealed consensus header and additional fields.
357    pub fn from_consensus(
358        header: Sealed<H>,
359        total_difficulty: Option<U256>,
360        size: Option<U256>,
361    ) -> Self {
362        let (inner, hash) = header.into_parts();
363        Self { hash, inner, total_difficulty, size }
364    }
365
366    /// Set the total difficulty of the block.
367    pub const fn with_total_difficulty(mut self, total_difficulty: Option<U256>) -> Self {
368        self.total_difficulty = total_difficulty;
369        self
370    }
371
372    /// Set the size of the block.
373    pub const fn with_size(mut self, size: Option<U256>) -> Self {
374        self.size = size;
375        self
376    }
377
378    /// Applies the given closure to the inner header.
379    #[expect(clippy::use_self)]
380    pub fn map<H1>(self, f: impl FnOnce(H) -> H1) -> Header<H1> {
381        let Header { hash, inner, total_difficulty, size } = self;
382
383        Header { hash, inner: f(inner), total_difficulty, size }
384    }
385
386    /// Applies the given fallible closure to the inner header.
387    #[expect(clippy::use_self)]
388    pub fn try_map<H1, E>(self, f: impl FnOnce(H) -> Result<H1, E>) -> Result<Header<H1>, E> {
389        let Header { hash, inner, total_difficulty, size } = self;
390
391        Ok(Header { hash, inner: f(inner)?, total_difficulty, size })
392    }
393}
394
395impl<H> Deref for Header<H> {
396    type Target = H;
397
398    fn deref(&self) -> &Self::Target {
399        &self.inner
400    }
401}
402
403impl<H> DerefMut for Header<H> {
404    fn deref_mut(&mut self) -> &mut Self::Target {
405        &mut self.inner
406    }
407}
408
409impl<H> AsRef<H> for Header<H> {
410    fn as_ref(&self) -> &H {
411        &self.inner
412    }
413}
414
415impl<H: BlockHeader> Header<H> {
416    /// Returns the blob fee for _this_ block according to the EIP-4844 spec.
417    ///
418    /// Returns `None` if `excess_blob_gas` is None
419    pub fn blob_fee(&self) -> Option<u128> {
420        self.inner.excess_blob_gas().map(calc_blob_gasprice)
421    }
422
423    /// Returns the blob fee for the next block according to the EIP-4844 spec.
424    ///
425    /// Returns `None` if `excess_blob_gas` is None.
426    ///
427    /// See also [Self::next_block_excess_blob_gas]
428    pub fn next_block_blob_fee(&self, blob_params: BlobParams) -> Option<u128> {
429        self.inner.next_block_blob_fee(blob_params)
430    }
431
432    /// Calculate excess blob gas for the next block according to the EIP-4844
433    /// spec.
434    ///
435    /// Returns a `None` if no excess blob gas is set, no EIP-4844 support
436    pub fn next_block_excess_blob_gas(&self, blob_params: BlobParams) -> Option<u64> {
437        self.inner.next_block_excess_blob_gas(blob_params)
438    }
439}
440
441impl<H: BlockHeader> BlockHeader for Header<H> {
442    fn parent_hash(&self) -> B256 {
443        self.inner.parent_hash()
444    }
445
446    fn ommers_hash(&self) -> B256 {
447        self.inner.ommers_hash()
448    }
449
450    fn beneficiary(&self) -> Address {
451        self.inner.beneficiary()
452    }
453
454    fn state_root(&self) -> B256 {
455        self.inner.state_root()
456    }
457
458    fn transactions_root(&self) -> B256 {
459        self.inner.transactions_root()
460    }
461
462    fn receipts_root(&self) -> B256 {
463        self.inner.receipts_root()
464    }
465
466    fn withdrawals_root(&self) -> Option<B256> {
467        self.inner.withdrawals_root()
468    }
469
470    fn logs_bloom(&self) -> Bloom {
471        self.inner.logs_bloom()
472    }
473
474    fn difficulty(&self) -> U256 {
475        self.inner.difficulty()
476    }
477
478    fn number(&self) -> u64 {
479        self.inner.number()
480    }
481
482    fn gas_limit(&self) -> u64 {
483        self.inner.gas_limit()
484    }
485
486    fn gas_used(&self) -> u64 {
487        self.inner.gas_used()
488    }
489
490    fn timestamp(&self) -> u64 {
491        self.inner.timestamp()
492    }
493
494    fn mix_hash(&self) -> Option<B256> {
495        self.inner.mix_hash()
496    }
497
498    fn nonce(&self) -> Option<B64> {
499        self.inner.nonce()
500    }
501
502    fn base_fee_per_gas(&self) -> Option<u64> {
503        self.inner.base_fee_per_gas()
504    }
505
506    fn blob_gas_used(&self) -> Option<u64> {
507        self.inner.blob_gas_used()
508    }
509
510    fn excess_blob_gas(&self) -> Option<u64> {
511        self.inner.excess_blob_gas()
512    }
513
514    fn parent_beacon_block_root(&self) -> Option<B256> {
515        self.inner.parent_beacon_block_root()
516    }
517
518    fn requests_hash(&self) -> Option<B256> {
519        self.inner.requests_hash()
520    }
521
522    fn extra_data(&self) -> &Bytes {
523        self.inner.extra_data()
524    }
525}
526
527impl<H: BlockHeader> HeaderResponse for Header<H> {
528    fn hash(&self) -> BlockHash {
529        self.hash
530    }
531}
532
533impl From<Header> for alloy_consensus::Header {
534    fn from(header: Header) -> Self {
535        header.into_consensus()
536    }
537}
538
539impl<H> From<Header<H>> for Sealed<H> {
540    fn from(value: Header<H>) -> Self {
541        value.into_sealed()
542    }
543}
544
545/// Error that can occur when converting other types to blocks
546#[derive(Clone, Copy, Debug, thiserror::Error)]
547pub enum BlockError {
548    /// A transaction failed sender recovery
549    #[error("transaction failed sender recovery")]
550    InvalidSignature,
551    /// A raw block failed to decode
552    #[error("failed to decode raw block {0}")]
553    RlpDecodeRawBlock(alloy_rlp::Error),
554}
555
556#[cfg(feature = "serde")]
557impl From<Block> for alloy_serde::WithOtherFields<Block> {
558    fn from(inner: Block) -> Self {
559        Self { inner, other: Default::default() }
560    }
561}
562
563#[cfg(feature = "serde")]
564impl From<Header> for alloy_serde::WithOtherFields<Header> {
565    fn from(inner: Header) -> Self {
566        Self { inner, other: Default::default() }
567    }
568}
569
570/// BlockOverrides is a set of header fields to override.
571#[derive(Clone, Debug, Default, PartialEq, Eq)]
572#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
573#[cfg_attr(feature = "serde", serde(default, rename_all = "camelCase", deny_unknown_fields))]
574pub struct BlockOverrides {
575    /// Overrides the block number.
576    ///
577    /// For `eth_callMany` this will be the block number of the first simulated block. Each
578    /// following block increments its block number by 1
579    // Note: geth uses `number`, erigon uses `blockNumber`
580    #[cfg_attr(
581        feature = "serde",
582        serde(default, skip_serializing_if = "Option::is_none", alias = "blockNumber")
583    )]
584    pub number: Option<U256>,
585    /// Overrides the difficulty of the block.
586    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
587    pub difficulty: Option<U256>,
588    /// Overrides the timestamp of the block.
589    // Note: geth uses `time`, erigon uses `timestamp`
590    #[cfg_attr(
591        feature = "serde",
592        serde(
593            default,
594            skip_serializing_if = "Option::is_none",
595            alias = "timestamp",
596            with = "alloy_serde::quantity::opt"
597        )
598    )]
599    pub time: Option<u64>,
600    /// Overrides the gas limit of the block.
601    #[cfg_attr(
602        feature = "serde",
603        serde(
604            default,
605            skip_serializing_if = "Option::is_none",
606            with = "alloy_serde::quantity::opt"
607        )
608    )]
609    pub gas_limit: Option<u64>,
610    /// Overrides the coinbase address of the block.
611    #[cfg_attr(
612        feature = "serde",
613        serde(default, skip_serializing_if = "Option::is_none", alias = "feeRecipient")
614    )]
615    pub coinbase: Option<Address>,
616    /// Overrides the prevrandao of the block.
617    #[cfg_attr(
618        feature = "serde",
619        serde(default, skip_serializing_if = "Option::is_none", alias = "prevRandao")
620    )]
621    pub random: Option<B256>,
622    /// Overrides the basefee of the block.
623    #[cfg_attr(
624        feature = "serde",
625        serde(default, skip_serializing_if = "Option::is_none", alias = "baseFeePerGas")
626    )]
627    pub base_fee: Option<U256>,
628    /// A dictionary that maps blockNumber to a user-defined hash. It can be queried from the
629    /// EVM opcode BLOCKHASH.
630    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
631    pub block_hash: Option<BTreeMap<u64, B256>>,
632}
633
634impl<T: TransactionResponse, H> BlockResponse for Block<T, H> {
635    type Header = H;
636    type Transaction = T;
637
638    fn header(&self) -> &Self::Header {
639        &self.header
640    }
641
642    fn transactions(&self) -> &BlockTransactions<T> {
643        &self.transactions
644    }
645
646    fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction> {
647        &mut self.transactions
648    }
649}
650
651/// Bad block representation.
652#[derive(Clone, Debug, Default, PartialEq, Eq)]
653#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
654#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
655pub struct BadBlock {
656    /// Underlying block object.
657    block: Block,
658    /// Hash of the block.
659    hash: BlockHash,
660    /// RLP encoded block header.
661    rlp: Bytes,
662}
663
664#[cfg(test)]
665mod tests {
666    use super::*;
667    use alloy_primitives::{hex, keccak256, Bloom, B64};
668    use arbitrary::Arbitrary;
669    use rand::Rng;
670    use similar_asserts::assert_eq;
671
672    #[test]
673    fn arbitrary_header() {
674        let mut bytes = [0u8; 1024];
675        rand::thread_rng().fill(bytes.as_mut_slice());
676        let _: Header = Header::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
677    }
678
679    #[test]
680    #[cfg(all(feature = "jsonrpsee-types", feature = "serde"))]
681    fn serde_json_header() {
682        use jsonrpsee_types::SubscriptionResponse;
683        let resp = r#"{"jsonrpc":"2.0","method":"eth_subscribe","params":{"subscription":"0x7eef37ff35d471f8825b1c8f67a5d3c0","result":{"hash":"0x7a7ada12e140961a32395059597764416499f4178daf1917193fad7bd2cc6386","parentHash":"0xdedbd831f496e705e7f2ec3c8dcb79051040a360bf1455dbd7eb8ea6ad03b751","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","number":"0x8","gasUsed":"0x0","gasLimit":"0x1c9c380","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x642aa48f","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000"}}}"#;
684        let _header: SubscriptionResponse<'_, Header> = serde_json::from_str(resp).unwrap();
685
686        let resp = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1a14b6bdcf4542fabf71c4abee244e47","result":{"author":"0x000000568b9b5a365eaa767d42e74ed88915c204","difficulty":"0x1","extraData":"0x4e65746865726d696e6420312e392e32322d302d6463373666616366612d32308639ad8ff3d850a261f3b26bc2a55e0f3a718de0dd040a19a4ce37e7b473f2d7481448a1e1fd8fb69260825377c0478393e6055f471a5cf839467ce919a6ad2700","gasLimit":"0x7a1200","gasUsed":"0x0","hash":"0xa4856602944fdfd18c528ef93cc52a681b38d766a7e39c27a47488c8461adcb0","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x434822","parentHash":"0x1a9bdc31fc785f8a95efeeb7ae58f40f6366b8e805f47447a52335c95f4ceb49","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x261","stateRoot":"0xf38c4bf2958e541ec6df148e54ce073dc6b610f8613147ede568cb7b5c2d81ee","totalDifficulty":"0x633ebd","timestamp":"0x604726b0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}}"#;
687        let _header: SubscriptionResponse<'_, Header> = serde_json::from_str(resp).unwrap();
688    }
689
690    #[test]
691    #[cfg(feature = "serde")]
692    fn serde_block() {
693        use alloy_primitives::B64;
694
695        let block = Block {
696            header: Header {
697                hash: B256::with_last_byte(1),
698                inner: alloy_consensus::Header {
699                    parent_hash: B256::with_last_byte(2),
700                    ommers_hash: B256::with_last_byte(3),
701                    beneficiary: Address::with_last_byte(4),
702                    state_root: B256::with_last_byte(5),
703                    transactions_root: B256::with_last_byte(6),
704                    receipts_root: B256::with_last_byte(7),
705                    withdrawals_root: Some(B256::with_last_byte(8)),
706                    number: 9,
707                    gas_used: 10,
708                    gas_limit: 11,
709                    extra_data: vec![1, 2, 3].into(),
710                    logs_bloom: Default::default(),
711                    timestamp: 12,
712                    difficulty: U256::from(13),
713                    mix_hash: B256::with_last_byte(14),
714                    nonce: B64::with_last_byte(15),
715                    base_fee_per_gas: Some(20),
716                    blob_gas_used: None,
717                    excess_blob_gas: None,
718                    parent_beacon_block_root: None,
719                    requests_hash: None,
720                },
721                total_difficulty: Some(U256::from(100000)),
722                size: None,
723            },
724            uncles: vec![B256::with_last_byte(17)],
725            transactions: vec![B256::with_last_byte(18)].into(),
726            withdrawals: Some(Default::default()),
727        };
728        let serialized = serde_json::to_string(&block).unwrap();
729        similar_asserts::assert_eq!(
730            serialized,
731            r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0xd","number":"0x9","gasLimit":"0xb","gasUsed":"0xa","timestamp":"0xc","extraData":"0x010203","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","baseFeePerGas":"0x14","withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000008","totalDifficulty":"0x186a0","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"],"withdrawals":[]}"#
732        );
733        let deserialized: Block = serde_json::from_str(&serialized).unwrap();
734        similar_asserts::assert_eq!(block, deserialized);
735    }
736
737    #[test]
738    #[cfg(feature = "serde")]
739    fn serde_uncle_block() {
740        use alloy_primitives::B64;
741
742        let block = Block {
743            header: Header {
744                hash: B256::with_last_byte(1),
745                inner: alloy_consensus::Header {
746                    parent_hash: B256::with_last_byte(2),
747                    ommers_hash: B256::with_last_byte(3),
748                    beneficiary: Address::with_last_byte(4),
749                    state_root: B256::with_last_byte(5),
750                    transactions_root: B256::with_last_byte(6),
751                    receipts_root: B256::with_last_byte(7),
752                    withdrawals_root: Some(B256::with_last_byte(8)),
753                    number: 9,
754                    gas_used: 10,
755                    gas_limit: 11,
756                    extra_data: vec![1, 2, 3].into(),
757                    logs_bloom: Default::default(),
758                    timestamp: 12,
759                    difficulty: U256::from(13),
760                    mix_hash: B256::with_last_byte(14),
761                    nonce: B64::with_last_byte(15),
762                    base_fee_per_gas: Some(20),
763                    blob_gas_used: None,
764                    excess_blob_gas: None,
765                    parent_beacon_block_root: None,
766                    requests_hash: None,
767                },
768                size: None,
769                total_difficulty: Some(U256::from(100000)),
770            },
771            uncles: vec![],
772            transactions: BlockTransactions::Uncle,
773            withdrawals: None,
774        };
775        let serialized = serde_json::to_string(&block).unwrap();
776        assert_eq!(
777            serialized,
778            r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0xd","number":"0x9","gasLimit":"0xb","gasUsed":"0xa","timestamp":"0xc","extraData":"0x010203","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","baseFeePerGas":"0x14","withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000008","totalDifficulty":"0x186a0","uncles":[]}"#
779        );
780        let deserialized: Block = serde_json::from_str(&serialized).unwrap();
781        assert_eq!(block, deserialized);
782    }
783
784    #[test]
785    #[cfg(feature = "serde")]
786    fn serde_block_with_withdrawals_set_as_none() {
787        let block = Block {
788            header: Header {
789                hash: B256::with_last_byte(1),
790                inner: alloy_consensus::Header {
791                    parent_hash: B256::with_last_byte(2),
792                    ommers_hash: B256::with_last_byte(3),
793                    beneficiary: Address::with_last_byte(4),
794                    state_root: B256::with_last_byte(5),
795                    transactions_root: B256::with_last_byte(6),
796                    receipts_root: B256::with_last_byte(7),
797                    withdrawals_root: None,
798                    number: 9,
799                    gas_used: 10,
800                    gas_limit: 11,
801                    extra_data: vec![1, 2, 3].into(),
802                    logs_bloom: Bloom::default(),
803                    timestamp: 12,
804                    difficulty: U256::from(13),
805                    mix_hash: B256::with_last_byte(14),
806                    nonce: B64::with_last_byte(15),
807                    base_fee_per_gas: Some(20),
808                    blob_gas_used: None,
809                    excess_blob_gas: None,
810                    parent_beacon_block_root: None,
811                    requests_hash: None,
812                },
813                total_difficulty: Some(U256::from(100000)),
814                size: None,
815            },
816            uncles: vec![B256::with_last_byte(17)],
817            transactions: vec![B256::with_last_byte(18)].into(),
818            withdrawals: None,
819        };
820        let serialized = serde_json::to_string(&block).unwrap();
821        assert_eq!(
822            serialized,
823            r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0xd","number":"0x9","gasLimit":"0xb","gasUsed":"0xa","timestamp":"0xc","extraData":"0x010203","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","baseFeePerGas":"0x14","totalDifficulty":"0x186a0","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"]}"#
824        );
825        let deserialized: Block = serde_json::from_str(&serialized).unwrap();
826        assert_eq!(block, deserialized);
827    }
828
829    #[test]
830    #[cfg(feature = "serde")]
831    fn block_overrides() {
832        let s = r#"{"blockNumber": "0xe39dd0"}"#;
833        let _overrides = serde_json::from_str::<BlockOverrides>(s).unwrap();
834    }
835
836    #[test]
837    #[cfg(feature = "serde")]
838    fn serde_rich_block() {
839        let s = r#"{
840    "hash": "0xb25d0e54ca0104e3ebfb5a1dcdf9528140854d609886a300946fd6750dcb19f4",
841    "parentHash": "0x9400ec9ef59689c157ac89eeed906f15ddd768f94e1575e0e27d37c241439a5d",
842    "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
843    "miner": "0x829bd824b016326a401d083b33d092293333a830",
844    "stateRoot": "0x546e330050c66d02923e7f1f3e925efaf64e4384eeecf2288f40088714a77a84",
845    "transactionsRoot": "0xd5eb3ad6d7c7a4798cc5fb14a6820073f44a941107c5d79dac60bd16325631fe",
846    "receiptsRoot": "0xb21c41cbb3439c5af25304e1405524c885e733b16203221900cb7f4b387b62f0",
847    "logsBloom": "0x1f304e641097eafae088627298685d20202004a4a59e4d8900914724e2402b028c9d596660581f361240816e82d00fa14250c9ca89840887a381efa600288283d170010ab0b2a0694c81842c2482457e0eb77c2c02554614007f42aaf3b4dc15d006a83522c86a240c06d241013258d90540c3008888d576a02c10120808520a2221110f4805200302624d22092b2c0e94e849b1e1aa80bc4cc3206f00b249d0a603ee4310216850e47c8997a20aa81fe95040a49ca5a420464600e008351d161dc00d620970b6a801535c218d0b4116099292000c08001943a225d6485528828110645b8244625a182c1a88a41087e6d039b000a180d04300d0680700a15794",
848    "difficulty": "0xc40faff9c737d",
849    "number": "0xa9a230",
850    "gasLimit": "0xbe5a66",
851    "gasUsed": "0xbe0fcc",
852    "timestamp": "0x5f93b749",
853    "totalDifficulty": "0x3dc957fd8167fb2684a",
854    "extraData": "0x7070796520e4b883e5bda9e7a59ee4bb99e9b1bc0103",
855    "mixHash": "0xd5e2b7b71fbe4ddfe552fb2377bf7cddb16bbb7e185806036cee86994c6e97fc",
856    "nonce": "0x4722f2acd35abe0f",
857    "uncles": [],
858    "transactions": [
859        "0xf435a26acc2a9ef73ac0b73632e32e29bd0e28d5c4f46a7e18ed545c93315916"
860    ],
861    "size": "0xaeb6"
862}"#;
863
864        let block = serde_json::from_str::<alloy_serde::WithOtherFields<Block>>(s).unwrap();
865        let serialized = serde_json::to_string(&block).unwrap();
866        let block2 =
867            serde_json::from_str::<alloy_serde::WithOtherFields<Block>>(&serialized).unwrap();
868        assert_eq!(block, block2);
869    }
870
871    #[test]
872    #[cfg(feature = "serde")]
873    fn serde_missing_uncles_block() {
874        let s = r#"{
875            "baseFeePerGas":"0x886b221ad",
876            "blobGasUsed":"0x0",
877            "difficulty":"0x0",
878            "excessBlobGas":"0x0",
879            "extraData":"0x6265617665726275696c642e6f7267",
880            "gasLimit":"0x1c9c380",
881            "gasUsed":"0xb0033c",
882            "hash":"0x85cdcbe36217fd57bf2c33731d8460657a7ce512401f49c9f6392c82a7ccf7ac",
883            "logsBloom":"0xc36919406572730518285284f2293101104140c0d42c4a786c892467868a8806f40159d29988002870403902413a1d04321320308da2e845438429e0012a00b419d8ccc8584a1c28f82a415d04eab8a5ae75c00d07761acf233414c08b6d9b571c06156086c70ea5186e9b989b0c2d55c0213c936805cd2ab331589c90194d070c00867549b1e1be14cb24500b0386cd901197c1ef5a00da453234fa48f3003dcaa894e3111c22b80e17f7d4388385a10720cda1140c0400f9e084ca34fc4870fb16b472340a2a6a63115a82522f506c06c2675080508834828c63defd06bc2331b4aa708906a06a560457b114248041e40179ebc05c6846c1e922125982f427",
884            "miner":"0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
885            "mixHash":"0x4c068e902990f21f92a2456fc75c59bec8be03b7f13682b6ebd27da56269beb5",
886            "nonce":"0x0000000000000000",
887            "number":"0x128c6df",
888            "parentBeaconBlockRoot":"0x2843cb9f7d001bd58816a915e685ed96a555c9aeec1217736bd83a96ebd409cc",
889            "parentHash":"0x90926e0298d418181bd20c23b332451e35fd7d696b5dcdc5a3a0a6b715f4c717",
890            "receiptsRoot":"0xd43aa19ecb03571d1b86d89d9bb980139d32f2f2ba59646cd5c1de9e80c68c90",
891            "sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
892            "size":"0xdcc3",
893            "stateRoot":"0x707875120a7103621fb4131df59904cda39de948dfda9084a1e3da44594d5404",
894            "timestamp":"0x65f5f4c3",
895            "transactionsRoot":"0x889a1c26dc42ba829dab552b779620feac231cde8a6c79af022bdc605c23a780",
896            "withdrawals":[
897               {
898                  "index":"0x24d80e6",
899                  "validatorIndex":"0x8b2b6",
900                  "address":"0x7cd1122e8e118b12ece8d25480dfeef230da17ff",
901                  "amount":"0x1161f10"
902               }
903            ],
904            "withdrawalsRoot":"0x360c33f20eeed5efbc7d08be46e58f8440af5db503e40908ef3d1eb314856ef7"
905         }"#;
906
907        let block = serde_json::from_str::<Block>(s).unwrap();
908        let serialized = serde_json::to_string(&block).unwrap();
909        let block2 = serde_json::from_str::<Block>(&serialized).unwrap();
910        assert_eq!(block, block2);
911    }
912
913    #[test]
914    #[cfg(feature = "serde")]
915    fn serde_block_containing_uncles() {
916        let s = r#"{
917            "baseFeePerGas":"0x886b221ad",
918            "blobGasUsed":"0x0",
919            "difficulty":"0x0",
920            "excessBlobGas":"0x0",
921            "extraData":"0x6265617665726275696c642e6f7267",
922            "gasLimit":"0x1c9c380",
923            "gasUsed":"0xb0033c",
924            "hash":"0x85cdcbe36217fd57bf2c33731d8460657a7ce512401f49c9f6392c82a7ccf7ac",
925            "logsBloom":"0xc36919406572730518285284f2293101104140c0d42c4a786c892467868a8806f40159d29988002870403902413a1d04321320308da2e845438429e0012a00b419d8ccc8584a1c28f82a415d04eab8a5ae75c00d07761acf233414c08b6d9b571c06156086c70ea5186e9b989b0c2d55c0213c936805cd2ab331589c90194d070c00867549b1e1be14cb24500b0386cd901197c1ef5a00da453234fa48f3003dcaa894e3111c22b80e17f7d4388385a10720cda1140c0400f9e084ca34fc4870fb16b472340a2a6a63115a82522f506c06c2675080508834828c63defd06bc2331b4aa708906a06a560457b114248041e40179ebc05c6846c1e922125982f427",
926            "miner":"0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
927            "mixHash":"0x4c068e902990f21f92a2456fc75c59bec8be03b7f13682b6ebd27da56269beb5",
928            "nonce":"0x0000000000000000",
929            "number":"0x128c6df",
930            "parentBeaconBlockRoot":"0x2843cb9f7d001bd58816a915e685ed96a555c9aeec1217736bd83a96ebd409cc",
931            "parentHash":"0x90926e0298d418181bd20c23b332451e35fd7d696b5dcdc5a3a0a6b715f4c717",
932            "receiptsRoot":"0xd43aa19ecb03571d1b86d89d9bb980139d32f2f2ba59646cd5c1de9e80c68c90",
933            "sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
934            "size":"0xdcc3",
935            "stateRoot":"0x707875120a7103621fb4131df59904cda39de948dfda9084a1e3da44594d5404",
936            "timestamp":"0x65f5f4c3",
937            "transactionsRoot":"0x889a1c26dc42ba829dab552b779620feac231cde8a6c79af022bdc605c23a780",
938            "uncles": ["0x123a1c26dc42ba829dab552b779620feac231cde8a6c79af022bdc605c23a780", "0x489a1c26dc42ba829dab552b779620feac231cde8a6c79af022bdc605c23a780"],
939            "withdrawals":[
940               {
941                  "index":"0x24d80e6",
942                  "validatorIndex":"0x8b2b6",
943                  "address":"0x7cd1122e8e118b12ece8d25480dfeef230da17ff",
944                  "amount":"0x1161f10"
945               }
946            ],
947            "withdrawalsRoot":"0x360c33f20eeed5efbc7d08be46e58f8440af5db503e40908ef3d1eb314856ef7"
948         }"#;
949
950        let block = serde_json::from_str::<Block>(s).unwrap();
951        assert_eq!(block.uncles.len(), 2);
952        let serialized = serde_json::to_string(&block).unwrap();
953        let block2 = serde_json::from_str::<Block>(&serialized).unwrap();
954        assert_eq!(block, block2);
955    }
956
957    #[test]
958    #[cfg(feature = "serde")]
959    fn serde_empty_block() {
960        let s = r#"{
961    "hash": "0xb25d0e54ca0104e3ebfb5a1dcdf9528140854d609886a300946fd6750dcb19f4",
962    "parentHash": "0x9400ec9ef59689c157ac89eeed906f15ddd768f94e1575e0e27d37c241439a5d",
963    "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
964    "miner": "0x829bd824b016326a401d083b33d092293333a830",
965    "stateRoot": "0x546e330050c66d02923e7f1f3e925efaf64e4384eeecf2288f40088714a77a84",
966    "transactionsRoot": "0xd5eb3ad6d7c7a4798cc5fb14a6820073f44a941107c5d79dac60bd16325631fe",
967    "receiptsRoot": "0xb21c41cbb3439c5af25304e1405524c885e733b16203221900cb7f4b387b62f0",
968    "logsBloom": "0x1f304e641097eafae088627298685d20202004a4a59e4d8900914724e2402b028c9d596660581f361240816e82d00fa14250c9ca89840887a381efa600288283d170010ab0b2a0694c81842c2482457e0eb77c2c02554614007f42aaf3b4dc15d006a83522c86a240c06d241013258d90540c3008888d576a02c10120808520a2221110f4805200302624d22092b2c0e94e849b1e1aa80bc4cc3206f00b249d0a603ee4310216850e47c8997a20aa81fe95040a49ca5a420464600e008351d161dc00d620970b6a801535c218d0b4116099292000c08001943a225d6485528828110645b8244625a182c1a88a41087e6d039b000a180d04300d0680700a15794",
969    "difficulty": "0xc40faff9c737d",
970    "number": "0xa9a230",
971    "gasLimit": "0xbe5a66",
972    "gasUsed": "0xbe0fcc",
973    "timestamp": "0x5f93b749",
974    "totalDifficulty": "0x3dc957fd8167fb2684a",
975    "extraData": "0x7070796520e4b883e5bda9e7a59ee4bb99e9b1bc0103",
976    "mixHash": "0xd5e2b7b71fbe4ddfe552fb2377bf7cddb16bbb7e185806036cee86994c6e97fc",
977    "nonce": "0x4722f2acd35abe0f",
978    "uncles": [],
979    "transactions": [],
980    "size": "0xaeb6"
981}"#;
982
983        let block = serde_json::from_str::<Block>(s).unwrap();
984        assert!(block.transactions.is_empty());
985        assert!(block.transactions.as_transactions().is_some());
986    }
987
988    #[test]
989    #[cfg(feature = "serde")]
990    fn recompute_block_hash() {
991        let s = r#"{
992    "hash": "0xb25d0e54ca0104e3ebfb5a1dcdf9528140854d609886a300946fd6750dcb19f4",
993    "parentHash": "0x9400ec9ef59689c157ac89eeed906f15ddd768f94e1575e0e27d37c241439a5d",
994    "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
995    "miner": "0x829bd824b016326a401d083b33d092293333a830",
996    "stateRoot": "0x546e330050c66d02923e7f1f3e925efaf64e4384eeecf2288f40088714a77a84",
997    "transactionsRoot": "0xd5eb3ad6d7c7a4798cc5fb14a6820073f44a941107c5d79dac60bd16325631fe",
998    "receiptsRoot": "0xb21c41cbb3439c5af25304e1405524c885e733b16203221900cb7f4b387b62f0",
999    "logsBloom": "0x1f304e641097eafae088627298685d20202004a4a59e4d8900914724e2402b028c9d596660581f361240816e82d00fa14250c9ca89840887a381efa600288283d170010ab0b2a0694c81842c2482457e0eb77c2c02554614007f42aaf3b4dc15d006a83522c86a240c06d241013258d90540c3008888d576a02c10120808520a2221110f4805200302624d22092b2c0e94e849b1e1aa80bc4cc3206f00b249d0a603ee4310216850e47c8997a20aa81fe95040a49ca5a420464600e008351d161dc00d620970b6a801535c218d0b4116099292000c08001943a225d6485528828110645b8244625a182c1a88a41087e6d039b000a180d04300d0680700a15794",
1000    "difficulty": "0xc40faff9c737d",
1001    "number": "0xa9a230",
1002    "gasLimit": "0xbe5a66",
1003    "gasUsed": "0xbe0fcc",
1004    "timestamp": "0x5f93b749",
1005    "totalDifficulty": "0x3dc957fd8167fb2684a",
1006    "extraData": "0x7070796520e4b883e5bda9e7a59ee4bb99e9b1bc0103",
1007    "mixHash": "0xd5e2b7b71fbe4ddfe552fb2377bf7cddb16bbb7e185806036cee86994c6e97fc",
1008    "nonce": "0x4722f2acd35abe0f",
1009    "uncles": [],
1010    "transactions": [],
1011    "size": "0xaeb6"
1012}"#;
1013        let block = serde_json::from_str::<Block>(s).unwrap();
1014        let header = block.clone().header.inner;
1015        let recomputed_hash = keccak256(alloy_rlp::encode(&header));
1016        assert_eq!(recomputed_hash, block.header.hash);
1017
1018        let s2 = r#"{
1019            "baseFeePerGas":"0x886b221ad",
1020            "blobGasUsed":"0x0",
1021            "difficulty":"0x0",
1022            "excessBlobGas":"0x0",
1023            "extraData":"0x6265617665726275696c642e6f7267",
1024            "gasLimit":"0x1c9c380",
1025            "gasUsed":"0xb0033c",
1026            "hash":"0x85cdcbe36217fd57bf2c33731d8460657a7ce512401f49c9f6392c82a7ccf7ac",
1027            "logsBloom":"0xc36919406572730518285284f2293101104140c0d42c4a786c892467868a8806f40159d29988002870403902413a1d04321320308da2e845438429e0012a00b419d8ccc8584a1c28f82a415d04eab8a5ae75c00d07761acf233414c08b6d9b571c06156086c70ea5186e9b989b0c2d55c0213c936805cd2ab331589c90194d070c00867549b1e1be14cb24500b0386cd901197c1ef5a00da453234fa48f3003dcaa894e3111c22b80e17f7d4388385a10720cda1140c0400f9e084ca34fc4870fb16b472340a2a6a63115a82522f506c06c2675080508834828c63defd06bc2331b4aa708906a06a560457b114248041e40179ebc05c6846c1e922125982f427",
1028            "miner":"0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
1029            "mixHash":"0x4c068e902990f21f92a2456fc75c59bec8be03b7f13682b6ebd27da56269beb5",
1030            "nonce":"0x0000000000000000",
1031            "number":"0x128c6df",
1032            "parentBeaconBlockRoot":"0x2843cb9f7d001bd58816a915e685ed96a555c9aeec1217736bd83a96ebd409cc",
1033            "parentHash":"0x90926e0298d418181bd20c23b332451e35fd7d696b5dcdc5a3a0a6b715f4c717",
1034            "receiptsRoot":"0xd43aa19ecb03571d1b86d89d9bb980139d32f2f2ba59646cd5c1de9e80c68c90",
1035            "sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
1036            "size":"0xdcc3",
1037            "stateRoot":"0x707875120a7103621fb4131df59904cda39de948dfda9084a1e3da44594d5404",
1038            "timestamp":"0x65f5f4c3",
1039            "transactionsRoot":"0x889a1c26dc42ba829dab552b779620feac231cde8a6c79af022bdc605c23a780",
1040            "withdrawals":[
1041               {
1042                  "index":"0x24d80e6",
1043                  "validatorIndex":"0x8b2b6",
1044                  "address":"0x7cd1122e8e118b12ece8d25480dfeef230da17ff",
1045                  "amount":"0x1161f10"
1046               }
1047            ],
1048            "withdrawalsRoot":"0x360c33f20eeed5efbc7d08be46e58f8440af5db503e40908ef3d1eb314856ef7"
1049         }"#;
1050        let block2 = serde_json::from_str::<Block>(s2).unwrap();
1051        let header = block2.clone().header.inner;
1052        let recomputed_hash = keccak256(alloy_rlp::encode(&header));
1053        assert_eq!(recomputed_hash, block2.header.hash);
1054    }
1055
1056    #[test]
1057    fn header_roundtrip_conversion() {
1058        // Setup a RPC header
1059        let rpc_header = Header {
1060            hash: B256::with_last_byte(1),
1061            inner: alloy_consensus::Header {
1062                parent_hash: B256::with_last_byte(2),
1063                ommers_hash: B256::with_last_byte(3),
1064                beneficiary: Address::with_last_byte(4),
1065                state_root: B256::with_last_byte(5),
1066                transactions_root: B256::with_last_byte(6),
1067                receipts_root: B256::with_last_byte(7),
1068                withdrawals_root: None,
1069                number: 9,
1070                gas_used: 10,
1071                gas_limit: 11,
1072                extra_data: vec![1, 2, 3].into(),
1073                logs_bloom: Bloom::default(),
1074                timestamp: 12,
1075                difficulty: U256::from(13),
1076                mix_hash: B256::with_last_byte(14),
1077                nonce: B64::with_last_byte(15),
1078                base_fee_per_gas: Some(20),
1079                blob_gas_used: None,
1080                excess_blob_gas: None,
1081                parent_beacon_block_root: None,
1082                requests_hash: None,
1083            },
1084            size: None,
1085            total_difficulty: None,
1086        };
1087
1088        // Convert the RPC header to a primitive header
1089        let primitive_header = rpc_header.clone().inner;
1090
1091        // Seal the primitive header
1092        let sealed_header: Sealed<alloy_consensus::Header> =
1093            primitive_header.seal(B256::with_last_byte(1));
1094
1095        // Convert the sealed header back to a RPC header
1096        let roundtrip_rpc_header = Header::from_consensus(sealed_header, None, None);
1097
1098        // Ensure the roundtrip conversion is correct
1099        assert_eq!(rpc_header, roundtrip_rpc_header);
1100    }
1101
1102    #[test]
1103    fn test_consensus_header_to_rpc_block() {
1104        // Setup a RPC header
1105        let header = Header {
1106            hash: B256::with_last_byte(1),
1107            inner: alloy_consensus::Header {
1108                parent_hash: B256::with_last_byte(2),
1109                ommers_hash: B256::with_last_byte(3),
1110                beneficiary: Address::with_last_byte(4),
1111                state_root: B256::with_last_byte(5),
1112                transactions_root: B256::with_last_byte(6),
1113                receipts_root: B256::with_last_byte(7),
1114                withdrawals_root: None,
1115                number: 9,
1116                gas_used: 10,
1117                gas_limit: 11,
1118                extra_data: vec![1, 2, 3].into(),
1119                logs_bloom: Bloom::default(),
1120                timestamp: 12,
1121                difficulty: U256::from(13),
1122                mix_hash: B256::with_last_byte(14),
1123                nonce: B64::with_last_byte(15),
1124                base_fee_per_gas: Some(20),
1125                blob_gas_used: None,
1126                excess_blob_gas: None,
1127                parent_beacon_block_root: None,
1128                requests_hash: None,
1129            },
1130            total_difficulty: None,
1131            size: Some(U256::from(505)),
1132        };
1133
1134        // Convert the RPC header to a primitive header
1135        let primitive_header = header.clone().inner;
1136
1137        // Convert the primitive header to a RPC uncle block
1138        let block: Block<Transaction> = Block::uncle_from_header(primitive_header);
1139
1140        // Ensure the block is correct
1141        assert_eq!(
1142            block,
1143            Block {
1144                header: Header {
1145                    hash: B256::from(hex!(
1146                        "379bd1414cf69a9b86fb4e0e6b05a2e4b14cb3d5af057e13ccdc2192cb9780b2"
1147                    )),
1148                    ..header
1149                },
1150                uncles: vec![],
1151                transactions: BlockTransactions::Uncle,
1152                withdrawals: None,
1153            }
1154        );
1155    }
1156
1157    #[test]
1158    #[cfg(feature = "serde")]
1159    fn serde_bad_block() {
1160        use alloy_primitives::B64;
1161
1162        let block = Block {
1163            header: Header {
1164                hash: B256::with_last_byte(1),
1165                inner: alloy_consensus::Header {
1166                    parent_hash: B256::with_last_byte(2),
1167                    ommers_hash: B256::with_last_byte(3),
1168                    beneficiary: Address::with_last_byte(4),
1169                    state_root: B256::with_last_byte(5),
1170                    transactions_root: B256::with_last_byte(6),
1171                    receipts_root: B256::with_last_byte(7),
1172                    withdrawals_root: Some(B256::with_last_byte(8)),
1173                    number: 9,
1174                    gas_used: 10,
1175                    gas_limit: 11,
1176                    extra_data: vec![1, 2, 3].into(),
1177                    logs_bloom: Default::default(),
1178                    timestamp: 12,
1179                    difficulty: U256::from(13),
1180                    mix_hash: B256::with_last_byte(14),
1181                    nonce: B64::with_last_byte(15),
1182                    base_fee_per_gas: Some(20),
1183                    blob_gas_used: None,
1184                    excess_blob_gas: None,
1185                    parent_beacon_block_root: None,
1186                    requests_hash: None,
1187                },
1188                total_difficulty: Some(U256::from(100000)),
1189                size: Some(U256::from(19)),
1190            },
1191            uncles: vec![B256::with_last_byte(17)],
1192            transactions: vec![B256::with_last_byte(18)].into(),
1193            withdrawals: Some(Default::default()),
1194        };
1195        let hash = block.header.hash;
1196        let rlp = Bytes::from("header");
1197
1198        let bad_block = BadBlock { block, hash, rlp };
1199
1200        let serialized = serde_json::to_string(&bad_block).unwrap();
1201        assert_eq!(
1202            serialized,
1203            r#"{"block":{"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000002","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000003","miner":"0x0000000000000000000000000000000000000004","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000006","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000007","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0xd","number":"0x9","gasLimit":"0xb","gasUsed":"0xa","timestamp":"0xc","extraData":"0x010203","mixHash":"0x000000000000000000000000000000000000000000000000000000000000000e","nonce":"0x000000000000000f","baseFeePerGas":"0x14","withdrawalsRoot":"0x0000000000000000000000000000000000000000000000000000000000000008","totalDifficulty":"0x186a0","size":"0x13","uncles":["0x0000000000000000000000000000000000000000000000000000000000000011"],"transactions":["0x0000000000000000000000000000000000000000000000000000000000000012"],"withdrawals":[]},"hash":"0x0000000000000000000000000000000000000000000000000000000000000001","rlp":"0x686561646572"}"#
1204        );
1205
1206        let deserialized: BadBlock = serde_json::from_str(&serialized).unwrap();
1207        similar_asserts::assert_eq!(bad_block, deserialized);
1208    }
1209
1210    // <https://github.com/succinctlabs/kona/issues/31>
1211    #[test]
1212    #[cfg(feature = "serde")]
1213    fn deserde_tenderly_block() {
1214        let s = include_str!("../testdata/tenderly.sepolia.json");
1215        let _block: Block = serde_json::from_str(s).unwrap();
1216    }
1217}