Skip to main content

anvil/eth/backend/mem/
mod.rs

1//! In-memory blockchain backend.
2use self::state::trie_storage;
3use super::executor::new_eth_evm_with_inspector;
4use crate::{
5    ForkChoice, NodeConfig, PrecompileFactory,
6    config::PruneStateHistoryConfig,
7    eth::{
8        backend::{
9            cheats::{CheatEcrecover, CheatsManager},
10            db::{AnvilCacheDB, Db, MaybeFullDatabase, SerializableState, StateDb},
11            env::Env,
12            executor::{AnvilBlockExecutorFactory, AnvilExecutionCtx, build_tx_env_for_pending},
13            fork::ClientFork,
14            genesis::GenesisConfig,
15            mem::{
16                state::{storage_root, trie_accounts},
17                storage::MinedTransactionReceipt,
18            },
19            notifications::{NewBlockNotification, NewBlockNotifications},
20            time::{TimeManager, utc_from_secs},
21            validate::TransactionValidator,
22        },
23        error::{BlockchainError, ErrDetail, InvalidTransactionError},
24        fees::{FeeDetails, FeeManager, MIN_SUGGESTED_PRIORITY_FEE},
25        macros::node_info,
26        pool::transactions::PoolTransaction,
27        sign::build_impersonated,
28    },
29    mem::{
30        inspector::AnvilInspector,
31        storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
32    },
33};
34use alloy_chains::NamedChain;
35use alloy_consensus::{
36    Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait,
37    TrieAccount, TxEnvelope, TxReceipt, Typed2718,
38    constants::EMPTY_WITHDRAWALS,
39    proofs::{calculate_receipt_root, calculate_transaction_root},
40    transaction::Recovered,
41};
42use alloy_eips::{
43    BlockNumHash, Encodable2718, eip2935, eip4844::kzg_to_versioned_hash,
44    eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams, eip7910::SystemContract,
45};
46use alloy_evm::{
47    Database, Evm, FromRecoveredTx,
48    block::BlockExecutor,
49    eth::EthEvmContext,
50    overrides::{OverrideBlockHashes, apply_state_overrides},
51    precompiles::{DynPrecompile, Precompile, PrecompilesMap},
52};
53use alloy_network::{
54    AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, Network,
55    ReceiptResponse, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction,
56};
57use alloy_primitives::{
58    Address, B256, Bloom, BloomInput, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom,
59    map::{AddressMap, HashMap, HashSet},
60};
61use alloy_rpc_types::{
62    AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions,
63    EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter,
64    Header as AlloyHeader, Index, Log, Transaction, TransactionReceipt,
65    anvil::Forking,
66    request::TransactionRequest,
67    serde_helpers::JsonStorageKey,
68    simulate::{SimBlock, SimCallResult, SimulatePayload, SimulatedBlock},
69    state::EvmOverrides,
70    trace::{
71        filter::TraceFilter,
72        geth::{
73            FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
74            GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame,
75        },
76        parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType},
77    },
78};
79use alloy_serde::{OtherFields, WithOtherFields};
80use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer};
81use anvil_core::eth::{
82    block::{Block, BlockInfo, TypedBlockInfo, create_block},
83    transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
84};
85use anvil_rpc::error::RpcError;
86use chrono::Datelike;
87use eyre::{Context, Result};
88use flate2::{Compression, read::GzDecoder, write::GzEncoder};
89use foundry_evm::{
90    backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
91    constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
92    core::{either_evm::EitherEvm, precompiles::EC_RECOVER},
93    decode::RevertDecoder,
94    inspectors::AccessListInspector,
95    traces::{
96        CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
97        TracingInspectorConfig,
98    },
99    utils::{
100        block_env_from_header, get_blob_base_fee_update_fraction,
101        get_blob_base_fee_update_fraction_by_spec_id,
102    },
103};
104use foundry_primitives::{
105    FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope,
106    FoundryTxReceipt, get_deposit_tx_parts,
107};
108use futures::channel::mpsc::{UnboundedSender, unbounded};
109use op_alloy_consensus::DEPOSIT_TX_TYPE_ID;
110use op_revm::{OpContext, OpHaltReason, OpTransaction};
111use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
112use revm::{
113    Database as RevmDatabase, DatabaseCommit, Inspector,
114    context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv},
115    context_interface::{
116        block::BlobExcessGasAndPrice,
117        result::{ExecutionResult, Output, ResultAndState},
118    },
119    database::{CacheDB, DbAccount, WrapDatabaseRef},
120    interpreter::InstructionResult,
121    precompile::{PrecompileSpecId, Precompiles},
122    primitives::{KECCAK_EMPTY, hardfork::SpecId},
123    state::AccountInfo,
124};
125use std::{
126    collections::BTreeMap,
127    fmt::{self, Debug},
128    io::{Read, Write},
129    ops::{Mul, Not},
130    path::PathBuf,
131    sync::Arc,
132    time::Duration,
133};
134use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
135use tokio::sync::RwLock as AsyncRwLock;
136
137pub mod cache;
138pub mod fork_db;
139pub mod in_memory_db;
140pub mod inspector;
141pub mod state;
142pub mod storage;
143
144/// Helper trait that combines revm::DatabaseRef with Debug.
145/// This is needed because alloy-evm requires Debug on Database implementations.
146/// With trait upcasting now stable, we can now upcast from this trait to revm::DatabaseRef.
147pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
148impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
149impl DatabaseRef for dyn crate::eth::backend::db::Db {}
150
151// Gas per transaction not creating a contract.
152pub const MIN_TRANSACTION_GAS: u128 = 21000;
153// Gas per transaction creating a contract.
154pub const MIN_CREATE_GAS: u128 = 53000;
155
156pub type State = foundry_evm::utils::StateChangeset;
157
158/// A block request, which includes the Pool Transactions if it's Pending
159pub enum BlockRequest<T> {
160    Pending(Vec<Arc<PoolTransaction<T>>>),
161    Number(u64),
162}
163
164impl<T> fmt::Debug for BlockRequest<T> {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        match self {
167            Self::Pending(txs) => f.debug_tuple("Pending").field(&txs.len()).finish(),
168            Self::Number(n) => f.debug_tuple("Number").field(n).finish(),
169        }
170    }
171}
172
173impl<T> BlockRequest<T> {
174    pub fn block_number(&self) -> BlockNumber {
175        match *self {
176            Self::Pending(_) => BlockNumber::Pending,
177            Self::Number(n) => BlockNumber::Number(n),
178        }
179    }
180}
181
182/// Gives access to the [revm::Database]
183pub struct Backend<N: Network> {
184    /// Access to [`revm::Database`] abstraction.
185    ///
186    /// This will be used in combination with [`alloy_evm::Evm`] and is responsible for feeding
187    /// data to the evm during its execution.
188    ///
189    /// At time of writing, there are two different types of `Db`:
190    ///   - [`MemDb`](crate::mem::in_memory_db::MemDb): everything is stored in memory
191    ///   - [`ForkDb`](crate::mem::fork_db::ForkedDatabase): forks off a remote client, missing
192    ///     data is retrieved via RPC-calls
193    ///
194    /// In order to commit changes to the [`revm::Database`], the [`alloy_evm::Evm`] requires
195    /// mutable access, which requires a write-lock from this `db`. In forking mode, the time
196    /// during which the write-lock is active depends on whether the `ForkDb` can provide all
197    /// requested data from memory or whether it has to retrieve it via RPC calls first. This
198    /// means that it potentially blocks for some time, even taking into account the rate
199    /// limits of RPC endpoints. Therefore the `Db` is guarded by a `tokio::sync::RwLock` here
200    /// so calls that need to read from it, while it's currently written to, don't block. E.g.
201    /// a new block is currently mined and a new [`Self::set_storage_at()`] request is being
202    /// executed.
203    db: Arc<AsyncRwLock<Box<dyn Db>>>,
204    /// stores all block related data in memory.
205    blockchain: Blockchain<N>,
206    /// Historic states of previous blocks.
207    states: Arc<RwLock<InMemoryBlockStates>>,
208    /// Env data of the chain
209    env: Arc<RwLock<Env>>,
210    /// This is set if this is currently forked off another client.
211    fork: Arc<RwLock<Option<ClientFork>>>,
212    /// Provides time related info, like timestamp.
213    time: TimeManager,
214    /// Contains state of custom overrides.
215    cheats: CheatsManager,
216    /// Contains fee data.
217    fees: FeeManager,
218    /// Initialised genesis.
219    genesis: GenesisConfig,
220    /// Listeners for new blocks that get notified when a new block was imported.
221    new_block_listeners: Arc<Mutex<Vec<UnboundedSender<NewBlockNotification>>>>,
222    /// Keeps track of active state snapshots at a specific block.
223    active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
224    enable_steps_tracing: bool,
225    print_logs: bool,
226    print_traces: bool,
227    /// Recorder used for decoding traces, used together with print_traces
228    call_trace_decoder: Arc<CallTraceDecoder>,
229    /// How to keep history state
230    prune_state_history_config: PruneStateHistoryConfig,
231    /// max number of blocks with transactions in memory
232    transaction_block_keeper: Option<usize>,
233    node_config: Arc<AsyncRwLock<NodeConfig>>,
234    /// Slots in an epoch
235    slots_in_an_epoch: u64,
236    /// Precompiles to inject to the EVM.
237    precompile_factory: Option<Arc<dyn PrecompileFactory>>,
238    /// Prevent race conditions during mining
239    mining: Arc<tokio::sync::Mutex<()>>,
240    /// Disable pool balance checks
241    disable_pool_balance_checks: bool,
242}
243
244impl<N: Network> Clone for Backend<N> {
245    fn clone(&self) -> Self {
246        Self {
247            db: self.db.clone(),
248            blockchain: self.blockchain.clone(),
249            states: self.states.clone(),
250            env: self.env.clone(),
251            fork: self.fork.clone(),
252            time: self.time.clone(),
253            cheats: self.cheats.clone(),
254            fees: self.fees.clone(),
255            genesis: self.genesis.clone(),
256            new_block_listeners: self.new_block_listeners.clone(),
257            active_state_snapshots: self.active_state_snapshots.clone(),
258            enable_steps_tracing: self.enable_steps_tracing,
259            print_logs: self.print_logs,
260            print_traces: self.print_traces,
261            call_trace_decoder: self.call_trace_decoder.clone(),
262            prune_state_history_config: self.prune_state_history_config,
263            transaction_block_keeper: self.transaction_block_keeper,
264            node_config: self.node_config.clone(),
265            slots_in_an_epoch: self.slots_in_an_epoch,
266            precompile_factory: self.precompile_factory.clone(),
267            mining: self.mining.clone(),
268            disable_pool_balance_checks: self.disable_pool_balance_checks,
269        }
270    }
271}
272
273impl<N: Network> fmt::Debug for Backend<N> {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        f.debug_struct("Backend").finish_non_exhaustive()
276    }
277}
278
279// Methods that are generic over any Network.
280impl<N: Network> Backend<N> {
281    /// Sets the account to impersonate
282    ///
283    /// Returns `true` if the account is already impersonated
284    pub fn impersonate(&self, addr: Address) -> bool {
285        if self.cheats.impersonated_accounts().contains(&addr) {
286            return true;
287        }
288        // Ensure EIP-3607 is disabled
289        let mut env = self.env.write();
290        env.evm_env.cfg_env.disable_eip3607 = true;
291        self.cheats.impersonate(addr)
292    }
293
294    /// Removes the account that from the impersonated set
295    ///
296    /// If the impersonated `addr` is a contract then we also reset the code here
297    pub fn stop_impersonating(&self, addr: Address) {
298        self.cheats.stop_impersonating(&addr);
299    }
300
301    /// If set to true will make every account impersonated
302    pub fn auto_impersonate_account(&self, enabled: bool) {
303        self.cheats.set_auto_impersonate_account(enabled);
304    }
305
306    /// Returns the configured fork, if any
307    pub fn get_fork(&self) -> Option<ClientFork> {
308        self.fork.read().clone()
309    }
310
311    /// Returns the database
312    pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
313        &self.db
314    }
315
316    /// Returns the `AccountInfo` from the database
317    pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
318        Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
319    }
320
321    /// Whether we're forked off some remote client
322    pub fn is_fork(&self) -> bool {
323        self.fork.read().is_some()
324    }
325
326    /// Writes the CREATE2 deployer code directly to the database at the address provided.
327    pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
328        self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
329        Ok(())
330    }
331
332    /// Updates memory limits that should be more strict when auto-mine is enabled
333    pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
334        self.states.write().update_interval_mine_block_time(block_time)
335    }
336
337    /// Returns the `TimeManager` responsible for timestamps
338    pub fn time(&self) -> &TimeManager {
339        &self.time
340    }
341
342    /// Returns the `CheatsManager` responsible for executing cheatcodes
343    pub fn cheats(&self) -> &CheatsManager {
344        &self.cheats
345    }
346
347    /// Whether to skip blob validation
348    pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
349        self.cheats().auto_impersonate_accounts()
350            || impersonator
351                .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
352    }
353
354    /// Returns the `FeeManager` that manages fee/pricings
355    pub fn fees(&self) -> &FeeManager {
356        &self.fees
357    }
358
359    /// The env data of the blockchain
360    pub fn env(&self) -> &Arc<RwLock<Env>> {
361        &self.env
362    }
363
364    /// Returns the current best hash of the chain
365    pub fn best_hash(&self) -> B256 {
366        self.blockchain.storage.read().best_hash
367    }
368
369    /// Returns the current best number of the chain
370    pub fn best_number(&self) -> u64 {
371        self.blockchain.storage.read().best_number
372    }
373
374    /// Sets the block number
375    pub fn set_block_number(&self, number: u64) {
376        let mut env = self.env.write();
377        env.evm_env.block_env.number = U256::from(number);
378    }
379
380    /// Returns the client coinbase address.
381    pub fn coinbase(&self) -> Address {
382        self.env.read().evm_env.block_env.beneficiary
383    }
384
385    /// Returns the client coinbase address.
386    pub fn chain_id(&self) -> U256 {
387        U256::from(self.env.read().evm_env.cfg_env.chain_id)
388    }
389
390    pub fn set_chain_id(&self, chain_id: u64) {
391        self.env.write().evm_env.cfg_env.chain_id = chain_id;
392    }
393
394    /// Returns the genesis data for the Beacon API.
395    pub fn genesis_time(&self) -> u64 {
396        self.genesis.timestamp
397    }
398
399    /// Returns balance of the given account.
400    pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
401        Ok(self.get_account(address).await?.balance)
402    }
403
404    /// Returns balance of the given account.
405    pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
406        Ok(self.get_account(address).await?.nonce)
407    }
408
409    /// Sets the coinbase address
410    pub fn set_coinbase(&self, address: Address) {
411        self.env.write().evm_env.block_env.beneficiary = address;
412    }
413
414    /// Sets the nonce of the given address
415    pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
416        self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
417    }
418
419    /// Sets the balance of the given address
420    pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
421        self.db.write().await.set_balance(address, balance)
422    }
423
424    /// Sets the code of the given address
425    pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
426        self.db.write().await.set_code(address, code)
427    }
428
429    /// Sets the value for the given slot of the given address
430    pub async fn set_storage_at(
431        &self,
432        address: Address,
433        slot: U256,
434        val: B256,
435    ) -> DatabaseResult<()> {
436        self.db.write().await.set_storage_at(address, slot.into(), val)
437    }
438
439    /// Returns the configured specid
440    pub fn spec_id(&self) -> SpecId {
441        self.env.read().evm_env.cfg_env.spec
442    }
443
444    /// Returns true for post London
445    pub fn is_eip1559(&self) -> bool {
446        (self.spec_id() as u8) >= (SpecId::LONDON as u8)
447    }
448
449    /// Returns true for post Merge
450    pub fn is_eip3675(&self) -> bool {
451        (self.spec_id() as u8) >= (SpecId::MERGE as u8)
452    }
453
454    /// Returns true for post Berlin
455    pub fn is_eip2930(&self) -> bool {
456        (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
457    }
458
459    /// Returns true for post Cancun
460    pub fn is_eip4844(&self) -> bool {
461        (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
462    }
463
464    /// Returns true for post Prague
465    pub fn is_eip7702(&self) -> bool {
466        (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
467    }
468
469    /// Returns true if op-stack deposits are active
470    pub fn is_optimism(&self) -> bool {
471        self.env.read().networks.is_optimism()
472    }
473
474    /// Returns the precompiles for the current spec.
475    pub fn precompiles(&self) -> BTreeMap<String, Address> {
476        let spec_id = self.env.read().evm_env.cfg_env.spec;
477        let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id));
478
479        let mut precompiles_map = BTreeMap::<String, Address>::default();
480        for (address, precompile) in precompiles.inner() {
481            precompiles_map.insert(precompile.id().name().to_string(), *address);
482        }
483
484        // Extend with configured network precompiles.
485        precompiles_map.extend(self.env.read().networks.precompiles());
486
487        if let Some(factory) = &self.precompile_factory {
488            for (address, precompile) in factory.precompiles() {
489                precompiles_map.insert(precompile.precompile_id().to_string(), address);
490            }
491        }
492
493        precompiles_map
494    }
495
496    /// Returns the system contracts for the current spec.
497    pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
498        let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
499
500        let spec_id = self.env.read().evm_env.cfg_env.spec;
501
502        if spec_id >= SpecId::CANCUN {
503            system_contracts.extend(SystemContract::cancun());
504        }
505
506        if spec_id >= SpecId::PRAGUE {
507            system_contracts.extend(SystemContract::prague(None));
508        }
509
510        system_contracts
511    }
512
513    /// Returns [`BlobParams`] corresponding to the current spec.
514    pub fn blob_params(&self) -> BlobParams {
515        let spec_id = self.env.read().evm_env.cfg_env.spec;
516
517        if spec_id >= SpecId::OSAKA {
518            return BlobParams::osaka();
519        }
520
521        if spec_id >= SpecId::PRAGUE {
522            return BlobParams::prague();
523        }
524
525        BlobParams::cancun()
526    }
527
528    /// Returns an error if EIP1559 is not active (pre Berlin)
529    pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
530        if self.is_eip1559() {
531            return Ok(());
532        }
533        Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
534    }
535
536    /// Returns an error if EIP1559 is not active (pre muirGlacier)
537    pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
538        if self.is_eip2930() {
539            return Ok(());
540        }
541        Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
542    }
543
544    pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
545        if self.is_eip4844() {
546            return Ok(());
547        }
548        Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
549    }
550
551    pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
552        if self.is_eip7702() {
553            return Ok(());
554        }
555        Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
556    }
557
558    /// Returns an error if op-stack deposits are not active
559    pub fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> {
560        if self.is_optimism() {
561            return Ok(());
562        }
563        Err(BlockchainError::DepositTransactionUnsupported)
564    }
565
566    /// Returns the block gas limit
567    pub fn gas_limit(&self) -> u64 {
568        self.env.read().evm_env.block_env.gas_limit
569    }
570
571    /// Sets the block gas limit
572    pub fn set_gas_limit(&self, gas_limit: u64) {
573        self.env.write().evm_env.block_env.gas_limit = gas_limit;
574    }
575
576    /// Returns the current base fee
577    pub fn base_fee(&self) -> u64 {
578        self.fees.base_fee()
579    }
580
581    /// Returns whether the minimum suggested priority fee is enforced
582    pub fn is_min_priority_fee_enforced(&self) -> bool {
583        self.fees.is_min_priority_fee_enforced()
584    }
585
586    pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
587        self.fees.excess_blob_gas_and_price()
588    }
589
590    /// Sets the current basefee
591    pub fn set_base_fee(&self, basefee: u64) {
592        self.fees.set_base_fee(basefee)
593    }
594
595    /// Sets the gas price
596    pub fn set_gas_price(&self, price: u128) {
597        self.fees.set_gas_price(price)
598    }
599
600    pub fn elasticity(&self) -> f64 {
601        self.fees.elasticity()
602    }
603
604    /// Returns the total difficulty of the chain until this block
605    ///
606    /// Note: this will always be `0` in memory mode
607    /// In forking mode this will always be the total difficulty of the forked block
608    pub fn total_difficulty(&self) -> U256 {
609        self.blockchain.storage.read().total_difficulty
610    }
611
612    /// Creates a new `evm_snapshot` at the current height.
613    ///
614    /// Returns the id of the snapshot created.
615    pub async fn create_state_snapshot(&self) -> U256 {
616        let num = self.best_number();
617        let hash = self.best_hash();
618        let id = self.db.write().await.snapshot_state();
619        trace!(target: "backend", "creating snapshot {} at {}", id, num);
620        self.active_state_snapshots.lock().insert(id, (num, hash));
621        id
622    }
623
624    pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
625        self.active_state_snapshots.lock().clone().into_iter().collect()
626    }
627
628    /// Returns the environment for the next block
629    fn next_env(&self) -> Env {
630        let mut env = self.env.read().clone();
631        // increase block number for this block
632        env.evm_env.block_env.number = env.evm_env.block_env.number.saturating_add(U256::from(1));
633        env.evm_env.block_env.basefee = self.base_fee();
634        env.evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
635        env.evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp());
636        env
637    }
638
639    /// Builds [`Inspector`] with the configured options.
640    fn build_inspector(&self) -> AnvilInspector {
641        let mut inspector = AnvilInspector::default();
642
643        if self.print_logs {
644            inspector = inspector.with_log_collector();
645        }
646        if self.print_traces {
647            inspector = inspector.with_trace_printer();
648        }
649
650        inspector
651    }
652
653    /// Returns a new block event stream that yields Notifications when a new block was added
654    pub fn new_block_notifications(&self) -> NewBlockNotifications {
655        let (tx, rx) = unbounded();
656        self.new_block_listeners.lock().push(tx);
657        trace!(target: "backed", "added new block listener");
658        rx
659    }
660
661    /// Notifies all `new_block_listeners` about the new block
662    fn notify_on_new_block(&self, header: Header, hash: B256) {
663        // cleanup closed notification streams first, if the channel is closed we can remove the
664        // sender half for the set
665        self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
666
667        let notification = NewBlockNotification { hash, header: Arc::new(header) };
668
669        self.new_block_listeners
670            .lock()
671            .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
672    }
673
674    /// Returns the block number for the given block id
675    pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
676        let current = self.best_number();
677        match block.unwrap_or(BlockNumber::Latest) {
678            BlockNumber::Latest | BlockNumber::Pending => current,
679            BlockNumber::Earliest => 0,
680            BlockNumber::Number(num) => num,
681            BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
682            BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
683        }
684    }
685
686    /// Returns the block and its hash for the given id
687    fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
688        let hash = match id.into() {
689            BlockId::Hash(hash) => hash.block_hash,
690            BlockId::Number(number) => {
691                let storage = self.blockchain.storage.read();
692                let slots_in_an_epoch = self.slots_in_an_epoch;
693                match number {
694                    BlockNumber::Latest => storage.best_hash,
695                    BlockNumber::Earliest => storage.genesis_hash,
696                    BlockNumber::Pending => return None,
697                    BlockNumber::Number(num) => *storage.hashes.get(&num)?,
698                    BlockNumber::Safe => {
699                        if storage.best_number > (slots_in_an_epoch) {
700                            *storage.hashes.get(&(storage.best_number - (slots_in_an_epoch)))?
701                        } else {
702                            storage.genesis_hash // treat the genesis block as safe "by definition"
703                        }
704                    }
705                    BlockNumber::Finalized => {
706                        if storage.best_number > (slots_in_an_epoch * 2) {
707                            *storage.hashes.get(&(storage.best_number - (slots_in_an_epoch * 2)))?
708                        } else {
709                            storage.genesis_hash
710                        }
711                    }
712                }
713            }
714        };
715        let block = self.get_block_by_hash(hash)?;
716        Some((block, hash))
717    }
718
719    pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
720        self.get_block_with_hash(id).map(|(block, _)| block)
721    }
722
723    pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
724        self.blockchain.get_block_by_hash(&hash)
725    }
726
727    /// Returns the traces for the given transaction
728    pub(crate) fn mined_parity_trace_transaction(
729        &self,
730        hash: B256,
731    ) -> Option<Vec<LocalizedTransactionTrace>> {
732        self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
733    }
734
735    /// Returns the traces for the given block
736    pub(crate) fn mined_parity_trace_block(
737        &self,
738        block: u64,
739    ) -> Option<Vec<LocalizedTransactionTrace>> {
740        let block = self.get_block(block)?;
741        let mut traces = vec![];
742        let storage = self.blockchain.storage.read();
743        for tx in block.body.transactions {
744            traces.extend(storage.transactions.get(&tx.hash())?.parity_traces());
745        }
746        Some(traces)
747    }
748
749    /// Returns the mined transaction for the given hash
750    pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction<N>> {
751        self.blockchain.storage.read().transactions.get(&hash).cloned()
752    }
753
754    /// Overrides the given signature to impersonate the specified address during ecrecover.
755    pub async fn impersonate_signature(
756        &self,
757        signature: Bytes,
758        address: Address,
759    ) -> Result<(), BlockchainError> {
760        self.cheats.add_recover_override(signature, address);
761        Ok(())
762    }
763
764    /// Returns code by its hash
765    pub async fn debug_code_by_hash(
766        &self,
767        code_hash: B256,
768        block_id: Option<BlockId>,
769    ) -> Result<Option<Bytes>, BlockchainError> {
770        if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
771            return Ok(Some(code.original_bytes()));
772        }
773        if let Some(fork) = self.get_fork() {
774            return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
775        }
776
777        Ok(None)
778    }
779
780    /// Returns the value associated with a key from the database
781    /// Currently only supports bytecode lookups.
782    ///
783    /// Based on Reth implementation: <https://github.com/paradigmxyz/reth/blob/66cfa9ed1a8c4bc2424aacf6fb2c1e67a78ee9a2/crates/rpc/rpc/src/debug.rs#L1146-L1178>
784    ///
785    /// Key should be: 0x63 (1-byte prefix) + 32 bytes (code_hash)
786    /// Total key length must be 33 bytes.
787    pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
788        let key_bytes = if key.starts_with("0x") {
789            hex::decode(&key)
790                .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
791        } else {
792            key.into_bytes()
793        };
794
795        // Validate key length: must be 33 bytes (1 byte prefix + 32 bytes code hash)
796        if key_bytes.len() != 33 {
797            return Err(BlockchainError::Message(format!(
798                "Invalid key length: expected 33 bytes, got {}",
799                key_bytes.len()
800            )));
801        }
802
803        // Check for bytecode prefix (0x63 = 'c' in ASCII)
804        if key_bytes[0] != 0x63 {
805            return Err(BlockchainError::Message(
806                "Key prefix must be 0x63 for code hash lookups".to_string(),
807            ));
808        }
809
810        let code_hash = B256::from_slice(&key_bytes[1..33]);
811
812        // Use the existing debug_code_by_hash method to retrieve the bytecode
813        self.debug_code_by_hash(code_hash, None).await
814    }
815
816    fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
817        let block = self.blockchain.get_block_by_hash(&hash)?;
818        Some(self.convert_block_with_hash(block, Some(hash)))
819    }
820
821    pub(crate) async fn mined_transactions_by_block_number(
822        &self,
823        number: BlockNumber,
824    ) -> Option<Vec<AnyRpcTransaction>> {
825        if let Some(block) = self.get_block(number) {
826            return self.mined_transactions_in_block(&block);
827        }
828        None
829    }
830
831    /// Returns all transactions given a block
832    pub(crate) fn mined_transactions_in_block(
833        &self,
834        block: &Block,
835    ) -> Option<Vec<AnyRpcTransaction>> {
836        let mut transactions = Vec::with_capacity(block.body.transactions.len());
837        let base_fee = block.header.base_fee_per_gas();
838        let storage = self.blockchain.storage.read();
839        for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
840            let info = storage.transactions.get(&hash)?.info.clone();
841            let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
842
843            let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
844            transactions.push(tx);
845        }
846        Some(transactions)
847    }
848
849    pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
850        let (block, hash) = self.get_block_with_hash(number)?;
851        let mut block = self.convert_block_with_hash(block, Some(hash));
852        block.transactions.convert_to_hashes();
853        Some(block)
854    }
855
856    pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
857        let (block, hash) = self.get_block_with_hash(id)?;
858        let transactions = self.mined_transactions_in_block(&block)?;
859        let mut block = self.convert_block_with_hash(block, Some(hash));
860        block.inner.transactions = BlockTransactions::Full(transactions);
861        Some(block)
862    }
863
864    /// Takes a block as it's stored internally and returns the eth api conform block format.
865    pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
866        self.convert_block_with_hash(block, None)
867    }
868
869    /// Takes a block as it's stored internally and returns the eth api conform block format.
870    /// If `known_hash` is provided, it will be used instead of computing `hash_slow()`.
871    pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
872        let size = U256::from(alloy_rlp::encode(&block).len() as u32);
873
874        let header = block.header.clone();
875        let transactions = block.body.transactions;
876
877        let hash = known_hash.unwrap_or_else(|| header.hash_slow());
878        let Header { number, withdrawals_root, .. } = header;
879
880        let block = AlloyBlock {
881            header: AlloyHeader {
882                inner: AnyHeader::from(header),
883                hash,
884                total_difficulty: Some(self.total_difficulty()),
885                size: Some(size),
886            },
887            transactions: alloy_rpc_types::BlockTransactions::Hashes(
888                transactions.into_iter().map(|tx| tx.hash()).collect(),
889            ),
890            uncles: vec![],
891            withdrawals: withdrawals_root.map(|_| Default::default()),
892        };
893
894        let mut block = WithOtherFields::new(block);
895
896        // If Arbitrum, apply chain specifics to converted block.
897        if is_arbitrum(self.env.read().evm_env.cfg_env.chain_id) {
898            // Set `l1BlockNumber` field.
899            block.other.insert("l1BlockNumber".to_string(), number.into());
900        }
901
902        AnyRpcBlock::from(block)
903    }
904
905    pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
906        trace!(target: "backend", "get block by hash {:?}", hash);
907        if let tx @ Some(_) = self.mined_block_by_hash(hash) {
908            return Ok(tx);
909        }
910
911        if let Some(fork) = self.get_fork() {
912            return Ok(fork.block_by_hash(hash).await?);
913        }
914
915        Ok(None)
916    }
917
918    pub async fn block_by_hash_full(
919        &self,
920        hash: B256,
921    ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
922        trace!(target: "backend", "get block by hash {:?}", hash);
923        if let tx @ Some(_) = self.get_full_block(hash) {
924            return Ok(tx);
925        }
926
927        if let Some(fork) = self.get_fork() {
928            return Ok(fork.block_by_hash_full(hash).await?);
929        }
930
931        Ok(None)
932    }
933
934    pub async fn block_by_number(
935        &self,
936        number: BlockNumber,
937    ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
938        trace!(target: "backend", "get block by number {:?}", number);
939        if let tx @ Some(_) = self.mined_block_by_number(number) {
940            return Ok(tx);
941        }
942
943        if let Some(fork) = self.get_fork() {
944            let number = self.convert_block_number(Some(number));
945            if fork.predates_fork_inclusive(number) {
946                return Ok(fork.block_by_number(number).await?);
947            }
948        }
949
950        Ok(None)
951    }
952
953    pub async fn block_by_number_full(
954        &self,
955        number: BlockNumber,
956    ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
957        trace!(target: "backend", "get block by number {:?}", number);
958        if let tx @ Some(_) = self.get_full_block(number) {
959            return Ok(tx);
960        }
961
962        if let Some(fork) = self.get_fork() {
963            let number = self.convert_block_number(Some(number));
964            if fork.predates_fork_inclusive(number) {
965                return Ok(fork.block_by_number_full(number).await?);
966            }
967        }
968
969        Ok(None)
970    }
971
972    /// Converts the `BlockNumber` into a numeric value
973    ///
974    /// # Errors
975    ///
976    /// returns an error if the requested number is larger than the current height
977    pub async fn ensure_block_number<T: Into<BlockId>>(
978        &self,
979        block_id: Option<T>,
980    ) -> Result<u64, BlockchainError> {
981        let current = self.best_number();
982        let requested =
983            match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) {
984                BlockId::Hash(hash) => {
985                    self.block_by_hash(hash.block_hash)
986                        .await?
987                        .ok_or(BlockchainError::BlockNotFound)?
988                        .header
989                        .number
990                }
991                BlockId::Number(num) => match num {
992                    BlockNumber::Latest | BlockNumber::Pending => current,
993                    BlockNumber::Earliest => U64::ZERO.to::<u64>(),
994                    BlockNumber::Number(num) => num,
995                    BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
996                    BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
997                },
998            };
999
1000        if requested > current {
1001            Err(BlockchainError::BlockOutOfRange(current, requested))
1002        } else {
1003            Ok(requested)
1004        }
1005    }
1006
1007    /// Creates an EVM instance with optionally injected precompiles.
1008    fn new_eth_evm_with_inspector_ref<'db, I, DB>(
1009        &self,
1010        db: &'db DB,
1011        env: &Env,
1012        inspector: &'db mut I,
1013    ) -> EitherEvm<WrapDatabaseRef<&'db DB>, &'db mut I, PrecompilesMap>
1014    where
1015        DB: DatabaseRef + ?Sized,
1016        I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>
1017            + Inspector<OpContext<WrapDatabaseRef<&'db DB>>>,
1018        WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1019    {
1020        let mut evm = new_eth_evm_with_inspector(WrapDatabaseRef(db), env, inspector);
1021        self.env.read().networks.inject_precompiles(evm.precompiles_mut());
1022
1023        if let Some(factory) = &self.precompile_factory {
1024            evm.precompiles_mut().extend_precompiles(factory.precompiles());
1025        }
1026
1027        let cheats = Arc::new(self.cheats.clone());
1028        if cheats.has_recover_overrides() {
1029            let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
1030            evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
1031                Some(DynPrecompile::new_stateful(
1032                    cheat_ecrecover.precompile_id().clone(),
1033                    move |input| cheat_ecrecover.call(input),
1034                ))
1035            });
1036        }
1037
1038        evm
1039    }
1040
1041    /// ## EVM settings
1042    ///
1043    /// This modifies certain EVM settings to mirror geth's `SkipAccountChecks` when transacting requests, see also: <https://github.com/ethereum/go-ethereum/blob/380688c636a654becc8f114438c2a5d93d2db032/core/state_transition.go#L145-L148>:
1044    ///
1045    ///  - `disable_eip3607` is set to `true`
1046    ///  - `disable_base_fee` is set to `true`
1047    ///  - `tx_gas_limit_cap` is set to `Some(u64::MAX)` indicating no gas limit cap
1048    ///  - `nonce` check is skipped
1049    fn build_call_env(
1050        &self,
1051        request: WithOtherFields<TransactionRequest>,
1052        fee_details: FeeDetails,
1053        block_env: BlockEnv,
1054    ) -> Env {
1055        let tx_type = request.minimal_tx_type() as u8;
1056
1057        let WithOtherFields::<TransactionRequest> {
1058            inner:
1059                TransactionRequest {
1060                    from,
1061                    to,
1062                    gas,
1063                    value,
1064                    input,
1065                    access_list,
1066                    blob_versioned_hashes,
1067                    authorization_list,
1068                    nonce,
1069                    sidecar: _,
1070                    chain_id,
1071                    .. // Rest of the gas fees related fields are taken from `fee_details`
1072                },
1073            other,
1074        } = request;
1075
1076        let FeeDetails {
1077            gas_price,
1078            max_fee_per_gas,
1079            max_priority_fee_per_gas,
1080            max_fee_per_blob_gas,
1081        } = fee_details;
1082
1083        let gas_limit = gas.unwrap_or(block_env.gas_limit);
1084        let mut env = self.env.read().clone();
1085        env.evm_env.block_env = block_env;
1086        // we want to disable this in eth_call, since this is common practice used by other node
1087        // impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
1088        env.evm_env.cfg_env.disable_block_gas_limit = true;
1089        env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
1090
1091        // The basefee should be ignored for calls against state for
1092        // - eth_call
1093        // - eth_estimateGas
1094        // - eth_createAccessList
1095        // - tracing
1096        env.evm_env.cfg_env.disable_base_fee = true;
1097
1098        // Disable nonce check in revm
1099        env.evm_env.cfg_env.disable_nonce_check = true;
1100
1101        let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
1102            self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
1103        });
1104        let caller = from.unwrap_or_default();
1105        let to = to.as_ref().and_then(TxKind::to);
1106        let blob_hashes = blob_versioned_hashes.unwrap_or_default();
1107        let mut base = TxEnv {
1108            caller,
1109            gas_limit,
1110            gas_price,
1111            gas_priority_fee: max_priority_fee_per_gas,
1112            max_fee_per_blob_gas: max_fee_per_blob_gas
1113                .or_else(|| {
1114                    if !blob_hashes.is_empty() {
1115                        env.evm_env.block_env.blob_gasprice()
1116                    } else {
1117                        Some(0)
1118                    }
1119                })
1120                .unwrap_or_default(),
1121            kind: match to {
1122                Some(addr) => TxKind::Call(*addr),
1123                None => TxKind::Create,
1124            },
1125            tx_type,
1126            value: value.unwrap_or_default(),
1127            data: input.into_input().unwrap_or_default(),
1128            chain_id: Some(chain_id.unwrap_or(self.env.read().evm_env.cfg_env.chain_id)),
1129            access_list: access_list.unwrap_or_default(),
1130            blob_hashes,
1131            ..Default::default()
1132        };
1133        base.set_signed_authorization(authorization_list.unwrap_or_default());
1134        env.tx = OpTransaction { base, ..Default::default() };
1135
1136        if let Some(nonce) = nonce {
1137            env.tx.base.nonce = nonce;
1138        }
1139
1140        if env.evm_env.block_env.basefee == 0 {
1141            // this is an edge case because the evm fails if `tx.effective_gas_price < base_fee`
1142            // 0 is only possible if it's manually set
1143            env.evm_env.cfg_env.disable_base_fee = true;
1144        }
1145
1146        // Deposit transaction?
1147        if let Ok(deposit) = get_deposit_tx_parts(&other) {
1148            env.tx.deposit = deposit;
1149        }
1150
1151        env
1152    }
1153
1154    pub fn call_with_state(
1155        &self,
1156        state: &dyn DatabaseRef,
1157        request: WithOtherFields<TransactionRequest>,
1158        fee_details: FeeDetails,
1159        block_env: BlockEnv,
1160    ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1161        let mut inspector = self.build_inspector();
1162
1163        let env = self.build_call_env(request, fee_details, block_env);
1164        let mut evm = self.new_eth_evm_with_inspector_ref(state, &env, &mut inspector);
1165        let ResultAndState { result, state } = evm.transact(env.tx)?;
1166        let (exit_reason, gas_used, out) = match result {
1167            ExecutionResult::Success { reason, gas_used, output, .. } => {
1168                (reason.into(), gas_used, Some(output))
1169            }
1170            ExecutionResult::Revert { gas_used, output } => {
1171                (InstructionResult::Revert, gas_used, Some(Output::Call(output)))
1172            }
1173            ExecutionResult::Halt { reason, gas_used } => {
1174                (op_haltreason_to_instruction_result(reason), gas_used, None)
1175            }
1176        };
1177        drop(evm);
1178        inspector.print_logs();
1179
1180        if self.print_traces {
1181            inspector.into_print_traces(self.call_trace_decoder.clone());
1182        }
1183
1184        Ok((exit_reason, out, gas_used as u128, state))
1185    }
1186
1187    pub fn build_access_list_with_state(
1188        &self,
1189        state: &dyn DatabaseRef,
1190        request: WithOtherFields<TransactionRequest>,
1191        fee_details: FeeDetails,
1192        block_env: BlockEnv,
1193    ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
1194        let mut inspector =
1195            AccessListInspector::new(request.access_list.clone().unwrap_or_default());
1196
1197        let env = self.build_call_env(request, fee_details, block_env);
1198        let mut evm = self.new_eth_evm_with_inspector_ref(state, &env, &mut inspector);
1199        let ResultAndState { result, state: _ } = evm.transact(env.tx)?;
1200        let (exit_reason, gas_used, out) = match result {
1201            ExecutionResult::Success { reason, gas_used, output, .. } => {
1202                (reason.into(), gas_used, Some(output))
1203            }
1204            ExecutionResult::Revert { gas_used, output } => {
1205                (InstructionResult::Revert, gas_used, Some(Output::Call(output)))
1206            }
1207            ExecutionResult::Halt { reason, gas_used } => {
1208                (op_haltreason_to_instruction_result(reason), gas_used, None)
1209            }
1210        };
1211        drop(evm);
1212        let access_list = inspector.access_list();
1213        Ok((exit_reason, out, gas_used, access_list))
1214    }
1215
1216    pub fn get_code_with_state(
1217        &self,
1218        state: &dyn DatabaseRef,
1219        address: Address,
1220    ) -> Result<Bytes, BlockchainError> {
1221        trace!(target: "backend", "get code for {:?}", address);
1222        let account = state.basic_ref(address)?.unwrap_or_default();
1223        if account.code_hash == KECCAK_EMPTY {
1224            // if the code hash is `KECCAK_EMPTY`, we check no further
1225            return Ok(Default::default());
1226        }
1227        let code = if let Some(code) = account.code {
1228            code
1229        } else {
1230            state.code_by_hash_ref(account.code_hash)?
1231        };
1232        Ok(code.bytes()[..code.len()].to_vec().into())
1233    }
1234
1235    pub fn get_balance_with_state<D>(
1236        &self,
1237        state: D,
1238        address: Address,
1239    ) -> Result<U256, BlockchainError>
1240    where
1241        D: DatabaseRef,
1242    {
1243        trace!(target: "backend", "get balance for {:?}", address);
1244        Ok(state.basic_ref(address)?.unwrap_or_default().balance)
1245    }
1246
1247    pub async fn transaction_by_block_number_and_index(
1248        &self,
1249        number: BlockNumber,
1250        index: Index,
1251    ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1252        if let Some(block) = self.mined_block_by_number(number) {
1253            return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
1254        }
1255
1256        if let Some(fork) = self.get_fork() {
1257            let number = self.convert_block_number(Some(number));
1258            if fork.predates_fork(number) {
1259                return Ok(fork
1260                    .transaction_by_block_number_and_index(number, index.into())
1261                    .await?);
1262            }
1263        }
1264
1265        Ok(None)
1266    }
1267
1268    pub async fn transaction_by_block_hash_and_index(
1269        &self,
1270        hash: B256,
1271        index: Index,
1272    ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1273        if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
1274            return Ok(tx);
1275        }
1276
1277        if let Some(fork) = self.get_fork() {
1278            return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
1279        }
1280
1281        Ok(None)
1282    }
1283
1284    pub fn mined_transaction_by_block_hash_and_index(
1285        &self,
1286        block_hash: B256,
1287        index: Index,
1288    ) -> Option<AnyRpcTransaction> {
1289        let (info, block, tx) = {
1290            let storage = self.blockchain.storage.read();
1291            let block = storage.blocks.get(&block_hash).cloned()?;
1292            let index: usize = index.into();
1293            let tx = block.body.transactions.get(index)?.clone();
1294            let info = storage.transactions.get(&tx.hash())?.info.clone();
1295            (info, block, tx)
1296        };
1297
1298        Some(transaction_build(
1299            Some(info.transaction_hash),
1300            tx,
1301            Some(&block),
1302            Some(info),
1303            block.header.base_fee_per_gas(),
1304        ))
1305    }
1306
1307    pub async fn transaction_by_hash(
1308        &self,
1309        hash: B256,
1310    ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1311        trace!(target: "backend", "transaction_by_hash={:?}", hash);
1312        if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
1313            return Ok(tx);
1314        }
1315
1316        if let Some(fork) = self.get_fork() {
1317            return fork
1318                .transaction_by_hash(hash)
1319                .await
1320                .map_err(BlockchainError::AlloyForkProvider);
1321        }
1322
1323        Ok(None)
1324    }
1325
1326    pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
1327        let (info, block) = {
1328            let storage = self.blockchain.storage.read();
1329            let MinedTransaction { info, block_hash, .. } =
1330                storage.transactions.get(&hash)?.clone();
1331            let block = storage.blocks.get(&block_hash).cloned()?;
1332            (info, block)
1333        };
1334        let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
1335
1336        Some(transaction_build(
1337            Some(info.transaction_hash),
1338            tx,
1339            Some(&block),
1340            Some(info),
1341            block.header.base_fee_per_gas(),
1342        ))
1343    }
1344
1345    /// Returns the traces for the given transaction
1346    pub async fn trace_transaction(
1347        &self,
1348        hash: B256,
1349    ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1350        if let Some(traces) = self.mined_parity_trace_transaction(hash) {
1351            return Ok(traces);
1352        }
1353
1354        if let Some(fork) = self.get_fork() {
1355            return Ok(fork.trace_transaction(hash).await?);
1356        }
1357
1358        Ok(vec![])
1359    }
1360
1361    /// Returns the traces for the given block
1362    pub async fn trace_block(
1363        &self,
1364        block: BlockNumber,
1365    ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1366        let number = self.convert_block_number(Some(block));
1367        if let Some(traces) = self.mined_parity_trace_block(number) {
1368            return Ok(traces);
1369        }
1370
1371        if let Some(fork) = self.get_fork()
1372            && fork.predates_fork(number)
1373        {
1374            return Ok(fork.trace_block(number).await?);
1375        }
1376
1377        Ok(vec![])
1378    }
1379
1380    /// Replays all transactions in a block and returns the requested traces for each transaction
1381    pub async fn trace_replay_block_transactions(
1382        &self,
1383        block: BlockNumber,
1384        trace_types: HashSet<TraceType>,
1385    ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
1386        let block_number = self.convert_block_number(Some(block));
1387
1388        // Try mined blocks first
1389        if let Some(results) =
1390            self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)
1391        {
1392            return Ok(results);
1393        }
1394
1395        // Fallback to fork if block predates fork
1396        if let Some(fork) = self.get_fork()
1397            && fork.predates_fork(block_number)
1398        {
1399            return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?);
1400        }
1401
1402        Ok(vec![])
1403    }
1404
1405    /// Returns the trace results for all transactions in a mined block by replaying them
1406    fn mined_parity_trace_replay_block_transactions(
1407        &self,
1408        block_number: u64,
1409        trace_types: &HashSet<TraceType>,
1410    ) -> Option<Vec<TraceResultsWithTransactionHash>> {
1411        let block = self.get_block(block_number)?;
1412
1413        // Execute this in the context of the parent state
1414        let parent_hash = block.header.parent_hash;
1415        let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
1416
1417        let read_guard = self.states.upgradable_read();
1418        if let Some(state) = read_guard.get_state(&parent_hash) {
1419            self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
1420        } else {
1421            let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
1422            let state = write_guard.get_on_disk_state(&parent_hash)?;
1423            self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
1424        }
1425    }
1426
1427    /// Replays all transactions in a block with the tracing inspector to generate TraceResults
1428    fn replay_block_transactions_with_inspector(
1429        &self,
1430        block: &Block,
1431        parent_state: &StateDb,
1432        trace_config: TracingInspectorConfig,
1433        trace_types: &HashSet<TraceType>,
1434    ) -> Option<Vec<TraceResultsWithTransactionHash>> {
1435        let mut cache_db = CacheDB::new(Box::new(parent_state));
1436        let mut results = Vec::new();
1437
1438        // Configure the block environment
1439        let mut env = self.env.read().clone();
1440        env.evm_env.block_env = block_env_from_header(&block.header);
1441
1442        // Execute each transaction in the block with tracing
1443        for tx_envelope in &block.body.transactions {
1444            let tx_hash = tx_envelope.hash();
1445
1446            // Create a fresh inspector for this transaction
1447            let mut inspector = TracingInspector::new(trace_config);
1448
1449            // Prepare transaction environment
1450            let pending_tx =
1451                PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?;
1452            let mut tx_env: OpTransaction<TxEnv> = FromRecoveredTx::from_recovered_tx(
1453                pending_tx.transaction.as_ref(),
1454                *pending_tx.sender(),
1455            );
1456            if env.networks.is_optimism() {
1457                tx_env.enveloped_tx = Some(pending_tx.transaction.encoded_2718().into());
1458            }
1459
1460            // Execute the transaction with the inspector
1461            let mut evm = self.new_eth_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
1462            let result = evm.transact(tx_env.clone()).ok()?;
1463
1464            // Build TraceResults from the inspector and execution result
1465            let full_trace = inspector
1466                .into_parity_builder()
1467                .into_trace_results_with_state(&result, trace_types, &cache_db)
1468                .ok()?;
1469
1470            results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
1471
1472            // Commit the state changes for the next transaction
1473            cache_db.commit(result.state);
1474        }
1475
1476        Some(results)
1477    }
1478
1479    // Returns the traces matching a given filter
1480    pub async fn trace_filter(
1481        &self,
1482        filter: TraceFilter,
1483    ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1484        let matcher = filter.matcher();
1485        let start = filter.from_block.unwrap_or(0);
1486        let end = filter.to_block.unwrap_or_else(|| self.best_number());
1487
1488        if start > end {
1489            return Err(BlockchainError::RpcError(RpcError::invalid_params(
1490                "invalid block range, ensure that to block is greater than from block".to_string(),
1491            )));
1492        }
1493
1494        let dist = end - start;
1495        if dist > 300 {
1496            return Err(BlockchainError::RpcError(RpcError::invalid_params(
1497                "block range too large, currently limited to 300".to_string(),
1498            )));
1499        }
1500
1501        // Accumulate tasks for block range
1502        let mut trace_tasks = vec![];
1503        for num in start..=end {
1504            trace_tasks.push(self.trace_block(num.into()));
1505        }
1506
1507        // Execute tasks and filter traces
1508        let traces = futures::future::try_join_all(trace_tasks).await?;
1509        let filtered_traces =
1510            traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
1511
1512        // Apply after and count
1513        let filtered_traces: Vec<_> = if let Some(after) = filter.after {
1514            filtered_traces.skip(after as usize).collect()
1515        } else {
1516            filtered_traces.collect()
1517        };
1518
1519        let filtered_traces: Vec<_> = if let Some(count) = filter.count {
1520            filtered_traces.into_iter().take(count as usize).collect()
1521        } else {
1522            filtered_traces
1523        };
1524
1525        Ok(filtered_traces)
1526    }
1527
1528    pub fn get_blobs_by_block_id(
1529        &self,
1530        id: impl Into<BlockId>,
1531        versioned_hashes: Vec<B256>,
1532    ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
1533        Ok(self.get_block(id).map(|block| {
1534            block
1535                .body
1536                .transactions
1537                .iter()
1538                .filter_map(|tx| tx.as_ref().sidecar())
1539                .flat_map(|sidecar| {
1540                    sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
1541                })
1542                .filter(|(_, commitment)| {
1543                    // Filter blobs by versioned_hashes if provided
1544                    versioned_hashes.is_empty()
1545                        || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
1546                })
1547                .map(|(blob, _)| *blob)
1548                .collect()
1549        }))
1550    }
1551
1552    pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
1553        let storage = self.blockchain.storage.read();
1554        for block in storage.blocks.values() {
1555            for tx in &block.body.transactions {
1556                let typed_tx = tx.as_ref();
1557                if let Some(sidecar) = typed_tx.sidecar() {
1558                    for versioned_hash in sidecar.sidecar.versioned_hashes() {
1559                        if versioned_hash == hash
1560                            && let Some(index) =
1561                                sidecar.sidecar.commitments().iter().position(|commitment| {
1562                                    kzg_to_versioned_hash(commitment.as_slice()) == *hash
1563                                })
1564                            && let Some(blob) = sidecar.sidecar.blobs().get(index)
1565                        {
1566                            return Ok(Some(*blob));
1567                        }
1568                    }
1569                }
1570            }
1571        }
1572        Ok(None)
1573    }
1574
1575    /// Initialises the balance of the given accounts
1576    #[expect(clippy::too_many_arguments)]
1577    pub async fn with_genesis(
1578        db: Arc<AsyncRwLock<Box<dyn Db>>>,
1579        env: Arc<RwLock<Env>>,
1580        genesis: GenesisConfig,
1581        fees: FeeManager,
1582        fork: Arc<RwLock<Option<ClientFork>>>,
1583        enable_steps_tracing: bool,
1584        print_logs: bool,
1585        print_traces: bool,
1586        call_trace_decoder: Arc<CallTraceDecoder>,
1587        prune_state_history_config: PruneStateHistoryConfig,
1588        max_persisted_states: Option<usize>,
1589        transaction_block_keeper: Option<usize>,
1590        automine_block_time: Option<Duration>,
1591        cache_path: Option<PathBuf>,
1592        node_config: Arc<AsyncRwLock<NodeConfig>>,
1593    ) -> Result<Self> {
1594        // if this is a fork then adjust the blockchain storage
1595        let blockchain = if let Some(fork) = fork.read().as_ref() {
1596            trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
1597            Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
1598        } else {
1599            let env = env.read();
1600            Blockchain::new(
1601                &env,
1602                env.evm_env.cfg_env.spec,
1603                fees.is_eip1559().then(|| fees.base_fee()),
1604                genesis.timestamp,
1605                genesis.number,
1606            )
1607        };
1608
1609        // Sync EVM block.number with genesis for non-fork mode.
1610        // Fork mode syncs in setup_fork_db_config() instead.
1611        if fork.read().is_none() {
1612            let mut write_env = env.write();
1613            write_env.evm_env.block_env.number = U256::from(genesis.number);
1614        }
1615
1616        let start_timestamp = if let Some(fork) = fork.read().as_ref() {
1617            fork.timestamp()
1618        } else {
1619            genesis.timestamp
1620        };
1621
1622        let mut states = if prune_state_history_config.is_config_enabled() {
1623            // if prune state history is enabled, configure the state cache only for memory
1624            prune_state_history_config
1625                .max_memory_history
1626                .map(|limit| InMemoryBlockStates::new(limit, 0))
1627                .unwrap_or_default()
1628                .memory_only()
1629        } else if max_persisted_states.is_some() {
1630            max_persisted_states
1631                .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
1632                .unwrap_or_default()
1633        } else {
1634            Default::default()
1635        };
1636
1637        if let Some(cache_path) = cache_path {
1638            states = states.disk_path(cache_path);
1639        }
1640
1641        let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks) = {
1642            let cfg = node_config.read().await;
1643            (cfg.slots_in_an_epoch, cfg.precompile_factory.clone(), cfg.disable_pool_balance_checks)
1644        };
1645
1646        let backend = Self {
1647            db,
1648            blockchain,
1649            states: Arc::new(RwLock::new(states)),
1650            env,
1651            fork,
1652            time: TimeManager::new(start_timestamp),
1653            cheats: Default::default(),
1654            new_block_listeners: Default::default(),
1655            fees,
1656            genesis,
1657            active_state_snapshots: Arc::new(Mutex::new(Default::default())),
1658            enable_steps_tracing,
1659            print_logs,
1660            print_traces,
1661            call_trace_decoder,
1662            prune_state_history_config,
1663            transaction_block_keeper,
1664            node_config,
1665            slots_in_an_epoch,
1666            precompile_factory,
1667            mining: Arc::new(tokio::sync::Mutex::new(())),
1668            disable_pool_balance_checks,
1669        };
1670
1671        if let Some(interval_block_time) = automine_block_time {
1672            backend.update_interval_mine_block_time(interval_block_time);
1673        }
1674
1675        // Note: this can only fail in forking mode, in which case we can't recover
1676        backend.apply_genesis().await.wrap_err("failed to create genesis")?;
1677        Ok(backend)
1678    }
1679
1680    /// Applies the configured genesis settings
1681    ///
1682    /// This will fund, create the genesis accounts
1683    async fn apply_genesis(&self) -> Result<(), DatabaseError> {
1684        trace!(target: "backend", "setting genesis balances");
1685
1686        if self.fork.read().is_some() {
1687            // fetch all account first
1688            let mut genesis_accounts_futures = Vec::with_capacity(self.genesis.accounts.len());
1689            for address in self.genesis.accounts.iter().copied() {
1690                let db = Arc::clone(&self.db);
1691
1692                // The forking Database backend can handle concurrent requests, we can fetch all dev
1693                // accounts concurrently by spawning the job to a new task
1694                genesis_accounts_futures.push(tokio::task::spawn(async move {
1695                    let db = db.read().await;
1696                    let info = db.basic_ref(address)?.unwrap_or_default();
1697                    Ok::<_, DatabaseError>((address, info))
1698                }));
1699            }
1700
1701            let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
1702
1703            let mut db = self.db.write().await;
1704
1705            for res in genesis_accounts {
1706                let (address, mut info) = res.unwrap()?;
1707                info.balance = self.genesis.balance;
1708                db.insert_account(address, info.clone());
1709            }
1710        } else {
1711            let mut db = self.db.write().await;
1712            for (account, info) in self.genesis.account_infos() {
1713                db.insert_account(account, info);
1714            }
1715
1716            // insert the new genesis hash to the database so it's available for the next block in
1717            // the evm
1718            db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
1719
1720            // Deploy EIP-2935 blockhash history storage contract if Prague is active.
1721            if self.spec_id() >= SpecId::PRAGUE {
1722                db.set_code(
1723                    eip2935::HISTORY_STORAGE_ADDRESS,
1724                    eip2935::HISTORY_STORAGE_CODE.clone(),
1725                )?;
1726            }
1727        }
1728
1729        let db = self.db.write().await;
1730        // apply the genesis.json alloc
1731        self.genesis.apply_genesis_json_alloc(db)?;
1732
1733        trace!(target: "backend", "set genesis balances");
1734
1735        Ok(())
1736    }
1737
1738    /// Resets the fork to a fresh state
1739    pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
1740        if !self.is_fork() {
1741            if let Some(eth_rpc_url) = forking.json_rpc_url.clone() {
1742                let mut env = self.env.read().clone();
1743
1744                let (db, config) = {
1745                    let mut node_config = self.node_config.write().await;
1746
1747                    // we want to force the correct base fee for the next block during
1748                    // `setup_fork_db_config`
1749                    node_config.base_fee.take();
1750
1751                    node_config.setup_fork_db_config(eth_rpc_url, &mut env, &self.fees).await?
1752                };
1753
1754                *self.db.write().await = Box::new(db);
1755
1756                let fork = ClientFork::new(config, Arc::clone(&self.db));
1757
1758                *self.env.write() = env;
1759                *self.fork.write() = Some(fork);
1760            } else {
1761                return Err(RpcError::invalid_params(
1762                    "Forking not enabled and RPC URL not provided to start forking",
1763                )
1764                .into());
1765            }
1766        }
1767
1768        if let Some(fork) = self.get_fork() {
1769            let block_number =
1770                forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest);
1771            // reset the fork entirely and reapply the genesis config
1772            fork.reset(forking.json_rpc_url.clone(), block_number).await?;
1773            let fork_block_number = fork.block_number();
1774            let fork_block = fork
1775                .block_by_number(fork_block_number)
1776                .await?
1777                .ok_or(BlockchainError::BlockNotFound)?;
1778            // update all settings related to the forked block
1779            {
1780                if let Some(fork_url) = forking.json_rpc_url {
1781                    self.reset_block_number(fork_url, fork_block_number).await?;
1782                } else {
1783                    // If rpc url is unspecified, then update the fork with the new block number and
1784                    // existing rpc url, this updates the cache path
1785                    {
1786                        let maybe_fork_url = { self.node_config.read().await.eth_rpc_url.clone() };
1787                        if let Some(fork_url) = maybe_fork_url {
1788                            self.reset_block_number(fork_url, fork_block_number).await?;
1789                        }
1790                    }
1791
1792                    let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_block);
1793                    let mut env = self.env.write();
1794
1795                    env.evm_env.cfg_env.chain_id = fork.chain_id();
1796                    env.evm_env.block_env = BlockEnv {
1797                        number: U256::from(fork_block_number),
1798                        timestamp: U256::from(fork_block.header.timestamp()),
1799                        gas_limit,
1800                        difficulty: fork_block.header.difficulty(),
1801                        prevrandao: Some(fork_block.header.mix_hash().unwrap_or_default()),
1802                        // Keep previous `beneficiary` and `basefee` value
1803                        beneficiary: env.evm_env.block_env.beneficiary,
1804                        basefee: env.evm_env.block_env.basefee,
1805                        ..env.evm_env.block_env.clone()
1806                    };
1807
1808                    // this is the base fee of the current block, but we need the base fee of
1809                    // the next block
1810                    let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
1811                        fork_block.header.gas_used(),
1812                        gas_limit,
1813                        fork_block.header.base_fee_per_gas().unwrap_or_default(),
1814                    );
1815
1816                    self.fees.set_base_fee(next_block_base_fee);
1817                }
1818
1819                // reset the time to the timestamp of the forked block
1820                self.time.reset(fork_block.header.timestamp());
1821
1822                // also reset the total difficulty
1823                self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
1824            }
1825            // reset storage
1826            *self.blockchain.storage.write() = BlockchainStorage::forked(
1827                fork.block_number(),
1828                fork.block_hash(),
1829                fork.total_difficulty(),
1830            );
1831            self.states.write().clear();
1832            self.db.write().await.clear();
1833
1834            self.apply_genesis().await?;
1835
1836            trace!(target: "backend", "reset fork");
1837
1838            Ok(())
1839        } else {
1840            Err(RpcError::invalid_params("Forking not enabled").into())
1841        }
1842    }
1843
1844    /// Resets the backend to a fresh in-memory state, clearing all existing data
1845    pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
1846        // Clear the fork if any exists
1847        *self.fork.write() = None;
1848
1849        // Get environment and genesis config
1850        let env = self.env.read().clone();
1851        let genesis_timestamp = self.genesis.timestamp;
1852        let genesis_number = self.genesis.number;
1853        let spec_id = self.spec_id();
1854
1855        // Reset environment to genesis state
1856        {
1857            let mut env = self.env.write();
1858            env.evm_env.block_env.number = U256::from(genesis_number);
1859            env.evm_env.block_env.timestamp = U256::from(genesis_timestamp);
1860            // Reset other block env fields to their defaults
1861            env.evm_env.block_env.basefee = self.fees.base_fee();
1862            env.evm_env.block_env.prevrandao = Some(B256::ZERO);
1863        }
1864
1865        // Clear all storage and reinitialize with genesis
1866        let base_fee = if self.fees.is_eip1559() { Some(self.fees.base_fee()) } else { None };
1867        *self.blockchain.storage.write() =
1868            BlockchainStorage::new(&env, spec_id, base_fee, genesis_timestamp, genesis_number);
1869        self.states.write().clear();
1870
1871        // Clear the database
1872        self.db.write().await.clear();
1873
1874        // Reset time manager
1875        self.time.reset(genesis_timestamp);
1876
1877        // Reset fees to initial state
1878        if self.fees.is_eip1559() {
1879            self.fees.set_base_fee(crate::eth::fees::INITIAL_BASE_FEE);
1880        }
1881
1882        self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
1883
1884        // Reapply genesis configuration
1885        self.apply_genesis().await?;
1886
1887        trace!(target: "backend", "reset to fresh in-memory state");
1888
1889        Ok(())
1890    }
1891
1892    async fn reset_block_number(
1893        &self,
1894        fork_url: String,
1895        fork_block_number: u64,
1896    ) -> Result<(), BlockchainError> {
1897        let mut node_config = self.node_config.write().await;
1898        node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128));
1899
1900        let mut env = self.env.read().clone();
1901        let (forked_db, client_fork_config) =
1902            node_config.setup_fork_db_config(fork_url, &mut env, &self.fees).await?;
1903
1904        *self.db.write().await = Box::new(forked_db);
1905        let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
1906        *self.fork.write() = Some(fork);
1907        *self.env.write() = env;
1908
1909        Ok(())
1910    }
1911
1912    /// Reverts the state to the state snapshot identified by the given `id`.
1913    pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError> {
1914        let block = { self.active_state_snapshots.lock().remove(&id) };
1915        if let Some((num, hash)) = block {
1916            let best_block_hash = {
1917                // revert the storage that's newer than the snapshot
1918                let current_height = self.best_number();
1919                let mut storage = self.blockchain.storage.write();
1920
1921                for n in ((num + 1)..=current_height).rev() {
1922                    trace!(target: "backend", "reverting block {}", n);
1923                    if let Some(hash) = storage.hashes.remove(&n)
1924                        && let Some(block) = storage.blocks.remove(&hash)
1925                    {
1926                        for tx in block.body.transactions {
1927                            let _ = storage.transactions.remove(&tx.hash());
1928                        }
1929                    }
1930                }
1931
1932                storage.best_number = num;
1933                storage.best_hash = hash;
1934                hash
1935            };
1936            let block =
1937                self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
1938
1939            let reset_time = block.header.timestamp();
1940            self.time.reset(reset_time);
1941
1942            let mut env = self.env.write();
1943            env.evm_env.block_env = BlockEnv {
1944                number: U256::from(num),
1945                timestamp: U256::from(block.header.timestamp()),
1946                difficulty: block.header.difficulty(),
1947                // ensures prevrandao is set
1948                prevrandao: Some(block.header.mix_hash().unwrap_or_default()),
1949                gas_limit: block.header.gas_limit(),
1950                // Keep previous `beneficiary` and `basefee` value
1951                beneficiary: env.evm_env.block_env.beneficiary,
1952                basefee: env.evm_env.block_env.basefee,
1953                ..Default::default()
1954            }
1955        }
1956        Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove))
1957    }
1958
1959    /// executes the transactions without writing to the underlying database
1960    pub async fn inspect_tx(
1961        &self,
1962        tx: Arc<PoolTransaction<FoundryTxEnvelope>>,
1963    ) -> Result<
1964        (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
1965        BlockchainError,
1966    > {
1967        let mut env = self.next_env();
1968        env.tx = FromRecoveredTx::from_recovered_tx(
1969            tx.pending_transaction.transaction.as_ref(),
1970            *tx.pending_transaction.sender(),
1971        );
1972
1973        if env.networks.is_optimism() {
1974            env.tx.enveloped_tx = Some(tx.pending_transaction.transaction.encoded_2718().into());
1975        }
1976
1977        let db = self.db.read().await;
1978        let mut inspector = self.build_inspector();
1979        let mut evm = self.new_eth_evm_with_inspector_ref(&**db, &env, &mut inspector);
1980        let ResultAndState { result, state } = evm.transact(env.tx)?;
1981        let (exit_reason, gas_used, out, logs) = match result {
1982            ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
1983                (reason.into(), gas_used, Some(output), Some(logs))
1984            }
1985            ExecutionResult::Revert { gas_used, output } => {
1986                (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None)
1987            }
1988            ExecutionResult::Halt { reason, gas_used } => {
1989                let eth_reason = op_haltreason_to_instruction_result(reason);
1990                (eth_reason, gas_used, None, None)
1991            }
1992        };
1993
1994        drop(evm);
1995        inspector.print_logs();
1996
1997        if self.print_traces {
1998            inspector.print_traces(self.call_trace_decoder.clone());
1999        }
2000
2001        Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default()))
2002    }
2003}
2004
2005impl<N: Network> Backend<N>
2006where
2007    N::ReceiptEnvelope: alloy_consensus::TxReceipt<Log = alloy_primitives::Log> + Clone,
2008{
2009    /// Returns all `Log`s mined by the node that were emitted in the `block` and match the `Filter`
2010    fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
2011        let mut all_logs = Vec::new();
2012        let mut block_log_index = 0u32;
2013
2014        let storage = self.blockchain.storage.read();
2015
2016        for tx in block.body.transactions {
2017            let Some(tx) = storage.transactions.get(&tx.hash()) else {
2018                continue;
2019            };
2020
2021            let logs = tx.receipt.logs();
2022            let transaction_hash = tx.info.transaction_hash;
2023
2024            for log in logs {
2025                if filter.matches(log) {
2026                    all_logs.push(Log {
2027                        inner: log.clone(),
2028                        block_hash: Some(block_hash),
2029                        block_number: Some(block.header.number()),
2030                        block_timestamp: Some(block.header.timestamp()),
2031                        transaction_hash: Some(transaction_hash),
2032                        transaction_index: Some(tx.info.transaction_index),
2033                        log_index: Some(block_log_index as u64),
2034                        removed: false,
2035                    });
2036                }
2037                block_log_index += 1;
2038            }
2039        }
2040        all_logs
2041    }
2042
2043    /// Returns the logs of the block that match the filter
2044    async fn logs_for_block(
2045        &self,
2046        filter: Filter,
2047        hash: B256,
2048    ) -> Result<Vec<Log>, BlockchainError> {
2049        if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
2050            return Ok(self.mined_logs_for_block(filter, block, hash));
2051        }
2052
2053        if let Some(fork) = self.get_fork() {
2054            return Ok(fork.logs(&filter).await?);
2055        }
2056
2057        Ok(Vec::new())
2058    }
2059
2060    /// Returns the logs that match the filter in the given range of blocks
2061    async fn logs_for_range(
2062        &self,
2063        filter: &Filter,
2064        mut from: u64,
2065        to: u64,
2066    ) -> Result<Vec<Log>, BlockchainError> {
2067        let mut all_logs = Vec::new();
2068
2069        // get the range that predates the fork if any
2070        if let Some(fork) = self.get_fork() {
2071            let mut to_on_fork = to;
2072
2073            if !fork.predates_fork(to) {
2074                // adjust the ranges
2075                to_on_fork = fork.block_number();
2076            }
2077
2078            if fork.predates_fork_inclusive(from) {
2079                // this data is only available on the forked client
2080                let filter = filter.clone().from_block(from).to_block(to_on_fork);
2081                all_logs = fork.logs(&filter).await?;
2082
2083                // update the range
2084                from = fork.block_number() + 1;
2085            }
2086        }
2087
2088        for number in from..=to {
2089            if let Some((block, hash)) = self.get_block_with_hash(number) {
2090                all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
2091            }
2092        }
2093
2094        Ok(all_logs)
2095    }
2096
2097    /// Returns the logs according to the filter
2098    pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
2099        trace!(target: "backend", "get logs [{:?}]", filter);
2100        if let Some(hash) = filter.get_block_hash() {
2101            self.logs_for_block(filter, hash).await
2102        } else {
2103            let best = self.best_number();
2104            let to_block =
2105                self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
2106            let from_block =
2107                self.convert_block_number(filter.block_option.get_from_block().copied());
2108            if from_block > best {
2109                return Err(BlockchainError::BlockOutOfRange(best, from_block));
2110            }
2111
2112            self.logs_for_range(&filter, from_block, to_block).await
2113        }
2114    }
2115
2116    /// Returns all receipts of the block
2117    pub fn mined_receipts(&self, hash: B256) -> Option<Vec<N::ReceiptEnvelope>> {
2118        let block = self.mined_block_by_hash(hash)?;
2119        let mut receipts = Vec::new();
2120        let storage = self.blockchain.storage.read();
2121        for tx in block.transactions.hashes() {
2122            let receipt = storage.transactions.get(&tx)?.receipt.clone();
2123            receipts.push(receipt);
2124        }
2125        Some(receipts)
2126    }
2127}
2128
2129// Mining methods — generic over N: Network, with Foundry-associated-type bounds for now.
2130impl<N: Network> Backend<N>
2131where
2132    Self: TransactionValidator<FoundryTxEnvelope>,
2133    N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2134{
2135    /// Mines a new block and stores it.
2136    ///
2137    /// this will execute all transaction in the order they come in and return all the markers they
2138    /// provide.
2139    pub async fn mine_block(
2140        &self,
2141        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2142    ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2143        self.do_mine_block(pool_transactions).await
2144    }
2145
2146    async fn do_mine_block(
2147        &self,
2148        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2149    ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2150        let _mining_guard = self.mining.lock().await;
2151        trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
2152
2153        let (outcome, header, block_hash) = {
2154            let current_base_fee = self.base_fee();
2155            let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
2156
2157            let mut env = self.env.read().clone();
2158
2159            if env.evm_env.block_env.basefee == 0 {
2160                // this is an edge case because the evm fails if `tx.effective_gas_price < base_fee`
2161                // 0 is only possible if it's manually set
2162                env.evm_env.cfg_env.disable_base_fee = true;
2163            }
2164
2165            let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
2166
2167            // increase block number for this block
2168            if is_arbitrum(env.evm_env.cfg_env.chain_id) {
2169                // Temporary set `env.block.number` to `block_number` for Arbitrum chains.
2170                env.evm_env.block_env.number = U256::from(block_number);
2171            } else {
2172                env.evm_env.block_env.number =
2173                    env.evm_env.block_env.number.saturating_add(U256::from(1));
2174            }
2175
2176            env.evm_env.block_env.basefee = current_base_fee;
2177            env.evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
2178
2179            let best_hash = self.blockchain.storage.read().best_hash;
2180
2181            let mut input = Vec::with_capacity(40);
2182            input.extend_from_slice(best_hash.as_slice());
2183            input.extend_from_slice(&block_number.to_le_bytes());
2184            env.evm_env.block_env.prevrandao = Some(keccak256(&input));
2185
2186            if self.prune_state_history_config.is_state_history_supported() {
2187                let db = self.db.read().await.current_state();
2188                // store current state before executing all transactions
2189                self.states.write().insert(best_hash, db);
2190            }
2191
2192            let (block_info, included, invalid, block_hash) = {
2193                let mut db = self.db.write().await;
2194
2195                // finally set the next block timestamp, this is done just before execution, because
2196                // there can be concurrent requests that can delay acquiring the db lock and we want
2197                // to ensure the timestamp is as close as possible to the actual execution.
2198                env.evm_env.block_env.timestamp = U256::from(self.time.next_timestamp());
2199
2200                let spec_id = *env.evm_env.spec_id();
2201                let is_shanghai = spec_id >= SpecId::SHANGHAI;
2202                let is_cancun = spec_id >= SpecId::CANCUN;
2203                let is_prague = spec_id >= SpecId::PRAGUE;
2204                let gas_limit = env.evm_env.block_env.gas_limit;
2205                let difficulty = env.evm_env.block_env.difficulty;
2206                let mix_hash = env.evm_env.block_env.prevrandao;
2207                let beneficiary = env.evm_env.block_env.beneficiary;
2208                let timestamp = env.evm_env.block_env.timestamp;
2209                let base_fee = if spec_id >= SpecId::LONDON {
2210                    Some(env.evm_env.block_env.basefee)
2211                } else {
2212                    None
2213                };
2214                let excess_blob_gas =
2215                    if is_cancun { env.evm_env.block_env.blob_excess_gas() } else { None };
2216
2217                // 1. Build inspector (per-block, NOT per-tx)
2218                let mut inspector = AnvilInspector::default().with_tracing();
2219                if self.enable_steps_tracing {
2220                    inspector = inspector.with_steps_tracing();
2221                }
2222                if self.print_logs {
2223                    inspector = inspector.with_log_collector();
2224                }
2225                if self.print_traces {
2226                    inspector = inspector.with_trace_printer();
2227                }
2228
2229                // 2. Create EVM
2230                let env_struct = Env::new(env.evm_env.clone(), Default::default(), env.networks);
2231                let mut evm = new_eth_evm_with_inspector(&mut **db, &env_struct, inspector);
2232
2233                // 3. Inject precompiles (once, before the tx loop)
2234                env.networks.inject_precompiles(evm.precompiles_mut());
2235                if let Some(factory) = &self.precompile_factory {
2236                    evm.precompiles_mut().extend_precompiles(factory.precompiles());
2237                }
2238                let cheats = self.cheats().clone();
2239                if cheats.has_recover_overrides() {
2240                    let cheats_arc = Arc::new(cheats.clone());
2241                    let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats_arc));
2242                    evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
2243                        Some(DynPrecompile::new_stateful(
2244                            cheat_ecrecover.precompile_id().clone(),
2245                            move |input| cheat_ecrecover.call(input),
2246                        ))
2247                    });
2248                }
2249
2250                // 4. Create executor via AnvilBlockExecutorFactory
2251                let exec_ctx = AnvilExecutionCtx { parent_hash: best_hash, is_prague, is_cancun };
2252                let mut executor = AnvilBlockExecutorFactory::create_executor(evm, exec_ctx);
2253                executor.apply_pre_execution_changes().expect("pre-execution changes failed");
2254
2255                // 5. Per-tx loop
2256                let mut included: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = Vec::new();
2257                let mut invalid: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = Vec::new();
2258                let mut transaction_infos: Vec<TransactionInfo> = Vec::new();
2259                let mut transactions = Vec::new();
2260                let mut bloom = Bloom::default();
2261
2262                let blob_params = self.blob_params();
2263                let networks = env.networks;
2264                let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
2265
2266                for pool_tx in pool_transactions {
2267                    let pending = &pool_tx.pending_transaction;
2268                    let sender = *pending.sender();
2269
2270                    let account = match executor
2271                        .evm_mut()
2272                        .db_mut()
2273                        .basic(sender)
2274                        .map(|a| a.unwrap_or_default())
2275                    {
2276                        Ok(acc) => acc,
2277                        Err(err) => {
2278                            trace!(target: "backend", ?err, "db error for tx {:?}, skipping", pool_tx.hash());
2279                            continue;
2280                        }
2281                    };
2282
2283                    // Build the per-tx env
2284                    let tx_env = build_tx_env_for_pending(pending, &cheats, networks, &env.evm_env);
2285                    let full_env = Env::new(env.evm_env.clone(), tx_env.clone(), networks);
2286
2287                    // Gas limit checks (same logic as TransactionExecutor::next)
2288                    let cumulative_gas =
2289                        executor.receipts().last().map(|r| r.cumulative_gas_used()).unwrap_or(0);
2290                    let max_block_gas =
2291                        cumulative_gas.saturating_add(pending.transaction.gas_limit());
2292                    if !env.evm_env.cfg_env.disable_block_gas_limit && max_block_gas > gas_limit {
2293                        trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "block gas limit exhausting, skipping transaction");
2294                        continue;
2295                    }
2296
2297                    // Osaka EIP-7825 tx gas limit cap check
2298                    if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
2299                        && pending.transaction.gas_limit() > env.evm_env.cfg_env.tx_gas_limit_cap()
2300                    {
2301                        trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "transaction gas limit exhausting, skipping transaction");
2302                        continue;
2303                    }
2304
2305                    // Blob gas check
2306                    let tx_blob_gas = pending.transaction.blob_gas_used().unwrap_or(0);
2307                    let current_blob_gas = cumulative_blob_gas_used.unwrap_or(0);
2308                    if current_blob_gas.saturating_add(tx_blob_gas)
2309                        > blob_params.max_blob_gas_per_block()
2310                    {
2311                        trace!(target: "backend", blob_gas = %tx_blob_gas, ?pool_tx, "block blob gas limit exhausting, skipping transaction");
2312                        continue;
2313                    }
2314
2315                    // Validate
2316                    if let Err(err) =
2317                        self.validate_pool_transaction_for(pending, &account, &full_env)
2318                    {
2319                        warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", pool_tx.hash(), err);
2320                        invalid.push(pool_tx.clone());
2321                        continue;
2322                    }
2323
2324                    let nonce = account.nonce;
2325
2326                    // Execute via block executor
2327                    let recovered =
2328                        Recovered::new_unchecked(pending.transaction.as_ref().clone(), sender);
2329                    trace!(target: "backend", "[{:?}] executing", pool_tx.hash());
2330                    match executor.execute_transaction_without_commit((tx_env, recovered)) {
2331                        Ok(result) => {
2332                            let exec_result = result.inner.result.result.clone();
2333                            let gas_used = result.inner.result.result.gas_used();
2334
2335                            executor.commit_transaction(result).expect("commit failed");
2336
2337                            // Drain per-tx traces from inspector
2338                            let insp = executor.evm_mut().inspector_mut();
2339
2340                            // Print before draining so the tracer is still populated.
2341                            if self.print_traces {
2342                                insp.print_traces(self.call_trace_decoder.clone());
2343                            }
2344                            insp.print_logs();
2345
2346                            let traces = insp
2347                                .tracer
2348                                .take()
2349                                .map(|t| t.into_traces().into_nodes())
2350                                .unwrap_or_default();
2351
2352                            // Reinstall tracer for next tx
2353                            if self.enable_steps_tracing {
2354                                insp.tracer = Some(TracingInspector::new(
2355                                    TracingInspectorConfig::all().with_state_diffs(),
2356                                ));
2357                            } else {
2358                                insp.tracer = Some(TracingInspector::new(
2359                                    TracingInspectorConfig::all().set_steps(false),
2360                                ));
2361                            }
2362                            // Reset log collector for next tx
2363                            if self.print_logs {
2364                                insp.log_collector =
2365                                    Some(foundry_evm::inspectors::LogCollector::Capture {
2366                                        logs: Vec::new(),
2367                                    });
2368                            }
2369
2370                            // Track blob gas
2371                            if is_cancun {
2372                                cumulative_blob_gas_used = Some(
2373                                    cumulative_blob_gas_used
2374                                        .unwrap_or(0)
2375                                        .saturating_add(tx_blob_gas),
2376                                );
2377                            }
2378
2379                            let (exit_reason, out, logs) = match exec_result {
2380                                ExecutionResult::Success {
2381                                    reason,
2382                                    gas_used: _,
2383                                    logs,
2384                                    output,
2385                                    ..
2386                                } => (reason.into(), Some(output), logs),
2387                                ExecutionResult::Revert { gas_used: _, output } => (
2388                                    InstructionResult::Revert,
2389                                    Some(Output::Call(output)),
2390                                    Vec::new(),
2391                                ),
2392                                ExecutionResult::Halt { reason, gas_used: _ } => {
2393                                    (op_haltreason_to_instruction_result(reason), None, Vec::new())
2394                                }
2395                            };
2396
2397                            if exit_reason == InstructionResult::OutOfGas {
2398                                warn!(target: "backend", "[{:?}] executed with out of gas", pool_tx.hash());
2399                            }
2400
2401                            trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", pool_tx.hash(), out);
2402                            trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", pool_tx.hash(), exit_reason, gas_used);
2403
2404                            // Build bloom from logs
2405                            for log in &logs {
2406                                bloom.accrue(BloomInput::Raw(&log.address[..]));
2407                                for topic in log.topics() {
2408                                    bloom.accrue(BloomInput::Raw(&topic[..]));
2409                                }
2410                            }
2411
2412                            // Contract address for creation txs
2413                            let contract_address = if pending.transaction.to().is_none() {
2414                                let addr = sender.create(nonce);
2415                                trace!(target: "backend", "Contract creation tx: computed address {:?}", addr);
2416                                Some(addr)
2417                            } else {
2418                                None
2419                            };
2420
2421                            let transaction_index = transaction_infos.len() as u64;
2422                            let info = TransactionInfo {
2423                                transaction_hash: pool_tx.hash(),
2424                                transaction_index,
2425                                from: sender,
2426                                to: pending.transaction.to(),
2427                                contract_address,
2428                                traces,
2429                                exit: exit_reason,
2430                                out: out.map(Output::into_data),
2431                                nonce,
2432                                gas_used,
2433                            };
2434
2435                            included.push(pool_tx.clone());
2436                            transaction_infos.push(info);
2437                            transactions.push(pending.transaction.clone());
2438                        }
2439                        Err(err) => {
2440                            trace!(target: "backend", ?err, "tx execution error, skipping {:?}", pool_tx.hash());
2441                        }
2442                    }
2443                }
2444
2445                // 6. Finish — drop EVM BEFORE accessing db again
2446                let (evm, block_result) = executor.finish().expect("executor finish failed");
2447                drop(evm);
2448
2449                let state_root = db.maybe_state_root().unwrap_or_default();
2450
2451                // 7. Build block header
2452                let receipts_root = calculate_receipt_root(&block_result.receipts);
2453
2454                let cumulative_gas_used = block_result.gas_used;
2455
2456                let header = Header {
2457                    parent_hash: best_hash,
2458                    ommers_hash: Default::default(),
2459                    beneficiary,
2460                    state_root,
2461                    transactions_root: Default::default(),
2462                    receipts_root,
2463                    logs_bloom: bloom,
2464                    difficulty,
2465                    number: block_number,
2466                    gas_limit,
2467                    gas_used: cumulative_gas_used,
2468                    timestamp: timestamp.saturating_to(),
2469                    extra_data: Default::default(),
2470                    mix_hash: mix_hash.unwrap_or_default(),
2471                    nonce: Default::default(),
2472                    base_fee_per_gas: base_fee,
2473                    parent_beacon_block_root: is_cancun.then_some(Default::default()),
2474                    blob_gas_used: cumulative_blob_gas_used,
2475                    excess_blob_gas,
2476                    withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
2477                    requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
2478                };
2479
2480                let block = create_block(header, transactions);
2481                let block_info = TypedBlockInfo {
2482                    block,
2483                    transactions: transaction_infos,
2484                    receipts: block_result.receipts,
2485                };
2486
2487                // update the new blockhash in the db itself
2488                let block_hash = block_info.block.header.hash_slow();
2489                db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash);
2490
2491                (block_info, included, invalid, block_hash)
2492            };
2493
2494            // create the new block with the current timestamp
2495            let BlockInfo { block, transactions, receipts } = block_info;
2496
2497            let header = block.header.clone();
2498
2499            trace!(
2500                target: "backend",
2501                "Mined block {} with {} tx {:?}",
2502                block_number,
2503                transactions.len(),
2504                transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
2505            );
2506            let mut storage = self.blockchain.storage.write();
2507            // update block metadata
2508            storage.best_number = block_number;
2509            storage.best_hash = block_hash;
2510            // Difficulty is removed and not used after Paris (aka TheMerge). Value is replaced with
2511            // prevrandao. https://github.com/bluealloy/revm/blob/1839b3fce8eaeebb85025576f2519b80615aca1e/crates/interpreter/src/instructions/host_env.rs#L27
2512            if !self.is_eip3675() {
2513                storage.total_difficulty =
2514                    storage.total_difficulty.saturating_add(header.difficulty);
2515            }
2516
2517            storage.blocks.insert(block_hash, block);
2518            storage.hashes.insert(block_number, block_hash);
2519
2520            node_info!("");
2521            // insert all transactions
2522            for (info, receipt) in transactions.into_iter().zip(receipts) {
2523                // log some tx info
2524                node_info!("    Transaction: {:?}", info.transaction_hash);
2525                if let Some(contract) = &info.contract_address {
2526                    node_info!("    Contract created: {contract}");
2527                }
2528                node_info!("    Gas used: {}", receipt.cumulative_gas_used());
2529                if !info.exit.is_ok() {
2530                    let r = RevertDecoder::new().decode(
2531                        info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
2532                        Some(info.exit),
2533                    );
2534                    node_info!("    Error: reverted with: {r}");
2535                }
2536                node_info!("");
2537
2538                let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
2539                storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
2540            }
2541
2542            // remove old transactions that exceed the transaction block keeper
2543            if let Some(transaction_block_keeper) = self.transaction_block_keeper
2544                && storage.blocks.len() > transaction_block_keeper
2545            {
2546                let to_clear = block_number
2547                    .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
2548                storage.remove_block_transactions_by_number(to_clear)
2549            }
2550
2551            // we intentionally set the difficulty to `0` for newer blocks
2552            env.evm_env.block_env.difficulty = U256::from(0);
2553
2554            // update env with new values
2555            *self.env.write() = env;
2556
2557            let timestamp = utc_from_secs(header.timestamp);
2558
2559            node_info!("    Block Number: {}", block_number);
2560            node_info!("    Block Hash: {:?}", block_hash);
2561            if timestamp.year() > 9999 {
2562                // rf2822 panics with more than 4 digits
2563                node_info!("    Block Time: {:?}\n", timestamp.to_rfc3339());
2564            } else {
2565                node_info!("    Block Time: {:?}\n", timestamp.to_rfc2822());
2566            }
2567
2568            let outcome = MinedBlockOutcome { block_number, included, invalid };
2569
2570            (outcome, header, block_hash)
2571        };
2572        let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
2573            header.gas_used,
2574            header.gas_limit,
2575            header.base_fee_per_gas.unwrap_or_default(),
2576        );
2577        let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
2578            header.excess_blob_gas.unwrap_or_default(),
2579            header.blob_gas_used.unwrap_or_default(),
2580        );
2581
2582        // update next base fee
2583        self.fees.set_base_fee(next_block_base_fee);
2584
2585        self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
2586            next_block_excess_blob_gas,
2587            get_blob_base_fee_update_fraction_by_spec_id(*self.env.read().evm_env.spec_id()),
2588        ));
2589
2590        // notify all listeners
2591        self.notify_on_new_block(header, block_hash);
2592
2593        outcome
2594    }
2595
2596    /// Reorg the chain to a common height and execute blocks to build new chain.
2597    ///
2598    /// The state of the chain is rewound using `rewind` to the common block, including the db,
2599    /// storage, and env.
2600    ///
2601    /// Finally, `do_mine_block` is called to create the new chain.
2602    pub async fn reorg(
2603        &self,
2604        depth: u64,
2605        tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>>,
2606        common_block: Block,
2607    ) -> Result<(), BlockchainError> {
2608        self.rollback(common_block).await?;
2609        // Create the new reorged chain, filling the blocks with transactions if supplied
2610        for i in 0..depth {
2611            let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
2612            let outcome = self.do_mine_block(to_be_mined).await;
2613            node_info!(
2614                "    Mined reorg block number {}. With {} valid txs and with invalid {} txs",
2615                outcome.block_number,
2616                outcome.included.len(),
2617                outcome.invalid.len()
2618            );
2619        }
2620
2621        Ok(())
2622    }
2623
2624    /// Creates the pending block
2625    ///
2626    /// This will execute all transaction in the order they come but will not mine the block
2627    pub async fn pending_block(
2628        &self,
2629        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2630    ) -> BlockInfo {
2631        self.with_pending_block(pool_transactions, |_, block| block).await
2632    }
2633
2634    /// Creates the pending block
2635    ///
2636    /// This will execute all transaction in the order they come but will not mine the block
2637    pub async fn with_pending_block<F, T>(
2638        &self,
2639        pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2640        f: F,
2641    ) -> T
2642    where
2643        F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo) -> T,
2644    {
2645        let db = self.db.read().await;
2646        let env = self.next_env();
2647
2648        let mut cache_db = AnvilCacheDB::new(&*db);
2649
2650        let parent_hash = self.blockchain.storage.read().best_hash;
2651
2652        let spec_id = *env.evm_env.spec_id();
2653        let is_shanghai = spec_id >= SpecId::SHANGHAI;
2654        let is_cancun = spec_id >= SpecId::CANCUN;
2655        let is_prague = spec_id >= SpecId::PRAGUE;
2656        let gas_limit = env.evm_env.block_env.gas_limit;
2657        let difficulty = env.evm_env.block_env.difficulty;
2658        let mix_hash = env.evm_env.block_env.prevrandao;
2659        let beneficiary = env.evm_env.block_env.beneficiary;
2660        let timestamp = env.evm_env.block_env.timestamp;
2661        let base_fee =
2662            if spec_id >= SpecId::LONDON { Some(env.evm_env.block_env.basefee) } else { None };
2663        let excess_blob_gas =
2664            if is_cancun { env.evm_env.block_env.blob_excess_gas() } else { None };
2665
2666        let mut inspector = AnvilInspector::default().with_tracing();
2667        if self.enable_steps_tracing {
2668            inspector = inspector.with_steps_tracing();
2669        }
2670        if self.print_logs {
2671            inspector = inspector.with_log_collector();
2672        }
2673        if self.print_traces {
2674            inspector = inspector.with_trace_printer();
2675        }
2676
2677        let env_struct = Env::new(env.evm_env.clone(), Default::default(), env.networks);
2678        let mut evm = new_eth_evm_with_inspector(&mut cache_db, &env_struct, inspector);
2679
2680        env.networks.inject_precompiles(evm.precompiles_mut());
2681        if let Some(factory) = &self.precompile_factory {
2682            evm.precompiles_mut().extend_precompiles(factory.precompiles());
2683        }
2684        let cheats = self.cheats().clone();
2685        if cheats.has_recover_overrides() {
2686            let cheats_arc = Arc::new(cheats.clone());
2687            let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats_arc));
2688            evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
2689                Some(DynPrecompile::new_stateful(
2690                    cheat_ecrecover.precompile_id().clone(),
2691                    move |input| cheat_ecrecover.call(input),
2692                ))
2693            });
2694        }
2695
2696        let exec_ctx = AnvilExecutionCtx { parent_hash, is_prague, is_cancun };
2697        let mut executor = AnvilBlockExecutorFactory::create_executor(evm, exec_ctx);
2698        executor.apply_pre_execution_changes().expect("pre-execution changes failed");
2699
2700        let mut transaction_infos: Vec<TransactionInfo> = Vec::new();
2701        let mut transactions = Vec::new();
2702        let mut bloom = Bloom::default();
2703
2704        let blob_params = self.blob_params();
2705        let networks = env.networks;
2706        let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
2707
2708        for pool_tx in pool_transactions {
2709            let pending = &pool_tx.pending_transaction;
2710            let sender = *pending.sender();
2711
2712            let account = match executor
2713                .evm_mut()
2714                .db_mut()
2715                .basic(sender)
2716                .map(|a| a.unwrap_or_default())
2717            {
2718                Ok(acc) => acc,
2719                Err(err) => {
2720                    trace!(target: "backend", ?err, "db error for tx {:?}, skipping", pool_tx.hash());
2721                    continue;
2722                }
2723            };
2724
2725            let tx_env = build_tx_env_for_pending(pending, &cheats, networks, &env.evm_env);
2726            let full_env = Env::new(env.evm_env.clone(), tx_env.clone(), networks);
2727
2728            let cumulative_gas =
2729                executor.receipts().last().map(|r| r.cumulative_gas_used()).unwrap_or(0);
2730            let max_block_gas = cumulative_gas.saturating_add(pending.transaction.gas_limit());
2731            if !env.evm_env.cfg_env.disable_block_gas_limit && max_block_gas > gas_limit {
2732                trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "block gas limit exhausting, skipping transaction");
2733                continue;
2734            }
2735
2736            if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
2737                && pending.transaction.gas_limit() > env.evm_env.cfg_env.tx_gas_limit_cap()
2738            {
2739                trace!(target: "backend", tx_gas_limit = %pending.transaction.gas_limit(), ?pool_tx, "transaction gas limit exhausting, skipping transaction");
2740                continue;
2741            }
2742
2743            let tx_blob_gas = pending.transaction.blob_gas_used().unwrap_or(0);
2744            let current_blob_gas = cumulative_blob_gas_used.unwrap_or(0);
2745            if current_blob_gas.saturating_add(tx_blob_gas) > blob_params.max_blob_gas_per_block() {
2746                trace!(target: "backend", blob_gas = %tx_blob_gas, ?pool_tx, "block blob gas limit exhausting, skipping transaction");
2747                continue;
2748            }
2749
2750            if let Err(err) = self.validate_pool_transaction_for(pending, &account, &full_env) {
2751                warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", pool_tx.hash(), err);
2752                continue;
2753            }
2754
2755            let nonce = account.nonce;
2756
2757            let recovered = Recovered::new_unchecked(pending.transaction.as_ref().clone(), sender);
2758            trace!(target: "backend", "[{:?}] executing", pool_tx.hash());
2759            match executor.execute_transaction_without_commit((tx_env, recovered)) {
2760                Ok(result) => {
2761                    let exec_result = result.inner.result.result.clone();
2762                    let gas_used = result.inner.result.result.gas_used();
2763
2764                    executor.commit_transaction(result).expect("commit failed");
2765
2766                    let insp = executor.evm_mut().inspector_mut();
2767
2768                    if self.print_traces {
2769                        insp.print_traces(self.call_trace_decoder.clone());
2770                    }
2771                    insp.print_logs();
2772
2773                    let traces = insp
2774                        .tracer
2775                        .take()
2776                        .map(|t| t.into_traces().into_nodes())
2777                        .unwrap_or_default();
2778
2779                    if self.enable_steps_tracing {
2780                        insp.tracer = Some(TracingInspector::new(
2781                            TracingInspectorConfig::all().with_state_diffs(),
2782                        ));
2783                    } else {
2784                        insp.tracer = Some(TracingInspector::new(
2785                            TracingInspectorConfig::all().set_steps(false),
2786                        ));
2787                    }
2788                    if self.print_logs {
2789                        insp.log_collector = Some(foundry_evm::inspectors::LogCollector::Capture {
2790                            logs: Vec::new(),
2791                        });
2792                    }
2793
2794                    if is_cancun {
2795                        cumulative_blob_gas_used =
2796                            Some(cumulative_blob_gas_used.unwrap_or(0).saturating_add(tx_blob_gas));
2797                    }
2798
2799                    let (exit_reason, out, logs) = match exec_result {
2800                        ExecutionResult::Success { reason, gas_used: _, logs, output, .. } => {
2801                            (reason.into(), Some(output), logs)
2802                        }
2803                        ExecutionResult::Revert { gas_used: _, output } => {
2804                            (InstructionResult::Revert, Some(Output::Call(output)), Vec::new())
2805                        }
2806                        ExecutionResult::Halt { reason, gas_used: _ } => {
2807                            (op_haltreason_to_instruction_result(reason), None, Vec::new())
2808                        }
2809                    };
2810
2811                    for log in &logs {
2812                        bloom.accrue(BloomInput::Raw(&log.address[..]));
2813                        for topic in log.topics() {
2814                            bloom.accrue(BloomInput::Raw(&topic[..]));
2815                        }
2816                    }
2817
2818                    let contract_address = if pending.transaction.to().is_none() {
2819                        let addr = sender.create(nonce);
2820                        Some(addr)
2821                    } else {
2822                        None
2823                    };
2824
2825                    let transaction_index = transaction_infos.len() as u64;
2826                    let info = TransactionInfo {
2827                        transaction_hash: pool_tx.hash(),
2828                        transaction_index,
2829                        from: sender,
2830                        to: pending.transaction.to(),
2831                        contract_address,
2832                        traces,
2833                        exit: exit_reason,
2834                        out: out.map(Output::into_data),
2835                        nonce,
2836                        gas_used,
2837                    };
2838
2839                    transaction_infos.push(info);
2840                    transactions.push(pending.transaction.clone());
2841                }
2842                Err(err) => {
2843                    trace!(target: "backend", ?err, "tx execution error, skipping {:?}", pool_tx.hash());
2844                }
2845            }
2846        }
2847
2848        let (evm, block_result) = executor.finish().expect("executor finish failed");
2849        drop(evm);
2850
2851        // Extract inner CacheDB (which implements MaybeFullDatabase)
2852        let cache_db = cache_db.0;
2853
2854        let state_root = cache_db.maybe_state_root().unwrap_or_default();
2855        let receipts_root = calculate_receipt_root(&block_result.receipts);
2856        let cumulative_gas_used = block_result.gas_used;
2857
2858        let header = Header {
2859            parent_hash,
2860            ommers_hash: Default::default(),
2861            beneficiary,
2862            state_root,
2863            transactions_root: Default::default(),
2864            receipts_root,
2865            logs_bloom: bloom,
2866            difficulty,
2867            number: env.evm_env.block_env.number.saturating_to(),
2868            gas_limit,
2869            gas_used: cumulative_gas_used,
2870            timestamp: timestamp.saturating_to(),
2871            extra_data: Default::default(),
2872            mix_hash: mix_hash.unwrap_or_default(),
2873            nonce: Default::default(),
2874            base_fee_per_gas: base_fee,
2875            parent_beacon_block_root: is_cancun.then_some(Default::default()),
2876            blob_gas_used: cumulative_blob_gas_used,
2877            excess_blob_gas,
2878            withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
2879            requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
2880        };
2881
2882        let block = create_block(header, transactions);
2883        let block_info = TypedBlockInfo {
2884            block,
2885            transactions: transaction_infos,
2886            receipts: block_result.receipts,
2887        };
2888
2889        f(Box::new(cache_db), block_info)
2890    }
2891
2892    /// Executes the [TransactionRequest] without writing to the DB
2893    ///
2894    /// # Errors
2895    ///
2896    /// Returns an error if the `block_number` is greater than the current height
2897    pub async fn call(
2898        &self,
2899        request: WithOtherFields<TransactionRequest>,
2900        fee_details: FeeDetails,
2901        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2902        overrides: EvmOverrides,
2903    ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2904        self.with_database_at(block_request, |state, mut block| {
2905            let block_number = block.number;
2906            let (exit, out, gas, state) = {
2907                let mut cache_db = CacheDB::new(state);
2908                if let Some(state_overrides) = overrides.state {
2909                    apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
2910                }
2911                if let Some(block_overrides) = overrides.block {
2912                    cache_db.apply_block_overrides(*block_overrides, &mut block);
2913                }
2914                self.call_with_state(&cache_db, request, fee_details, block)
2915            }?;
2916            trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
2917            Ok((exit, out, gas, state))
2918        }).await?
2919    }
2920
2921    pub async fn call_with_tracing(
2922        &self,
2923        request: WithOtherFields<TransactionRequest>,
2924        fee_details: FeeDetails,
2925        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2926        opts: GethDebugTracingCallOptions,
2927    ) -> Result<GethTrace, BlockchainError> {
2928        let GethDebugTracingCallOptions {
2929            tracing_options, block_overrides, state_overrides, ..
2930        } = opts;
2931        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
2932
2933        self.with_database_at(block_request, |state, mut block| {
2934            let block_number = block.number;
2935
2936            let mut cache_db = CacheDB::new(state);
2937            if let Some(state_overrides) = state_overrides {
2938                apply_state_overrides(state_overrides, &mut cache_db)?;
2939            }
2940            if let Some(block_overrides) = block_overrides {
2941                cache_db.apply_block_overrides(block_overrides, &mut block);
2942            }
2943
2944            if let Some(tracer) = tracer {
2945                return match tracer {
2946                    GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
2947                        GethDebugBuiltInTracerType::CallTracer => {
2948                            let call_config = tracer_config
2949                                .into_call_config()
2950                                .map_err(|e| RpcError::invalid_params(e.to_string()))?;
2951
2952                            let mut inspector = self.build_inspector().with_tracing_config(
2953                                TracingInspectorConfig::from_geth_call_config(&call_config),
2954                            );
2955
2956                            let env = self.build_call_env(request, fee_details, block);
2957                            let mut evm = self.new_eth_evm_with_inspector_ref(
2958                                &cache_db,
2959                                &env,
2960                                &mut inspector,
2961                            );
2962                            let ResultAndState { result, state: _ } = evm.transact(env.tx)?;
2963
2964                            drop(evm);
2965
2966                            inspector.print_logs();
2967                            if self.print_traces {
2968                                inspector.print_traces(self.call_trace_decoder.clone());
2969                            }
2970
2971                            let tracing_inspector = inspector.tracer.expect("tracer disappeared");
2972
2973                            Ok(tracing_inspector
2974                                .into_geth_builder()
2975                                .geth_call_traces(call_config, result.gas_used())
2976                                .into())
2977                        }
2978                        GethDebugBuiltInTracerType::PreStateTracer => {
2979                            let pre_state_config = tracer_config
2980                                .into_pre_state_config()
2981                                .map_err(|e| RpcError::invalid_params(e.to_string()))?;
2982
2983                            let mut inspector = TracingInspector::new(
2984                                TracingInspectorConfig::from_geth_prestate_config(
2985                                    &pre_state_config,
2986                                ),
2987                            );
2988
2989                            let env = self.build_call_env(request, fee_details, block);
2990                            let mut evm = self.new_eth_evm_with_inspector_ref(
2991                                &cache_db,
2992                                &env,
2993                                &mut inspector,
2994                            );
2995                            let result = evm.transact(env.tx)?;
2996
2997                            drop(evm);
2998
2999                            Ok(inspector
3000                                .into_geth_builder()
3001                                .geth_prestate_traces(&result, &pre_state_config, cache_db)?
3002                                .into())
3003                        }
3004                        GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
3005                        GethDebugBuiltInTracerType::FourByteTracer
3006                        | GethDebugBuiltInTracerType::MuxTracer
3007                        | GethDebugBuiltInTracerType::FlatCallTracer
3008                        | GethDebugBuiltInTracerType::Erc7562Tracer => {
3009                            Err(RpcError::invalid_params("unsupported tracer type").into())
3010                        }
3011                    },
3012                    #[cfg(not(feature = "js-tracer"))]
3013                    GethDebugTracerType::JsTracer(_) => {
3014                        Err(RpcError::invalid_params("unsupported tracer type").into())
3015                    }
3016                    #[cfg(feature = "js-tracer")]
3017                    GethDebugTracerType::JsTracer(code) => {
3018                        use alloy_evm::IntoTxEnv;
3019                        let config = tracer_config.into_json();
3020                        let mut inspector =
3021                            revm_inspectors::tracing::js::JsInspector::new(code, config)
3022                                .map_err(|err| BlockchainError::Message(err.to_string()))?;
3023
3024                        let env = self.build_call_env(request, fee_details, block.clone());
3025                        let mut evm =
3026                            self.new_eth_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
3027                        let result = evm.transact(env.tx.clone())?;
3028                        let res = evm
3029                            .inspector_mut()
3030                            .json_result(result, &env.tx.into_tx_env(), &block, &cache_db)
3031                            .map_err(|err| BlockchainError::Message(err.to_string()))?;
3032
3033                        Ok(GethTrace::JS(res))
3034                    }
3035                };
3036            }
3037
3038            // defaults to StructLog tracer used since no tracer is specified
3039            let mut inspector = self
3040                .build_inspector()
3041                .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
3042
3043            let env = self.build_call_env(request, fee_details, block);
3044            let mut evm = self.new_eth_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
3045            let ResultAndState { result, state: _ } = evm.transact(env.tx)?;
3046
3047            let (exit_reason, gas_used, out) = match result {
3048                ExecutionResult::Success { reason, gas_used, output, .. } => {
3049                    (reason.into(), gas_used, Some(output))
3050                }
3051                ExecutionResult::Revert { gas_used, output } => {
3052                    (InstructionResult::Revert, gas_used, Some(Output::Call(output)))
3053                }
3054                ExecutionResult::Halt { reason, gas_used } => {
3055                    (op_haltreason_to_instruction_result(reason), gas_used, None)
3056                }
3057            };
3058
3059            drop(evm);
3060            let tracing_inspector = inspector.tracer.expect("tracer disappeared");
3061            let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
3062
3063            trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
3064
3065            let res = tracing_inspector
3066                .into_geth_builder()
3067                .geth_traces(gas_used, return_value, config)
3068                .into();
3069
3070            Ok(res)
3071        })
3072        .await?
3073    }
3074
3075    /// Helper function to execute a closure with the database at a specific block
3076    pub async fn with_database_at<F, T>(
3077        &self,
3078        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3079        f: F,
3080    ) -> Result<T, BlockchainError>
3081    where
3082        F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
3083    {
3084        let block_number = match block_request {
3085            Some(BlockRequest::Pending(pool_transactions)) => {
3086                let result = self
3087                    .with_pending_block(pool_transactions, |state, block| {
3088                        let block = block.block;
3089                        f(state, block_env_from_header(&block.header))
3090                    })
3091                    .await;
3092                return Ok(result);
3093            }
3094            Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
3095            None => None,
3096        };
3097        let block_number = self.convert_block_number(block_number);
3098        let current_number = self.best_number();
3099
3100        // Reject requests for future blocks that don't exist yet
3101        if block_number > current_number {
3102            return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3103        }
3104
3105        if block_number < current_number {
3106            if let Some((block_hash, block)) = self
3107                .block_by_number(BlockNumber::Number(block_number))
3108                .await?
3109                .map(|block| (block.header.hash, block))
3110            {
3111                let read_guard = self.states.upgradable_read();
3112                if let Some(state_db) = read_guard.get_state(&block_hash) {
3113                    return Ok(f(Box::new(state_db), block_env_from_header(&block.header)));
3114                } else {
3115                    let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3116                    if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
3117                        return Ok(f(Box::new(state), block_env_from_header(&block.header)));
3118                    }
3119                }
3120            }
3121
3122            warn!(target: "backend", "Not historic state found for block={}", block_number);
3123            return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3124        }
3125
3126        let db = self.db.read().await;
3127        let block = self.env.read().evm_env.block_env.clone();
3128        Ok(f(Box::new(&**db), block))
3129    }
3130
3131    pub async fn storage_at(
3132        &self,
3133        address: Address,
3134        index: U256,
3135        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3136    ) -> Result<B256, BlockchainError> {
3137        self.with_database_at(block_request, |db, _| {
3138            trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
3139            let val = db.storage_ref(address, index)?;
3140            Ok(val.into())
3141        })
3142        .await?
3143    }
3144
3145    /// Returns the code of the address
3146    ///
3147    /// If the code is not present and fork mode is enabled then this will try to fetch it from the
3148    /// forked client
3149    pub async fn get_code(
3150        &self,
3151        address: Address,
3152        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3153    ) -> Result<Bytes, BlockchainError> {
3154        self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
3155    }
3156
3157    /// Returns the balance of the address
3158    ///
3159    /// If the requested number predates the fork then this will fetch it from the endpoint
3160    pub async fn get_balance(
3161        &self,
3162        address: Address,
3163        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3164    ) -> Result<U256, BlockchainError> {
3165        self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
3166            .await?
3167    }
3168
3169    pub async fn get_account_at_block(
3170        &self,
3171        address: Address,
3172        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3173    ) -> Result<TrieAccount, BlockchainError> {
3174        self.with_database_at(block_request, |block_db, _| {
3175            let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3176            let account = db.get(&address).cloned().unwrap_or_default();
3177            let storage_root = storage_root(&account.storage);
3178            let code_hash = account.info.code_hash;
3179            let balance = account.info.balance;
3180            let nonce = account.info.nonce;
3181            Ok(TrieAccount { balance, nonce, code_hash, storage_root })
3182        })
3183        .await?
3184    }
3185
3186    /// Returns the nonce of the address
3187    ///
3188    /// If the requested number predates the fork then this will fetch it from the endpoint
3189    pub async fn get_nonce(
3190        &self,
3191        address: Address,
3192        block_request: BlockRequest<FoundryTxEnvelope>,
3193    ) -> Result<u64, BlockchainError> {
3194        if let BlockRequest::Pending(pool_transactions) = &block_request
3195            && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
3196        {
3197            return Ok(value);
3198        }
3199        let final_block_request = match block_request {
3200            BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
3201            BlockRequest::Number(bn) => BlockRequest::Number(bn),
3202        };
3203
3204        self.with_database_at(Some(final_block_request), |db, _| {
3205            trace!(target: "backend", "get nonce for {:?}", address);
3206            Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
3207        })
3208        .await?
3209    }
3210
3211    fn replay_tx_with_inspector<I, F, T>(
3212        &self,
3213        hash: B256,
3214        mut inspector: I,
3215        f: F,
3216    ) -> Result<T, BlockchainError>
3217    where
3218        for<'a> I: Inspector<EthEvmContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
3219            + Inspector<OpContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
3220            + 'a,
3221        for<'a> F:
3222            FnOnce(ResultAndState<OpHaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, Env) -> T,
3223    {
3224        let block = {
3225            let storage = self.blockchain.storage.read();
3226            let MinedTransaction { block_hash, .. } = storage
3227                .transactions
3228                .get(&hash)
3229                .cloned()
3230                .ok_or(BlockchainError::TransactionNotFound)?;
3231
3232            storage.blocks.get(&block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
3233        };
3234
3235        let index = block
3236            .body
3237            .transactions
3238            .iter()
3239            .position(|tx| tx.hash() == hash)
3240            .expect("transaction not found in block");
3241
3242        let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
3243            [..index]
3244            .iter()
3245            .map(|tx| {
3246                let pending_tx =
3247                    PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
3248                Arc::new(PoolTransaction {
3249                    pending_transaction: pending_tx,
3250                    requires: vec![],
3251                    provides: vec![],
3252                    priority: crate::eth::pool::transactions::TransactionPriority(0),
3253                })
3254            })
3255            .collect();
3256
3257        let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
3258            let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
3259
3260            // configure the blockenv for the block of the transaction
3261            let mut env = self.env.read().clone();
3262
3263            env.evm_env.block_env = block_env_from_header(&block.header);
3264
3265            let spec_id = *env.evm_env.spec_id();
3266            let is_cancun = spec_id >= SpecId::CANCUN;
3267            let is_prague = spec_id >= SpecId::PRAGUE;
3268            let gas_limit = env.evm_env.block_env.gas_limit;
3269
3270            let mut inspector_replay = AnvilInspector::default().with_tracing();
3271            if self.enable_steps_tracing {
3272                inspector_replay = inspector_replay.with_steps_tracing();
3273            }
3274            if self.print_logs {
3275                inspector_replay = inspector_replay.with_log_collector();
3276            }
3277            if self.print_traces {
3278                inspector_replay = inspector_replay.with_trace_printer();
3279            }
3280
3281            let env_struct = Env::new(env.evm_env.clone(), Default::default(), env.networks);
3282            let mut evm_replay =
3283                new_eth_evm_with_inspector(&mut cache_db, &env_struct, inspector_replay);
3284
3285            env.networks.inject_precompiles(evm_replay.precompiles_mut());
3286            if let Some(factory) = &self.precompile_factory {
3287                evm_replay.precompiles_mut().extend_precompiles(factory.precompiles());
3288            }
3289            let cheats = self.cheats().clone();
3290            if cheats.has_recover_overrides() {
3291                let cheats_arc = Arc::new(cheats.clone());
3292                let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats_arc));
3293                evm_replay.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
3294                    Some(DynPrecompile::new_stateful(
3295                        cheat_ecrecover.precompile_id().clone(),
3296                        move |input| cheat_ecrecover.call(input),
3297                    ))
3298                });
3299            }
3300
3301            let exec_ctx =
3302                AnvilExecutionCtx { parent_hash: block.header.parent_hash, is_prague, is_cancun };
3303            let mut replay_executor =
3304                AnvilBlockExecutorFactory::create_executor(evm_replay, exec_ctx);
3305            replay_executor.apply_pre_execution_changes().expect("pre-execution changes failed");
3306
3307            let blob_params = self.blob_params();
3308            let networks = env.networks;
3309            let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
3310
3311            for pool_tx in pool_txs {
3312                let pending = &pool_tx.pending_transaction;
3313                let sender = *pending.sender();
3314
3315                let account = match replay_executor
3316                    .evm_mut()
3317                    .db_mut()
3318                    .basic(sender)
3319                    .map(|a| a.unwrap_or_default())
3320                {
3321                    Ok(acc) => acc,
3322                    Err(_) => continue,
3323                };
3324
3325                let tx_env = build_tx_env_for_pending(pending, &cheats, networks, &env.evm_env);
3326                let full_env = Env::new(env.evm_env.clone(), tx_env.clone(), networks);
3327
3328                let cumulative_gas =
3329                    replay_executor.receipts().last().map(|r| r.cumulative_gas_used()).unwrap_or(0);
3330                let max_block_gas = cumulative_gas.saturating_add(pending.transaction.gas_limit());
3331                if !env.evm_env.cfg_env.disable_block_gas_limit && max_block_gas > gas_limit {
3332                    continue;
3333                }
3334
3335                if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
3336                    && pending.transaction.gas_limit() > env.evm_env.cfg_env.tx_gas_limit_cap()
3337                {
3338                    continue;
3339                }
3340
3341                let tx_blob_gas = pending.transaction.blob_gas_used().unwrap_or(0);
3342                let current_blob_gas = cumulative_blob_gas_used.unwrap_or(0);
3343                if current_blob_gas.saturating_add(tx_blob_gas)
3344                    > blob_params.max_blob_gas_per_block()
3345                {
3346                    continue;
3347                }
3348
3349                if self.validate_pool_transaction_for(pending, &account, &full_env).is_err() {
3350                    continue;
3351                }
3352
3353                let recovered =
3354                    Recovered::new_unchecked(pending.transaction.as_ref().clone(), sender);
3355                if let Ok(result) =
3356                    replay_executor.execute_transaction_without_commit((tx_env, recovered))
3357                {
3358                    replay_executor.commit_transaction(result).expect("commit failed");
3359
3360                    let insp = replay_executor.evm_mut().inspector_mut();
3361                    insp.print_logs();
3362                    if self.print_traces {
3363                        insp.print_traces(self.call_trace_decoder.clone());
3364                    }
3365                    insp.tracer.take();
3366                    if self.enable_steps_tracing {
3367                        insp.tracer = Some(TracingInspector::new(
3368                            TracingInspectorConfig::all().with_state_diffs(),
3369                        ));
3370                    } else {
3371                        insp.tracer = Some(TracingInspector::new(
3372                            TracingInspectorConfig::all().set_steps(false),
3373                        ));
3374                    }
3375                    if self.print_logs {
3376                        insp.log_collector = Some(foundry_evm::inspectors::LogCollector::Capture {
3377                            logs: Vec::new(),
3378                        });
3379                    }
3380
3381                    if is_cancun {
3382                        cumulative_blob_gas_used =
3383                            Some(cumulative_blob_gas_used.unwrap_or(0).saturating_add(tx_blob_gas));
3384                    }
3385                }
3386            }
3387
3388            let (evm_done, _) = replay_executor.finish().expect("executor finish failed");
3389            drop(evm_done);
3390
3391            // Extract inner CacheDB to match the expected types for the target tx execution
3392            let cache_db = cache_db.0;
3393
3394            let target_tx = block.body.transactions[index].clone();
3395            let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
3396            let mut tx_env: OpTransaction<TxEnv> = FromRecoveredTx::from_recovered_tx(
3397                target_tx.transaction.as_ref(),
3398                *target_tx.sender(),
3399            );
3400            if env.networks.is_optimism() {
3401                tx_env.enveloped_tx = Some(target_tx.transaction.encoded_2718().into());
3402            }
3403
3404            let mut evm = self.new_eth_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
3405
3406            let result = evm
3407                .transact(tx_env.clone())
3408                .map_err(|err| BlockchainError::Message(err.to_string()))?;
3409
3410            Ok(f(result, cache_db, inspector, tx_env.base, env))
3411        };
3412
3413        let read_guard = self.states.upgradable_read();
3414        if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
3415            trace(state)
3416        } else {
3417            let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3418            let state = write_guard
3419                .get_on_disk_state(&block.header.parent_hash)
3420                .ok_or(BlockchainError::BlockNotFound)?;
3421            trace(state)
3422        }
3423    }
3424
3425    /// Traces the transaction with the js tracer
3426    #[cfg(feature = "js-tracer")]
3427    pub async fn trace_tx_with_js_tracer(
3428        &self,
3429        hash: B256,
3430        code: String,
3431        opts: GethDebugTracingOptions,
3432    ) -> Result<GethTrace, BlockchainError> {
3433        let GethDebugTracingOptions { tracer_config, .. } = opts;
3434        let config = tracer_config.into_json();
3435        let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
3436            .map_err(|err| BlockchainError::Message(err.to_string()))?;
3437        let trace = self.replay_tx_with_inspector(
3438            hash,
3439            inspector,
3440            |result, cache_db, mut inspector, tx_env, env| {
3441                inspector
3442                    .json_result(
3443                        result,
3444                        &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
3445                        &env.evm_env.block_env,
3446                        &cache_db,
3447                    )
3448                    .map_err(|e| BlockchainError::Message(e.to_string()))
3449            },
3450        )??;
3451        Ok(GethTrace::JS(trace))
3452    }
3453
3454    /// Prove an account's existence or nonexistence in the state trie.
3455    ///
3456    /// Returns a merkle proof of the account's trie node, `account_key` == keccak(address)
3457    pub async fn prove_account_at(
3458        &self,
3459        address: Address,
3460        keys: Vec<B256>,
3461        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3462    ) -> Result<AccountProof, BlockchainError> {
3463        let block_number = block_request.as_ref().map(|r| r.block_number());
3464
3465        self.with_database_at(block_request, |block_db, _| {
3466            trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
3467            let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3468            let account = db.get(&address).cloned().unwrap_or_default();
3469
3470            let mut builder = HashBuilder::default()
3471                .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
3472
3473            for (key, account) in trie_accounts(db) {
3474                builder.add_leaf(key, &account);
3475            }
3476
3477            let _ = builder.root();
3478
3479            let proof = builder
3480                .take_proof_nodes()
3481                .into_nodes_sorted()
3482                .into_iter()
3483                .map(|(_, v)| v)
3484                .collect();
3485            let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
3486
3487            let account_proof = AccountProof {
3488                address,
3489                balance: account.info.balance,
3490                nonce: account.info.nonce,
3491                code_hash: account.info.code_hash,
3492                storage_hash,
3493                account_proof: proof,
3494                storage_proof: keys
3495                    .into_iter()
3496                    .zip(storage_proofs)
3497                    .map(|(key, proof)| {
3498                        let storage_key: U256 = key.into();
3499                        let value = account.storage.get(&storage_key).copied().unwrap_or_default();
3500                        StorageProof { key: JsonStorageKey::Hash(key), value, proof }
3501                    })
3502                    .collect(),
3503            };
3504
3505            Ok(account_proof)
3506        })
3507        .await?
3508    }
3509
3510    /// Rollback the chain to a common height.
3511    ///
3512    /// The state of the chain is rewound using `rewind` to the common block, including the db,
3513    /// storage, and env.
3514    pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> {
3515        let hash = common_block.header.hash_slow();
3516
3517        // Get the database at the common block
3518        let common_state = {
3519            let return_state_or_throw_err =
3520                |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
3521                    let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
3522                    let db_full =
3523                        state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3524                    Ok(db_full.clone())
3525                };
3526
3527            let read_guard = self.states.upgradable_read();
3528            if let Some(db) = read_guard.get_state(&hash) {
3529                return_state_or_throw_err(Some(db))?
3530            } else {
3531                let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3532                return_state_or_throw_err(write_guard.get_on_disk_state(&hash))?
3533            }
3534        };
3535
3536        {
3537            // Unwind the storage back to the common ancestor first
3538            self.blockchain.storage.write().unwind_to(common_block.header.number(), hash);
3539
3540            // Set environment back to common block
3541            let mut env = self.env.write();
3542            env.evm_env.block_env.number = U256::from(common_block.header.number());
3543            env.evm_env.block_env.timestamp = U256::from(common_block.header.timestamp());
3544            env.evm_env.block_env.gas_limit = common_block.header.gas_limit();
3545            env.evm_env.block_env.difficulty = common_block.header.difficulty();
3546            env.evm_env.block_env.prevrandao = common_block.header.mix_hash();
3547
3548            self.time.reset(env.evm_env.block_env.timestamp.saturating_to());
3549        }
3550
3551        {
3552            // Collect block hashes before acquiring db lock to avoid holding blockchain storage
3553            // lock across await. Only collect the last 256 blocks since that's all BLOCKHASH can
3554            // access.
3555            let block_hashes: Vec<_> = {
3556                let storage = self.blockchain.storage.read();
3557                let min_block = common_block.header.number().saturating_sub(256);
3558                storage
3559                    .hashes
3560                    .iter()
3561                    .filter(|(num, _)| **num >= min_block)
3562                    .map(|(&num, &hash)| (num, hash))
3563                    .collect()
3564            };
3565
3566            // Acquire db lock once for the entire restore operation to reduce lock churn.
3567            let mut db = self.db.write().await;
3568            db.clear();
3569
3570            // Insert account info before storage to prevent fork-mode RPC fetches after clear.
3571            for (address, acc) in common_state {
3572                db.insert_account(address, acc.info);
3573                for (key, value) in acc.storage {
3574                    db.set_storage_at(address, key.into(), value.into())?;
3575                }
3576            }
3577
3578            // Restore block hashes from blockchain storage (now unwound, contains only valid
3579            // blocks).
3580            for (block_num, hash) in block_hashes {
3581                db.insert_block_hash(U256::from(block_num), hash);
3582            }
3583        }
3584
3585        Ok(())
3586    }
3587}
3588
3589impl Backend<FoundryNetwork> {
3590    /// Get the current state.
3591    pub async fn serialized_state(
3592        &self,
3593        preserve_historical_states: bool,
3594    ) -> Result<SerializableState, BlockchainError> {
3595        let at = self.env.read().evm_env.block_env.clone();
3596        let best_number = self.blockchain.storage.read().best_number;
3597        let blocks = self.blockchain.storage.read().serialized_blocks();
3598        let transactions = self.blockchain.storage.read().serialized_transactions();
3599        let historical_states = if preserve_historical_states {
3600            Some(self.states.write().serialized_states())
3601        } else {
3602            None
3603        };
3604
3605        let state = self.db.read().await.dump_state(
3606            at,
3607            best_number,
3608            blocks,
3609            transactions,
3610            historical_states,
3611        )?;
3612        state.ok_or_else(|| {
3613            RpcError::invalid_params("Dumping state not supported with the current configuration")
3614                .into()
3615        })
3616    }
3617
3618    /// Write all chain data to serialized bytes buffer
3619    pub async fn dump_state(
3620        &self,
3621        preserve_historical_states: bool,
3622    ) -> Result<Bytes, BlockchainError> {
3623        let state = self.serialized_state(preserve_historical_states).await?;
3624        let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
3625        encoder
3626            .write_all(&serde_json::to_vec(&state).unwrap_or_default())
3627            .map_err(|_| BlockchainError::DataUnavailable)?;
3628        Ok(encoder.finish().unwrap_or_default().into())
3629    }
3630
3631    /// Apply [SerializableState] data to the backend storage.
3632    pub async fn load_state(&self, state: SerializableState) -> Result<bool, BlockchainError> {
3633        // load the blocks and transactions into the storage
3634        self.blockchain.storage.write().load_blocks(state.blocks.clone());
3635        self.blockchain.storage.write().load_transactions(state.transactions.clone());
3636        // reset the block env
3637        if let Some(block) = state.block.clone() {
3638            self.env.write().evm_env.block_env = block.clone();
3639
3640            // Set the current best block number.
3641            // Defaults to block number for compatibility with existing state files.
3642            let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));
3643
3644            let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
3645            if let Some((number, hash)) = fork_num_and_hash {
3646                trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
3647                // If the state.block_number is greater than the fork block number, set best number
3648                // to the state block number.
3649                // Ref: https://github.com/foundry-rs/foundry/issues/9539
3650                if best_number > number {
3651                    self.blockchain.storage.write().best_number = best_number;
3652                    let best_hash =
3653                        self.blockchain.storage.read().hash(best_number.into()).ok_or_else(
3654                            || {
3655                                BlockchainError::RpcError(RpcError::internal_error_with(format!(
3656                                    "Best hash not found for best number {best_number}",
3657                                )))
3658                            },
3659                        )?;
3660                    self.blockchain.storage.write().best_hash = best_hash;
3661                } else {
3662                    // If loading state file on a fork, set best number to the fork block number.
3663                    // Ref: https://github.com/foundry-rs/foundry/pull/9215#issue-2618681838
3664                    self.blockchain.storage.write().best_number = number;
3665                    self.blockchain.storage.write().best_hash = hash;
3666                }
3667            } else {
3668                self.blockchain.storage.write().best_number = best_number;
3669
3670                // Set the current best block hash;
3671                let best_hash =
3672                    self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| {
3673                        BlockchainError::RpcError(RpcError::internal_error_with(format!(
3674                            "Best hash not found for best number {best_number}",
3675                        )))
3676                    })?;
3677
3678                self.blockchain.storage.write().best_hash = best_hash;
3679            }
3680        }
3681
3682        if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number()) {
3683            let header = &latest.header;
3684            let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
3685                header.gas_used(),
3686                header.gas_limit(),
3687                header.base_fee_per_gas().unwrap_or_default(),
3688            );
3689            let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
3690                header.excess_blob_gas().unwrap_or_default(),
3691                header.blob_gas_used().unwrap_or_default(),
3692            );
3693
3694            // update next base fee
3695            self.fees.set_base_fee(next_block_base_fee);
3696
3697            self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
3698                next_block_excess_blob_gas,
3699                get_blob_base_fee_update_fraction(
3700                    self.env.read().evm_env.cfg_env.chain_id,
3701                    header.timestamp,
3702                ),
3703            ));
3704        }
3705
3706        if !self.db.write().await.load_state(state.clone())? {
3707            return Err(RpcError::invalid_params(
3708                "Loading state not supported with the current configuration",
3709            )
3710            .into());
3711        }
3712
3713        if let Some(historical_states) = state.historical_states {
3714            self.states.write().load_states(historical_states);
3715        }
3716
3717        Ok(true)
3718    }
3719
3720    /// Deserialize and add all chain data to the backend storage
3721    pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
3722        let orig_buf = &buf.0[..];
3723        let mut decoder = GzDecoder::new(orig_buf);
3724        let mut decoded_data = Vec::new();
3725
3726        let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
3727            decoder
3728                .read_to_end(decoded_data.as_mut())
3729                .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
3730            &decoded_data
3731        } else {
3732            &buf.0
3733        })
3734        .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
3735
3736        self.load_state(state).await
3737    }
3738
3739    /// Simulates the payload by executing the calls in request.
3740    pub async fn simulate(
3741        &self,
3742        request: SimulatePayload,
3743        block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3744    ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
3745        self.with_database_at(block_request, |state, mut block_env| {
3746            let SimulatePayload {
3747                block_state_calls,
3748                trace_transfers,
3749                validation,
3750                return_full_transactions,
3751            } = request;
3752            let mut cache_db = CacheDB::new(state);
3753            let mut block_res = Vec::with_capacity(block_state_calls.len());
3754
3755            // execute the blocks
3756            for block in block_state_calls {
3757                let SimBlock { block_overrides, state_overrides, calls } = block;
3758                let mut call_res = Vec::with_capacity(calls.len());
3759                let mut log_index = 0;
3760                let mut gas_used = 0;
3761                let mut transactions = Vec::with_capacity(calls.len());
3762                let mut logs= Vec::new();
3763
3764                // apply state overrides before executing the transactions
3765                if let Some(state_overrides) = state_overrides {
3766                    apply_state_overrides(state_overrides, &mut cache_db)?;
3767                }
3768                if let Some(block_overrides) = block_overrides {
3769                    cache_db.apply_block_overrides(block_overrides, &mut block_env);
3770                }
3771
3772                // execute all calls in that block
3773                for (req_idx, request) in calls.into_iter().enumerate() {
3774                    let fee_details = FeeDetails::new(
3775                        request.gas_price,
3776                        request.max_fee_per_gas,
3777                        request.max_priority_fee_per_gas,
3778                        request.max_fee_per_blob_gas,
3779                    )?
3780                    .or_zero_fees();
3781
3782                    let mut env = self.build_call_env(
3783                        WithOtherFields::new(request.clone()),
3784                        fee_details,
3785                        block_env.clone(),
3786                    );
3787
3788                    // Always disable EIP-3607
3789                    env.evm_env.cfg_env.disable_eip3607 = true;
3790
3791                    if !validation {
3792                        env.evm_env.cfg_env.disable_base_fee = !validation;
3793                        env.evm_env.block_env.basefee = 0;
3794                    }
3795
3796                    let mut inspector = self.build_inspector();
3797
3798                    // transact
3799                    let ResultAndState { result, state } = if trace_transfers {
3800                        // prepare inspector to capture transfer inside the evm so they are
3801                        // recorded and included in logs
3802                        inspector = inspector.with_transfers();
3803                        let mut evm= self.new_eth_evm_with_inspector_ref(
3804                            &cache_db,
3805                            &env,
3806                            &mut inspector,
3807                        );
3808
3809                        trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env");
3810                        evm.transact(env.tx)?
3811                    } else {
3812                        let mut evm = self.new_eth_evm_with_inspector_ref(
3813                            &cache_db,
3814                            &env,
3815                            &mut inspector,
3816                        );
3817                        trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env");
3818                        evm.transact(env.tx)?
3819                    };
3820                    trace!(target: "backend", ?result, ?request, "simulate call");
3821
3822                    inspector.print_logs();
3823                    if self.print_traces {
3824                        inspector.into_print_traces(self.call_trace_decoder.clone());
3825                    }
3826
3827                    // commit the transaction
3828                    cache_db.commit(state);
3829                    gas_used += result.gas_used();
3830
3831                    // create the transaction from a request
3832                    let from = request.from.unwrap_or_default();
3833
3834                    let mut request = Into::<FoundryTransactionRequest>::into(WithOtherFields::new(request));
3835                    request.prep_for_submission();
3836
3837                    let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?;
3838
3839                    let tx = build_impersonated(typed_tx);
3840                    let tx_hash = tx.hash();
3841                    let rpc_tx = transaction_build(
3842                        None,
3843                        MaybeImpersonatedTransaction::impersonated(tx, from),
3844                        None,
3845                        None,
3846                        Some(block_env.basefee),
3847                    );
3848                    transactions.push(rpc_tx);
3849
3850                    let return_data = result.output().cloned().unwrap_or_default();
3851                    let sim_res = SimCallResult {
3852                        return_data,
3853                        gas_used: result.gas_used(),
3854                        status: result.is_success(),
3855                        error: result.is_success().not().then(|| {
3856                            alloy_rpc_types::simulate::SimulateError {
3857                                code: -3200,
3858                                message: "execution failed".to_string(),
3859                                data: None,
3860                            }
3861                        }),
3862                        logs: result.clone()
3863                            .into_logs()
3864                            .into_iter()
3865                            .enumerate()
3866                            .map(|(idx, log)| Log {
3867                                inner: log,
3868                                block_number: Some(block_env.number.saturating_to()),
3869                                block_timestamp: Some(block_env.timestamp.saturating_to()),
3870                                transaction_index: Some(req_idx as u64),
3871                                log_index: Some((idx + log_index) as u64),
3872                                removed: false,
3873
3874                                block_hash: None,
3875                                transaction_hash: Some(tx_hash),
3876                            })
3877                            .collect(),
3878                    };
3879                    logs.extend(sim_res.logs.iter().map(|log| log.inner.clone()));
3880                    log_index += sim_res.logs.len();
3881                    call_res.push(sim_res);
3882                }
3883
3884                let transactions_envelopes: Vec<AnyTxEnvelope> = transactions
3885                .iter()
3886                .map(|tx| AnyTxEnvelope::from(tx.clone()))
3887                .collect();
3888                let header = Header {
3889                    logs_bloom: logs_bloom(logs.iter()),
3890                    transactions_root: calculate_transaction_root(&transactions_envelopes),
3891                    receipts_root: calculate_receipt_root(&transactions_envelopes),
3892                    parent_hash: Default::default(),
3893                    beneficiary: block_env.beneficiary,
3894                    state_root: Default::default(),
3895                    difficulty: Default::default(),
3896                    number: block_env.number.saturating_to(),
3897                    gas_limit: block_env.gas_limit,
3898                    gas_used,
3899                    timestamp: block_env.timestamp.saturating_to(),
3900                    extra_data: Default::default(),
3901                    mix_hash: Default::default(),
3902                    nonce: Default::default(),
3903                    base_fee_per_gas: Some(block_env.basefee),
3904                    withdrawals_root: None,
3905                    blob_gas_used: None,
3906                    excess_blob_gas: None,
3907                    parent_beacon_block_root: None,
3908                    requests_hash: None,
3909                    ..Default::default()
3910                };
3911                let mut block = alloy_rpc_types::Block {
3912                    header: AnyRpcHeader {
3913                        hash: header.hash_slow(),
3914                        inner: header.into(),
3915                        total_difficulty: None,
3916                        size: None,
3917                    },
3918                    uncles: vec![],
3919                    transactions: BlockTransactions::Full(transactions),
3920                    withdrawals: None,
3921                };
3922
3923                if !return_full_transactions {
3924                    block.transactions.convert_to_hashes();
3925                }
3926
3927                for res in &mut call_res {
3928                    res.logs.iter_mut().for_each(|log| {
3929                        log.block_hash = Some(block.header.hash);
3930                    });
3931                }
3932
3933                let simulated_block = SimulatedBlock {
3934                    inner: AnyRpcBlock::new(WithOtherFields::new(block)),
3935                    calls: call_res,
3936                };
3937
3938                // update block env
3939                block_env.number += U256::from(1);
3940                block_env.timestamp += U256::from(12);
3941                block_env.basefee = simulated_block
3942                    .inner
3943                    .header
3944                    .next_block_base_fee(self.fees.base_fee_params())
3945                    .unwrap_or_default();
3946
3947                block_res.push(simulated_block);
3948            }
3949
3950            Ok(block_res)
3951        })
3952        .await?
3953    }
3954
3955    /// returns all receipts for the given transactions
3956    fn get_receipts(
3957        &self,
3958        tx_hashes: impl IntoIterator<Item = TxHash>,
3959    ) -> Vec<FoundryReceiptEnvelope> {
3960        let storage = self.blockchain.storage.read();
3961        let mut receipts = vec![];
3962
3963        for hash in tx_hashes {
3964            if let Some(tx) = storage.transactions.get(&hash) {
3965                receipts.push(tx.receipt.clone());
3966            }
3967        }
3968
3969        receipts
3970    }
3971
3972    /// Returns the traces for the given transaction
3973    pub async fn debug_trace_transaction(
3974        &self,
3975        hash: B256,
3976        opts: GethDebugTracingOptions,
3977    ) -> Result<GethTrace, BlockchainError> {
3978        #[cfg(feature = "js-tracer")]
3979        if let Some(tracer_type) = opts.tracer.as_ref()
3980            && tracer_type.is_js()
3981        {
3982            return self
3983                .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
3984                .await;
3985        }
3986
3987        if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
3988            return trace;
3989        }
3990
3991        if let Some(fork) = self.get_fork() {
3992            return Ok(fork.debug_trace_transaction(hash, opts).await?);
3993        }
3994
3995        Ok(GethTrace::Default(Default::default()))
3996    }
3997
3998    fn geth_trace(
3999        &self,
4000        tx: &MinedTransaction<FoundryNetwork>,
4001        opts: GethDebugTracingOptions,
4002    ) -> Result<GethTrace, BlockchainError> {
4003        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
4004
4005        if let Some(tracer) = tracer {
4006            match tracer {
4007                GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
4008                    GethDebugBuiltInTracerType::FourByteTracer => {
4009                        let inspector = FourByteInspector::default();
4010                        let res = self.replay_tx_with_inspector(
4011                            tx.info.transaction_hash,
4012                            inspector,
4013                            |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
4014                        )?;
4015                        return Ok(res);
4016                    }
4017                    GethDebugBuiltInTracerType::CallTracer => {
4018                        return match tracer_config.into_call_config() {
4019                            Ok(call_config) => {
4020                                let inspector = TracingInspector::new(
4021                                    TracingInspectorConfig::from_geth_call_config(&call_config),
4022                                );
4023                                let frame = self.replay_tx_with_inspector(
4024                                    tx.info.transaction_hash,
4025                                    inspector,
4026                                    |_, _, inspector, _, _| {
4027                                        inspector
4028                                            .geth_builder()
4029                                            .geth_call_traces(
4030                                                call_config,
4031                                                tx.receipt.cumulative_gas_used(),
4032                                            )
4033                                            .into()
4034                                    },
4035                                )?;
4036                                Ok(frame)
4037                            }
4038                            Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
4039                        };
4040                    }
4041                    GethDebugBuiltInTracerType::PreStateTracer => {
4042                        return match tracer_config.into_pre_state_config() {
4043                            Ok(pre_state_config) => {
4044                                let inspector = TracingInspector::new(
4045                                    TracingInspectorConfig::from_geth_prestate_config(
4046                                        &pre_state_config,
4047                                    ),
4048                                );
4049                                let frame = self.replay_tx_with_inspector(
4050                                    tx.info.transaction_hash,
4051                                    inspector,
4052                                    |state, db, inspector, _, _| {
4053                                        inspector.geth_builder().geth_prestate_traces(
4054                                            &state,
4055                                            &pre_state_config,
4056                                            db,
4057                                        )
4058                                    },
4059                                )??;
4060                                Ok(frame.into())
4061                            }
4062                            Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
4063                        };
4064                    }
4065                    GethDebugBuiltInTracerType::NoopTracer
4066                    | GethDebugBuiltInTracerType::MuxTracer
4067                    | GethDebugBuiltInTracerType::Erc7562Tracer
4068                    | GethDebugBuiltInTracerType::FlatCallTracer => {}
4069                },
4070                GethDebugTracerType::JsTracer(_code) => {}
4071            }
4072
4073            return Ok(NoopFrame::default().into());
4074        }
4075
4076        // default structlog tracer
4077        Ok(GethTraceBuilder::new(tx.info.traces.clone())
4078            .geth_traces(
4079                tx.receipt.cumulative_gas_used(),
4080                tx.info.out.clone().unwrap_or_default(),
4081                config,
4082            )
4083            .into())
4084    }
4085
4086    async fn mined_geth_trace_transaction(
4087        &self,
4088        hash: B256,
4089        opts: GethDebugTracingOptions,
4090    ) -> Option<Result<GethTrace, BlockchainError>> {
4091        self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
4092    }
4093
4094    pub async fn transaction_receipt(
4095        &self,
4096        hash: B256,
4097    ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
4098        if let Some(receipt) = self.mined_transaction_receipt(hash) {
4099            return Ok(Some(receipt.inner));
4100        }
4101
4102        if let Some(fork) = self.get_fork() {
4103            let receipt = fork.transaction_receipt(hash).await?;
4104            let number = self.convert_block_number(
4105                receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
4106            );
4107
4108            if fork.predates_fork_inclusive(number) {
4109                return Ok(receipt);
4110            }
4111        }
4112
4113        Ok(None)
4114    }
4115
4116    /// Returns all transaction receipts of the block
4117    pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
4118        let mut receipts = Vec::new();
4119        let block = self.get_block(id)?;
4120
4121        for transaction in block.body.transactions {
4122            let receipt = self.mined_transaction_receipt(transaction.hash())?;
4123            receipts.push(receipt.inner);
4124        }
4125
4126        Some(receipts)
4127    }
4128
4129    /// Returns the transaction receipt for the given hash
4130    pub(crate) fn mined_transaction_receipt(
4131        &self,
4132        hash: B256,
4133    ) -> Option<MinedTransactionReceipt<FoundryNetwork>> {
4134        let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } =
4135            self.blockchain.get_transaction_by_hash(&hash)?;
4136
4137        let index = info.transaction_index as usize;
4138        let block = self.blockchain.get_block_by_hash(&block_hash)?;
4139        let transaction = block.body.transactions[index].clone();
4140
4141        // Cancun specific
4142        let excess_blob_gas = block.header.excess_blob_gas();
4143        let blob_gas_price =
4144            alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
4145        let blob_gas_used = transaction.blob_gas_used();
4146
4147        let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas());
4148
4149        let receipts = self.get_receipts(block.body.transactions.iter().map(|tx| tx.hash()));
4150        let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::<usize>();
4151
4152        let tx_receipt = tx_receipt.convert_logs_rpc(
4153            BlockNumHash::new(block.header.number(), block_hash),
4154            block.header.timestamp(),
4155            info.transaction_hash,
4156            info.transaction_index,
4157            next_log_index,
4158        );
4159
4160        let receipt = TransactionReceipt {
4161            inner: tx_receipt,
4162            transaction_hash: info.transaction_hash,
4163            transaction_index: Some(info.transaction_index),
4164            block_number: Some(block.header.number()),
4165            gas_used: info.gas_used,
4166            contract_address: info.contract_address,
4167            effective_gas_price,
4168            block_hash: Some(block_hash),
4169            from: info.from,
4170            to: info.to,
4171            blob_gas_price: Some(blob_gas_price),
4172            blob_gas_used,
4173        };
4174
4175        // Include timestamp in receipt to avoid extra block lookups (e.g., in Otterscan API)
4176        let inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp());
4177        Some(MinedTransactionReceipt { inner, out: info.out })
4178    }
4179
4180    /// Returns the blocks receipts for the given number
4181    pub async fn block_receipts(
4182        &self,
4183        number: BlockId,
4184    ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
4185        if let Some(receipts) = self.mined_block_receipts(number) {
4186            return Ok(Some(receipts));
4187        }
4188
4189        if let Some(fork) = self.get_fork() {
4190            let number = match self.ensure_block_number(Some(number)).await {
4191                Err(_) => return Ok(None),
4192                Ok(n) => n,
4193            };
4194
4195            if fork.predates_fork_inclusive(number) {
4196                let receipts = fork.block_receipts(number).await?;
4197
4198                return Ok(receipts);
4199            }
4200        }
4201
4202        Ok(None)
4203    }
4204
4205    pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
4206        // Try to get the mined transaction by hash
4207        if let Some(tx) = self.mined_transaction_by_hash(hash)
4208            && let Ok(typed_tx) = FoundryTxEnvelope::try_from(tx)
4209            && let Some(sidecar) = typed_tx.sidecar()
4210        {
4211            return Ok(Some(sidecar.sidecar.blobs().to_vec()));
4212        }
4213
4214        Ok(None)
4215    }
4216}
4217
4218/// Get max nonce from transaction pool by address.
4219fn get_pool_transactions_nonce(
4220    pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
4221    address: Address,
4222) -> Option<u64> {
4223    if let Some(highest_nonce) = pool_transactions
4224        .iter()
4225        .filter(|tx| *tx.pending_transaction.sender() == address)
4226        .map(|tx| tx.pending_transaction.nonce())
4227        .max()
4228    {
4229        let tx_count = highest_nonce.saturating_add(1);
4230        return Some(tx_count);
4231    }
4232    None
4233}
4234
4235#[async_trait::async_trait]
4236impl TransactionValidator<FoundryTxEnvelope> for Backend<FoundryNetwork> {
4237    async fn validate_pool_transaction(
4238        &self,
4239        tx: &PendingTransaction<FoundryTxEnvelope>,
4240    ) -> Result<(), BlockchainError> {
4241        let address = *tx.sender();
4242        let account = self.get_account(address).await?;
4243        let env = self.next_env();
4244        Ok(self.validate_pool_transaction_for(tx, &account, &env)?)
4245    }
4246
4247    fn validate_pool_transaction_for(
4248        &self,
4249        pending: &PendingTransaction<FoundryTxEnvelope>,
4250        account: &AccountInfo,
4251        env: &Env,
4252    ) -> Result<(), InvalidTransactionError> {
4253        let tx = &pending.transaction;
4254
4255        if let Some(tx_chain_id) = tx.chain_id() {
4256            let chain_id = self.chain_id();
4257            if chain_id.to::<u64>() != tx_chain_id {
4258                if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
4259                    // <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md>
4260                    if env.evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON
4261                        && tx.chain_id().is_none()
4262                    {
4263                        warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
4264                        return Err(InvalidTransactionError::IncompatibleEIP155);
4265                    }
4266                } else {
4267                    warn!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
4268                    return Err(InvalidTransactionError::InvalidChainId);
4269                }
4270            }
4271        }
4272
4273        // Nonce validation
4274        let is_deposit_tx = matches!(pending.transaction.as_ref(), FoundryTxEnvelope::Deposit(_));
4275        let nonce = tx.nonce();
4276        if nonce < account.nonce && !is_deposit_tx {
4277            warn!(target: "backend", "[{:?}] nonce too low", tx.hash());
4278            return Err(InvalidTransactionError::NonceTooLow);
4279        }
4280
4281        // EIP-4844 structural validation
4282        if env.evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
4283            // Heavy (blob validation) checks
4284            let blob_tx = match tx.as_ref() {
4285                FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
4286                _ => unreachable!(),
4287            };
4288
4289            let blob_count = blob_tx.tx().blob_versioned_hashes.len();
4290
4291            // Ensure there are blob hashes.
4292            if blob_count == 0 {
4293                return Err(InvalidTransactionError::NoBlobHashes);
4294            }
4295
4296            // Ensure the tx does not exceed the max blobs per transaction.
4297            let max_blobs_per_tx = self.blob_params().max_blobs_per_tx as usize;
4298            if blob_count > max_blobs_per_tx {
4299                return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blobs_per_tx));
4300            }
4301
4302            // Check for any blob validation errors if not impersonating.
4303            if !self.skip_blob_validation(Some(*pending.sender()))
4304                && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
4305            {
4306                return Err(InvalidTransactionError::BlobTransactionValidationError(err));
4307            }
4308        }
4309
4310        // EIP-3860 initcode size validation, respects --code-size-limit / --disable-code-size-limit
4311        if env.evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create {
4312            let max_initcode_size = env
4313                .evm_env
4314                .cfg_env
4315                .limit_contract_code_size
4316                .map(|limit| limit.saturating_mul(2))
4317                .unwrap_or(revm::primitives::eip3860::MAX_INITCODE_SIZE);
4318            if tx.input().len() > max_initcode_size {
4319                return Err(InvalidTransactionError::MaxInitCodeSizeExceeded);
4320            }
4321        }
4322
4323        // Balance and fee related checks
4324        if !self.disable_pool_balance_checks {
4325            // Gas limit validation
4326            if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
4327                warn!(target: "backend", "[{:?}] gas too low", tx.hash());
4328                return Err(InvalidTransactionError::GasTooLow);
4329            }
4330
4331            // Check tx gas limit against block gas limit, if block gas limit is set.
4332            if !env.evm_env.cfg_env.disable_block_gas_limit
4333                && tx.gas_limit() > env.evm_env.block_env.gas_limit
4334            {
4335                warn!(target: "backend", "[{:?}] gas too high", tx.hash());
4336                return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
4337                    detail: String::from("tx.gas_limit > env.block.gas_limit"),
4338                }));
4339            }
4340
4341            // Check tx gas limit against tx gas limit cap (Osaka hard fork and later).
4342            if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
4343                && tx.gas_limit() > env.evm_env.cfg_env().tx_gas_limit_cap()
4344            {
4345                warn!(target: "backend", "[{:?}] gas too high", tx.hash());
4346                return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
4347                    detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
4348                }));
4349            }
4350
4351            // EIP-1559 fee validation (London hard fork and later).
4352            if env.evm_env.cfg_env.spec >= SpecId::LONDON {
4353                if tx.max_fee_per_gas() < env.evm_env.block_env.basefee.into() && !is_deposit_tx {
4354                    warn!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), env.evm_env.block_env.basefee);
4355                    return Err(InvalidTransactionError::FeeCapTooLow);
4356                }
4357
4358                if let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
4359                    (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
4360                    && max_priority_fee_per_gas > max_fee_per_gas
4361                {
4362                    warn!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
4363                    return Err(InvalidTransactionError::TipAboveFeeCap);
4364                }
4365            }
4366
4367            // EIP-4844 blob fee validation
4368            if env.evm_env.cfg_env.spec >= SpecId::CANCUN
4369                && tx.is_eip4844()
4370                && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
4371                && let Some(blob_gas_and_price) = &env.evm_env.block_env.blob_excess_gas_and_price
4372                && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
4373            {
4374                warn!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice);
4375                return Err(InvalidTransactionError::BlobFeeCapTooLow(
4376                    max_fee_per_blob_gas,
4377                    blob_gas_and_price.blob_gasprice,
4378                ));
4379            }
4380
4381            let max_cost =
4382                (tx.gas_limit() as u128).saturating_mul(tx.max_fee_per_gas()).saturating_add(
4383                    tx.blob_gas_used()
4384                        .map(|g| g as u128)
4385                        .unwrap_or(0)
4386                        .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
4387                );
4388            let value = tx.value();
4389            match tx.as_ref() {
4390                FoundryTxEnvelope::Deposit(deposit_tx) => {
4391                    // Deposit transactions
4392                    // https://specs.optimism.io/protocol/deposits.html#execution
4393                    // 1. no gas cost check required since already have prepaid gas from L1
4394                    // 2. increment account balance by deposited amount before checking for
4395                    //    sufficient funds `tx.value <= existing account value + deposited value`
4396                    if value > account.balance + U256::from(deposit_tx.mint) {
4397                        warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
4398                        return Err(InvalidTransactionError::InsufficientFunds);
4399                    }
4400                }
4401                _ => {
4402                    // check sufficient funds: `gas * price + value`
4403                    let req_funds =
4404                        max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
4405                            warn!(target: "backend", "[{:?}] cost too high", tx.hash());
4406                            InvalidTransactionError::InsufficientFunds
4407                        })?;
4408                    if account.balance < U256::from(req_funds) {
4409                        warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
4410                        return Err(InvalidTransactionError::InsufficientFunds);
4411                    }
4412                }
4413            }
4414        }
4415        Ok(())
4416    }
4417
4418    fn validate_for(
4419        &self,
4420        tx: &PendingTransaction<FoundryTxEnvelope>,
4421        account: &AccountInfo,
4422        env: &Env,
4423    ) -> Result<(), InvalidTransactionError> {
4424        self.validate_pool_transaction_for(tx, account, env)?;
4425        if tx.nonce() > account.nonce {
4426            return Err(InvalidTransactionError::NonceTooHigh);
4427        }
4428        Ok(())
4429    }
4430}
4431
4432/// Creates a `AnyRpcTransaction` as it's expected for the `eth` RPC api from storage data
4433pub fn transaction_build(
4434    tx_hash: Option<B256>,
4435    eth_transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
4436    block: Option<&Block>,
4437    info: Option<TransactionInfo>,
4438    base_fee: Option<u64>,
4439) -> AnyRpcTransaction {
4440    if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
4441        let dep_tx = deposit_tx;
4442
4443        let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
4444        let maybe_deposit_fields = OtherFields::try_from(ser);
4445
4446        match maybe_deposit_fields {
4447            Ok(mut fields) => {
4448                // Add zeroed signature fields for backwards compatibility
4449                // https://specs.optimism.io/protocol/deposits.html#the-deposited-transaction-type
4450                fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
4451                fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
4452                fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
4453                fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
4454
4455                let inner = UnknownTypedTransaction {
4456                    ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
4457                    fields,
4458                    memo: Default::default(),
4459                };
4460
4461                let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
4462                    hash: eth_transaction.hash(),
4463                    inner,
4464                });
4465
4466                let tx = Transaction {
4467                    inner: Recovered::new_unchecked(envelope, deposit_tx.from),
4468                    block_hash: block
4469                        .as_ref()
4470                        .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),
4471                    block_number: block.as_ref().map(|block| block.header.number()),
4472                    transaction_index: info.as_ref().map(|info| info.transaction_index),
4473                    effective_gas_price: None,
4474                    block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4475                };
4476
4477                return AnyRpcTransaction::from(WithOtherFields::new(tx));
4478            }
4479            Err(_) => {
4480                error!(target: "backend", "failed to serialize deposit transaction");
4481            }
4482        }
4483    }
4484
4485    let from = eth_transaction.recover().unwrap_or_default();
4486    let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
4487
4488    // if a specific hash was provided we update the transaction's hash
4489    // This is important for impersonated transactions since they all use the
4490    // `BYPASS_SIGNATURE` which would result in different hashes
4491    // Note: for impersonated transactions this only concerns pending transactions because
4492    // there's no `info` yet.
4493    let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
4494
4495    // TODO: this panics for non-standard tx types (e.g. Tempo) that aren't handled above
4496    // (pre-existing issue from the original `into_rpc_transaction`).
4497    let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
4498        .try_into_eth()
4499        .expect("deposit transactions are handled above");
4500
4501    let envelope = match eth_envelope {
4502        TxEnvelope::Legacy(signed_tx) => {
4503            let (t, sig, _) = signed_tx.into_parts();
4504            let new_signed = Signed::new_unchecked(t, sig, hash);
4505            AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(new_signed))
4506        }
4507        TxEnvelope::Eip1559(signed_tx) => {
4508            let (t, sig, _) = signed_tx.into_parts();
4509            let new_signed = Signed::new_unchecked(t, sig, hash);
4510            AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(new_signed))
4511        }
4512        TxEnvelope::Eip2930(signed_tx) => {
4513            let (t, sig, _) = signed_tx.into_parts();
4514            let new_signed = Signed::new_unchecked(t, sig, hash);
4515            AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(new_signed))
4516        }
4517        TxEnvelope::Eip4844(signed_tx) => {
4518            let (t, sig, _) = signed_tx.into_parts();
4519            let new_signed = Signed::new_unchecked(t, sig, hash);
4520            AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(new_signed))
4521        }
4522        TxEnvelope::Eip7702(signed_tx) => {
4523            let (t, sig, _) = signed_tx.into_parts();
4524            let new_signed = Signed::new_unchecked(t, sig, hash);
4525            AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(new_signed))
4526        }
4527    };
4528
4529    let tx = Transaction {
4530        inner: Recovered::new_unchecked(envelope, from),
4531        block_hash: block.as_ref().map(|block| block.header.hash_slow()),
4532        block_number: block.as_ref().map(|block| block.header.number()),
4533        transaction_index: info.as_ref().map(|info| info.transaction_index),
4534        // deprecated
4535        effective_gas_price: Some(effective_gas_price),
4536        block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4537    };
4538    AnyRpcTransaction::from(WithOtherFields::new(tx))
4539}
4540
4541/// Prove a storage key's existence or nonexistence in the account's storage trie.
4542///
4543/// `storage_key` is the hash of the desired storage key, meaning
4544/// this will only work correctly under a secure trie.
4545/// `storage_key` == keccak(key)
4546pub fn prove_storage(storage: &HashMap<U256, U256>, keys: &[B256]) -> (B256, Vec<Vec<Bytes>>) {
4547    let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
4548
4549    let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
4550
4551    for (key, value) in trie_storage(storage) {
4552        builder.add_leaf(key, &value);
4553    }
4554
4555    let root = builder.root();
4556
4557    let mut proofs = Vec::new();
4558    let all_proof_nodes = builder.take_proof_nodes();
4559
4560    for proof_key in keys {
4561        // Iterate over all proof nodes and find the matching ones.
4562        // The filtered results are guaranteed to be in order.
4563        let matching_proof_nodes =
4564            all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
4565        proofs.push(matching_proof_nodes.collect());
4566    }
4567
4568    (root, proofs)
4569}
4570
4571pub fn is_arbitrum(chain_id: u64) -> bool {
4572    if let Ok(chain) = NamedChain::try_from(chain_id) {
4573        return chain.is_arbitrum();
4574    }
4575    false
4576}
4577
4578pub fn op_haltreason_to_instruction_result(op_reason: OpHaltReason) -> InstructionResult {
4579    match op_reason {
4580        OpHaltReason::Base(eth_h) => eth_h.into(),
4581        OpHaltReason::FailedDeposit => InstructionResult::Stop,
4582    }
4583}
4584
4585#[cfg(test)]
4586mod tests {
4587    use crate::{NodeConfig, spawn};
4588
4589    #[tokio::test]
4590    async fn test_deterministic_block_mining() {
4591        // Test that mine_block produces deterministic block hashes with same initial conditions
4592        let genesis_timestamp = 1743944919u64;
4593
4594        // Create two identical backends
4595        let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
4596        let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
4597
4598        let (api_a, _handle_a) = spawn(config_a).await;
4599        let (api_b, _handle_b) = spawn(config_b).await;
4600
4601        // Mine empty blocks (no transactions) on both backends
4602        let outcome_a_1 = api_a.backend.mine_block(vec![]).await;
4603        let outcome_b_1 = api_b.backend.mine_block(vec![]).await;
4604
4605        // Both should mine the same block number
4606        assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
4607
4608        // Get the actual blocks to compare hashes
4609        let block_a_1 =
4610            api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
4611        let block_b_1 =
4612            api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
4613
4614        // The block hashes should be identical
4615        assert_eq!(
4616            block_a_1.header.hash, block_b_1.header.hash,
4617            "Block hashes should be deterministic. Got {} vs {}",
4618            block_a_1.header.hash, block_b_1.header.hash
4619        );
4620
4621        // Mine another block to ensure it remains deterministic
4622        let outcome_a_2 = api_a.backend.mine_block(vec![]).await;
4623        let outcome_b_2 = api_b.backend.mine_block(vec![]).await;
4624
4625        let block_a_2 =
4626            api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
4627        let block_b_2 =
4628            api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
4629
4630        assert_eq!(
4631            block_a_2.header.hash, block_b_2.header.hash,
4632            "Second block hashes should also be deterministic. Got {} vs {}",
4633            block_a_2.header.hash, block_b_2.header.hash
4634        );
4635
4636        // Ensure the blocks are different (sanity check)
4637        assert_ne!(
4638            block_a_1.header.hash, block_a_2.header.hash,
4639            "Different blocks should have different hashes"
4640        );
4641    }
4642}