Skip to main content

anvil/eth/backend/mem/
mod.rs

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