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