Skip to main content

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