Skip to main content

reth_rpc_eth_api/helpers/
call.rs

1//! Loads a pending block from database. Helper trait for `eth_` transaction, call and trace RPC
2//! methods.
3
4use core::fmt;
5
6use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};
7use crate::{
8    helpers::estimate::EstimateCall, FromEvmError, FullEthApiTypes, RpcBlock, RpcNodeCore,
9};
10use alloy_consensus::{transaction::TxHashRef, BlockHeader};
11use alloy_eips::eip2930::AccessListResult;
12use alloy_evm::overrides::{apply_block_overrides, apply_state_overrides, OverrideBlockHashes};
13use alloy_network::TransactionBuilder;
14use alloy_primitives::{Bytes, B256, U256};
15use alloy_rpc_types_eth::{
16    simulate::{SimBlock, SimulatePayload, SimulatedBlock},
17    state::{EvmOverrides, StateOverride},
18    BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo,
19};
20use futures::Future;
21use reth_chainspec::{ChainSpecProvider, EthChainSpec};
22use reth_errors::{ProviderError, RethError};
23use reth_evm::{
24    block::BlockExecutor, env::BlockEnvironment, execute::BlockBuilder, ConfigureEvm, Evm,
25    EvmEnvFor, HaltReasonFor, InspectorFor, TransactionEnvMut, TxEnvFor,
26};
27use reth_node_api::BlockBody;
28use reth_primitives_traits::Recovered;
29use reth_revm::{
30    cancelled::CancelOnDrop,
31    database::StateProviderDatabase,
32    db::{bal::EvmDatabaseError, State},
33};
34use reth_rpc_convert::{RpcConvert, RpcTxReq};
35use reth_rpc_eth_types::{
36    cache::db::StateProviderTraitObjWrapper,
37    error::{AsEthApiError, FromEthApiError},
38    simulate::{self, EthSimulateError},
39    EthApiError, StateCacheDb,
40};
41use reth_storage_api::{BlockIdReader, ProviderTx, StateProviderBox};
42use revm::{
43    context::Block,
44    context_interface::{result::ResultAndState, Transaction},
45    Database, DatabaseCommit,
46};
47use revm_inspectors::{access_list::AccessListInspector, transfer::TransferInspector};
48use tracing::{trace, warn};
49
50/// Result type for `eth_simulateV1` RPC method.
51pub type SimulatedBlocksResult<N, E> = Result<Vec<SimulatedBlock<RpcBlock<N>>>, E>;
52
53/// Execution related functions for the [`EthApiServer`](crate::EthApiServer) trait in
54/// the `eth_` namespace.
55pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthApiTypes {
56    /// Estimate gas needed for execution of the `request` at the [`BlockId`].
57    fn estimate_gas_at(
58        &self,
59        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
60        at: BlockId,
61        overrides: EvmOverrides,
62    ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
63        EstimateCall::estimate_gas_at(self, request, at, overrides)
64    }
65
66    /// `eth_simulateV1` executes an arbitrary number of transactions on top of the requested state.
67    /// The transactions are packed into individual blocks. Overrides can be provided.
68    ///
69    /// See also: <https://github.com/ethereum/go-ethereum/pull/27720>
70    fn simulate_v1(
71        &self,
72        payload: SimulatePayload<RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>>,
73        block: Option<BlockId>,
74    ) -> impl Future<Output = SimulatedBlocksResult<Self::NetworkTypes, Self::Error>> + Send {
75        async move {
76            if payload.block_state_calls.len() > self.max_simulate_blocks() as usize {
77                return Err(EthApiError::other(EthSimulateError::TooManyBlocks).into())
78            }
79
80            let block = block.unwrap_or_default();
81
82            let SimulatePayload {
83                block_state_calls,
84                trace_transfers,
85                validation,
86                return_full_transactions,
87            } = payload;
88
89            if block_state_calls.is_empty() {
90                return Err(EthApiError::InvalidParams(String::from("calls are empty.")).into())
91            }
92
93            let _permit = self.acquire_owned_blocking_io().await;
94
95            let base_block =
96                self.recovered_block(block).await?.ok_or(EthApiError::HeaderNotFound(block))?;
97            let parent = base_block.sealed_header().clone();
98            let max_simulate_blocks = self.max_simulate_blocks();
99
100            self.spawn_with_state_at_block(block, move |this, mut db| {
101                let mut parent = parent;
102
103                let chain_id = this.provider().chain_spec().chain_id();
104
105                // Validate block ordering and fill gaps with empty blocks so every entry has an
106                // explicit `number` and `time` override and the chain is contiguous (see the
107                // execution-apis spec note: "If the block number is increased more than 1 compared
108                // to the previous block, new empty blocks are generated in between.").
109                let block_state_calls = simulate::sanitize_chain(
110                    block_state_calls,
111                    &parent,
112                    chain_id,
113                    max_simulate_blocks,
114                )?;
115
116                let mut blocks: Vec<SimulatedBlock<RpcBlock<Self::NetworkTypes>>> =
117                    Vec::with_capacity(block_state_calls.len());
118
119                let call_gas_limit = this.call_gas_limit();
120                let mut remaining_call_gas_limit = (call_gas_limit > 0).then_some(call_gas_limit);
121
122                for block in block_state_calls {
123                    let SimBlock { block_overrides, state_overrides, calls } = block;
124
125                    let attributes = this
126                        .pending_env_builder()
127                        .pending_env_attributes(&parent, block_overrides.as_ref())
128                        .map_err(Self::Error::from_eth_err)?;
129
130                    let mut evm_env = this
131                        .evm_config()
132                        .next_evm_env(&parent, &attributes)
133                        .map_err(RethError::other)
134                        .map_err(Self::Error::from_eth_err)?;
135
136                    // Always disable EIP-3607
137                    evm_env.cfg_env.disable_eip3607 = true;
138
139                    if !validation {
140                        // If not explicitly required, we disable nonce check <https://github.com/paradigmxyz/reth/issues/16108>
141                        evm_env.cfg_env.disable_nonce_check = true;
142                        evm_env.cfg_env.disable_base_fee = true;
143                        evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
144                        evm_env.block_env.inner_mut().basefee = 0;
145                    }
146
147                    // Set prevrandao to zero for simulated blocks by default,
148                    // matching spec behavior where MixDigest is zero-initialized.
149                    // If user provides an override, it will be applied by apply_block_overrides.
150                    evm_env.block_env.inner_mut().prevrandao = Some(B256::ZERO);
151
152                    if let Some(block_overrides) = block_overrides {
153                        // ensure we don't allow uncapped gas limit per block
154                        if let Some(gas_limit_override) = block_overrides.gas_limit &&
155                            gas_limit_override > evm_env.block_env.gas_limit() &&
156                            gas_limit_override > this.call_gas_limit()
157                        {
158                            return Err(EthApiError::other(EthSimulateError::GasLimitReached).into())
159                        }
160                        apply_block_overrides(
161                            block_overrides,
162                            &mut db,
163                            evm_env.block_env.inner_mut(),
164                        );
165                    }
166                    if let Some(ref state_overrides) = state_overrides {
167                        apply_state_overrides(state_overrides.clone(), &mut db)
168                            .map_err(Self::Error::from_eth_err)?;
169                    }
170
171                    let chain_id = evm_env.cfg_env.chain_id;
172
173                    let ctx = this
174                        .evm_config()
175                        .context_for_next_block(&parent, attributes)
176                        .map_err(RethError::other)
177                        .map_err(Self::Error::from_eth_err)?;
178                    let map_err = |e: EthApiError| -> Self::Error {
179                        match e.as_simulate_error() {
180                            Some(sim_err) => Self::Error::from_eth_err(EthApiError::other(sim_err)),
181                            None => Self::Error::from_eth_err(e),
182                        }
183                    };
184
185                    let (result, results) = if trace_transfers {
186                        // prepare inspector to capture transfer inside the evm so they are recorded
187                        // and included in logs
188                        let inspector = TransferInspector::new(false).with_logs(true);
189                        let evm = this
190                            .evm_config()
191                            .evm_with_env_and_inspector(&mut db, evm_env, inspector);
192                        let mut builder = this.evm_config().create_block_builder(evm, &parent, ctx);
193
194                        if let Some(ref state_overrides) = state_overrides {
195                            simulate::apply_precompile_overrides(
196                                state_overrides,
197                                builder.evm_mut().precompiles_mut(),
198                            )
199                            .map_err(|e| Self::Error::from_eth_err(EthApiError::other(e)))?;
200                        }
201
202                        simulate::execute_transactions(
203                            builder,
204                            calls,
205                            &mut remaining_call_gas_limit,
206                            chain_id,
207                            this.converter(),
208                        )
209                        .map_err(map_err)?
210                    } else {
211                        let evm = this.evm_config().evm_with_env(&mut db, evm_env);
212                        let mut builder = this.evm_config().create_block_builder(evm, &parent, ctx);
213
214                        if let Some(ref state_overrides) = state_overrides {
215                            simulate::apply_precompile_overrides(
216                                state_overrides,
217                                builder.evm_mut().precompiles_mut(),
218                            )
219                            .map_err(|e| Self::Error::from_eth_err(EthApiError::other(e)))?;
220                        }
221
222                        simulate::execute_transactions(
223                            builder,
224                            calls,
225                            &mut remaining_call_gas_limit,
226                            chain_id,
227                            this.converter(),
228                        )
229                        .map_err(map_err)?
230                    };
231
232                    parent = result.block.clone_sealed_header();
233
234                    let block = simulate::build_simulated_block::<Self::Error, _>(
235                        result.block,
236                        results,
237                        return_full_transactions.into(),
238                        this.converter(),
239                    )?;
240
241                    blocks.push(block);
242                }
243
244                Ok(blocks)
245            })
246            .await
247        }
248    }
249
250    /// Executes the call request (`eth_call`) and returns the output
251    fn call(
252        &self,
253        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
254        block_number: Option<BlockId>,
255        overrides: EvmOverrides,
256    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
257        async move {
258            let _permit = self.acquire_owned_blocking_io().await;
259            let res =
260                self.transact_call_at(request, block_number.unwrap_or_default(), overrides).await?;
261
262            Self::Error::ensure_success(res.result)
263        }
264    }
265
266    /// Simulate arbitrary number of transactions at an arbitrary blockchain index, with the
267    /// optionality of state overrides
268    fn call_many(
269        &self,
270        bundles: Vec<Bundle<RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>>>,
271        state_context: Option<StateContext>,
272        mut state_override: Option<StateOverride>,
273    ) -> impl Future<Output = Result<Vec<Vec<EthCallResponse>>, Self::Error>> + Send {
274        async move {
275            // Check if the vector of bundles is empty
276            if bundles.is_empty() {
277                return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into());
278            }
279
280            let _permit = self.acquire_owned_blocking_io().await;
281
282            let StateContext { transaction_index, block_number } =
283                state_context.unwrap_or_default();
284            let transaction_index = transaction_index.unwrap_or_default();
285
286            let mut target_block = block_number.unwrap_or_default();
287            let is_block_target_pending = target_block.is_pending();
288
289            // if it's not pending, we should always use block_hash over block_number to ensure that
290            // different provider calls query data related to the same block.
291            if !is_block_target_pending {
292                let Some(block_hash) = self
293                    .provider()
294                    .block_hash_for_id(target_block)
295                    .map_err(Self::Error::from_eth_err::<ProviderError>)?
296                else {
297                    return Err(EthApiError::HeaderNotFound(target_block).into())
298                };
299                target_block = block_hash.into();
300            }
301
302            let block = self
303                .recovered_block(target_block)
304                .await?
305                .ok_or(EthApiError::HeaderNotFound(target_block))?;
306            let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
307
308            // we're essentially replaying the transactions in the block here, hence we need the
309            // state that points to the beginning of the block, which is the state at
310            // the parent block
311            let mut at = block.parent_hash();
312            let mut replay_block_txs = true;
313
314            let num_txs =
315                transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
316            // but if all transactions are to be replayed, we can use the state at the block itself,
317            // however only if we're not targeting the pending block, because for pending we can't
318            // rely on the block's state being available
319            if !is_block_target_pending && num_txs == block.body().transactions().len() {
320                at = block.hash();
321                replay_block_txs = false;
322            }
323
324            self.spawn_with_state_at_block(at, move |this, mut db| {
325                let mut all_results = Vec::with_capacity(bundles.len());
326
327                if replay_block_txs {
328                    let mut executor = RpcNodeCore::evm_config(&this)
329                        .executor_for_block(&mut db, block.sealed_block())
330                        .map_err(RethError::other)
331                        .map_err(Self::Error::from_eth_err)?;
332                    executor.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
333                    for tx in block.transactions_recovered().take(num_txs) {
334                        executor.execute_transaction(tx).map_err(Self::Error::from_eth_err)?;
335                    }
336                }
337
338                // transact all bundles
339                for (bundle_index, bundle) in bundles.into_iter().enumerate() {
340                    let Bundle { transactions, block_override } = bundle;
341                    if transactions.is_empty() {
342                        // Skip empty bundles
343                        continue;
344                    }
345
346                    let mut bundle_results = Vec::with_capacity(transactions.len());
347                    let block_overrides = block_override.map(Box::new);
348
349                    // transact all transactions in the bundle
350                    for (tx_index, tx) in transactions.into_iter().enumerate() {
351                        // Apply overrides, state overrides are only applied for the first tx in the
352                        // request
353                        let overrides =
354                            EvmOverrides::new(state_override.take(), block_overrides.clone());
355
356                        let (current_evm_env, prepared_tx) = this
357                            .prepare_call_env(evm_env.clone(), tx, &mut db, overrides)
358                            .map_err(|err| {
359                                Self::Error::from_eth_err(EthApiError::call_many_error(
360                                    bundle_index,
361                                    tx_index,
362                                    err.into(),
363                                ))
364                            })?;
365                        let res = this.transact(&mut db, current_evm_env, prepared_tx).map_err(
366                            |err| {
367                                Self::Error::from_eth_err(EthApiError::call_many_error(
368                                    bundle_index,
369                                    tx_index,
370                                    err.into(),
371                                ))
372                            },
373                        )?;
374
375                        match Self::Error::ensure_success(res.result) {
376                            Ok(output) => {
377                                bundle_results
378                                    .push(EthCallResponse { value: Some(output), error: None });
379                            }
380                            Err(err) => {
381                                bundle_results.push(EthCallResponse {
382                                    value: None,
383                                    error: Some(err.to_string()),
384                                });
385                            }
386                        }
387
388                        // Commit state changes after each transaction to allow subsequent calls to
389                        // see the updates
390                        db.commit(res.state);
391                    }
392
393                    all_results.push(bundle_results);
394                }
395
396                Ok(all_results)
397            })
398            .await
399        }
400    }
401
402    /// Creates [`AccessListResult`] for the [`RpcTxReq`] at the given
403    /// [`BlockId`], or latest block.
404    fn create_access_list_at(
405        &self,
406        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
407        block_number: Option<BlockId>,
408        state_override: Option<StateOverride>,
409    ) -> impl Future<Output = Result<AccessListResult, Self::Error>> + Send
410    where
411        Self: Trace,
412    {
413        async move {
414            let block_id = block_number.unwrap_or_default();
415            let (evm_env, at) = self.evm_env_at(block_id).await?;
416
417            self.spawn_blocking_io_fut(async move |this| {
418                this.create_access_list_with(evm_env, at, request, state_override).await
419            })
420            .await
421        }
422    }
423
424    /// Creates [`AccessListResult`] for the [`RpcTxReq`] at the given
425    /// [`BlockId`].
426    fn create_access_list_with(
427        &self,
428        mut evm_env: EvmEnvFor<Self::Evm>,
429        at: BlockId,
430        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
431        state_override: Option<StateOverride>,
432    ) -> impl Future<Output = Result<AccessListResult, Self::Error>> + Send
433    where
434        Self: Trace,
435    {
436        self.spawn_blocking_io_fut(async move |this| {
437            let state = this.state_at_block_id(at).await?;
438            let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
439
440            if let Some(state_overrides) = state_override {
441                apply_state_overrides(state_overrides, &mut db)
442                    .map_err(Self::Error::from_eth_err)?;
443            }
444
445            // Read fields from request before consuming it in create_txn_env
446            let request_has_gas_limit = request.as_ref().gas_limit().is_some();
447            let initial = request.as_ref().access_list().cloned().unwrap_or_default();
448
449            let mut tx_env = this.create_txn_env(&evm_env, request, &mut db)?;
450
451            // we want to disable this in eth_createAccessList, since this is common practice used
452            // by other node impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
453            evm_env.cfg_env.disable_block_gas_limit = true;
454
455            // The basefee should be ignored for eth_createAccessList
456            // See:
457            // <https://github.com/ethereum/go-ethereum/blob/8990c92aea01ca07801597b00c0d83d4e2d9b811/internal/ethapi/api.go#L1476-L1476>
458            evm_env.cfg_env.disable_base_fee = true;
459
460            // Disabled because eth_createAccessList is sometimes used with non-eoa senders
461            evm_env.cfg_env.disable_eip3607 = true;
462
463            // Disable additional fee charges (e.g. L2 operator fees),
464            // consistent with prepare_call_env and estimate_gas_with.
465            evm_env.cfg_env.disable_fee_charge = true;
466
467            // Disable EIP-7825 transaction gas limit cap so that the gas limit
468            // fallback (block gas limit) is not rejected when it exceeds the
469            // per-tx cap (2^24 ≈ 16.7M post-Osaka).
470            evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
471
472            if !request_has_gas_limit && tx_env.gas_price() > 0 {
473                let cap = this.caller_gas_allowance(&mut db, &evm_env, &tx_env)?;
474                // no gas limit was provided in the request, so we need to cap the request's gas
475                // limit
476                tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit()));
477            }
478
479            let mut inspector = AccessListInspector::new(initial);
480
481            let result = this.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
482            let access_list = inspector.into_access_list();
483            let gas_used = result.result.tx_gas_used();
484            tx_env.set_access_list(access_list.clone());
485            if let Err(err) = Self::Error::ensure_success(result.result) {
486                return Ok(AccessListResult {
487                    access_list,
488                    gas_used: U256::from(gas_used),
489                    error: Some(err.to_string()),
490                });
491            }
492
493            // transact again to get the exact gas used
494            let result = this.transact(&mut db, evm_env, tx_env)?;
495            let gas_used = result.result.tx_gas_used();
496            let error = Self::Error::ensure_success(result.result).err().map(|e| e.to_string());
497
498            Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error })
499        })
500    }
501}
502
503/// Executes code on state.
504pub trait Call:
505    LoadState<
506        RpcConvert: RpcConvert<Evm = Self::Evm>,
507        Error: FromEvmError<Self::Evm>
508                   + From<<Self::RpcConvert as RpcConvert>::Error>
509                   + From<ProviderError>,
510    > + SpawnBlocking
511{
512    /// Returns default gas limit to use for `eth_call` and tracing RPC methods.
513    ///
514    /// Data access in default trait method implementations.
515    fn call_gas_limit(&self) -> u64;
516
517    /// Returns the maximum number of blocks accepted for `eth_simulateV1`.
518    fn max_simulate_blocks(&self) -> u64;
519
520    /// Returns the maximum memory the EVM can allocate per RPC request.
521    fn evm_memory_limit(&self) -> u64;
522
523    /// Returns the max gas limit that the caller can afford given a transaction environment.
524    fn caller_gas_allowance(
525        &self,
526        mut db: impl Database<Error: Into<EthApiError>>,
527        _evm_env: &EvmEnvFor<Self::Evm>,
528        tx_env: &TxEnvFor<Self::Evm>,
529    ) -> Result<u64, Self::Error> {
530        alloy_evm::call::caller_gas_allowance(&mut db, tx_env).map_err(Self::Error::from_eth_err)
531    }
532
533    /// Executes the closure with the state that corresponds to the given [`BlockId`].
534    fn with_state_at_block<F, R>(
535        &self,
536        at: BlockId,
537        f: F,
538    ) -> impl Future<Output = Result<R, Self::Error>> + Send
539    where
540        R: Send + 'static,
541        F: FnOnce(Self, StateProviderBox) -> Result<R, Self::Error> + Send + 'static,
542    {
543        self.spawn_blocking_io_fut(async move |this| {
544            let state = this.state_at_block_id(at).await?;
545            f(this, state)
546        })
547    }
548
549    /// Executes the `TxEnv` against the given [Database] without committing state
550    /// changes.
551    fn transact<DB>(
552        &self,
553        db: DB,
554        evm_env: EvmEnvFor<Self::Evm>,
555        tx_env: TxEnvFor<Self::Evm>,
556    ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
557    where
558        DB: Database<Error = EvmDatabaseError<ProviderError>> + fmt::Debug,
559    {
560        let mut evm = self.evm_config().evm_with_env(db, evm_env);
561        let res = evm.transact(tx_env).map_err(Self::Error::from_evm_err)?;
562
563        Ok(res)
564    }
565
566    /// Executes the [`reth_evm::EvmEnv`] against the given [Database] without committing state
567    /// changes.
568    fn transact_with_inspector<DB, I>(
569        &self,
570        db: DB,
571        evm_env: EvmEnvFor<Self::Evm>,
572        tx_env: TxEnvFor<Self::Evm>,
573        inspector: I,
574    ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
575    where
576        DB: Database<Error = EvmDatabaseError<ProviderError>> + fmt::Debug,
577        I: InspectorFor<Self::Evm, DB>,
578    {
579        let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
580        let res = evm.transact(tx_env).map_err(Self::Error::from_evm_err)?;
581
582        Ok(res)
583    }
584
585    /// Executes the call request at the given [`BlockId`].
586    ///
587    /// This spawns a new task that obtains the state for the given [`BlockId`] and then transacts
588    /// the call [`Self::transact`]. If the future is dropped before the (blocking) transact
589    /// call is invoked, then the task is cancelled early, (for example if the request is terminated
590    /// early client-side).
591    fn transact_call_at(
592        &self,
593        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
594        at: BlockId,
595        overrides: EvmOverrides,
596    ) -> impl Future<Output = Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>> + Send
597    where
598        Self: LoadPendingBlock,
599    {
600        async move {
601            let guard = CancelOnDrop::default();
602            let cancel = guard.clone();
603            let this = self.clone();
604
605            let res = self
606                .spawn_with_call_at(request, at, overrides, move |db, evm_env, tx_env| {
607                    if cancel.is_cancelled() {
608                        // callsite dropped the guard
609                        return Err(EthApiError::InternalEthError.into())
610                    }
611                    this.transact(db, evm_env, tx_env)
612                })
613                .await;
614            drop(guard);
615            res
616        }
617    }
618
619    /// Executes the closure with the state that corresponds to the given [`BlockId`] on a new task
620    fn spawn_with_state_at_block<F, R>(
621        &self,
622        at: impl Into<BlockId>,
623        f: F,
624    ) -> impl Future<Output = Result<R, Self::Error>> + Send
625    where
626        F: FnOnce(Self, StateCacheDb) -> Result<R, Self::Error> + Send + 'static,
627        R: Send + 'static,
628    {
629        let at = at.into();
630        self.spawn_blocking_io_fut(async move |this| {
631            let state = this.state_at_block_id(at).await?;
632            let db = State::builder()
633                .with_database(StateProviderDatabase::new(StateProviderTraitObjWrapper(state)))
634                .build();
635            f(this, db)
636        })
637    }
638
639    /// Prepares the state and env for the given [`RpcTxReq`] at the given [`BlockId`] and
640    /// executes the closure on a new task returning the result of the closure.
641    ///
642    /// This returns the configured [`reth_evm::EvmEnv`] for the given [`RpcTxReq`] at
643    /// the given [`BlockId`] and with configured call settings: `prepare_call_env`.
644    ///
645    /// This is primarily used by `eth_call`.
646    ///
647    /// # Blocking behaviour
648    ///
649    /// This assumes executing the call is relatively more expensive on IO than CPU because it
650    /// transacts a single transaction on an empty in memory database. Because `eth_call`s are
651    /// usually allowed to consume a lot of gas, this also allows a lot of memory operations so
652    /// we assume this is not primarily CPU bound and instead spawn the call on a regular tokio task
653    /// instead, where blocking IO is less problematic.
654    fn spawn_with_call_at<F, R>(
655        &self,
656        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
657        at: BlockId,
658        overrides: EvmOverrides,
659        f: F,
660    ) -> impl Future<Output = Result<R, Self::Error>> + Send
661    where
662        Self: LoadPendingBlock,
663        F: FnOnce(
664                &mut StateCacheDb,
665                EvmEnvFor<Self::Evm>,
666                TxEnvFor<Self::Evm>,
667            ) -> Result<R, Self::Error>
668            + Send
669            + 'static,
670        R: Send + 'static,
671    {
672        async move {
673            let (evm_env, at) = self.evm_env_at(at).await?;
674            self.spawn_with_state_at_block(at, move |this, mut db| {
675                let (evm_env, tx_env) =
676                    this.prepare_call_env(evm_env, request, &mut db, overrides)?;
677
678                f(&mut db, evm_env, tx_env)
679            })
680            .await
681        }
682    }
683
684    /// Retrieves the transaction if it exists and executes it.
685    ///
686    /// Before the transaction is executed, all previous transaction in the block are applied to the
687    /// state by executing them first.
688    /// The callback `f` is invoked with the [`ResultAndState`] after the transaction was executed
689    /// and the database that points to the beginning of the transaction.
690    ///
691    /// Note: Implementers should use a threadpool where blocking is allowed, such as
692    /// [`BlockingTaskPool`](reth_tasks::pool::BlockingTaskPool).
693    fn spawn_replay_transaction<F, R>(
694        &self,
695        hash: B256,
696        f: F,
697    ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
698    where
699        Self: LoadBlock + LoadTransaction,
700        F: FnOnce(
701                TransactionInfo,
702                ResultAndState<HaltReasonFor<Self::Evm>>,
703                StateCacheDb,
704            ) -> Result<R, Self::Error>
705            + Send
706            + 'static,
707        R: Send + 'static,
708    {
709        async move {
710            let (transaction, block) = match self.transaction_and_block(hash).await? {
711                None => return Ok(None),
712                Some(res) => res,
713            };
714            let (tx, tx_info) = transaction.split();
715
716            // we need to get the state of the parent block because we're essentially replaying the
717            // block the transaction is included in
718            let parent_block = block.parent_hash();
719
720            self.spawn_with_state_at_block(parent_block, move |this, mut db| {
721                let block_txs = block.transactions_recovered();
722
723                let mut executor = RpcNodeCore::evm_config(&this)
724                    .executor_for_block(&mut db, block.sealed_block())
725                    .map_err(RethError::other)
726                    .map_err(Self::Error::from_eth_err)?;
727                executor.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
728
729                // replay all transactions prior to the targeted transaction
730                for block_tx in block_txs {
731                    if block_tx.tx_hash() == tx.tx_hash() {
732                        break;
733                    }
734                    executor.execute_transaction(block_tx).map_err(Self::Error::from_eth_err)?;
735                }
736
737                let tx_env = RpcNodeCore::evm_config(&this).tx_env(tx);
738
739                let res = executor.evm_mut().transact(tx_env).map_err(Self::Error::from_evm_err)?;
740                drop(executor);
741                f(tx_info, res, db)
742            })
743            .await
744            .map(Some)
745        }
746    }
747
748    /// Replays all the transactions until the target transaction is found.
749    ///
750    /// All transactions before the target transaction are executed and their changes are written to
751    /// the _runtime_ db ([`State`]).
752    ///
753    /// Note: This assumes the target transaction is in the given iterator.
754    /// Returns the index of the target transaction in the given iterator.
755    fn replay_transactions_until<'a, DB, I>(
756        &self,
757        db: &mut DB,
758        evm_env: EvmEnvFor<Self::Evm>,
759        transactions: I,
760        target_tx_hash: B256,
761    ) -> Result<usize, Self::Error>
762    where
763        DB: Database<Error = EvmDatabaseError<ProviderError>> + DatabaseCommit + core::fmt::Debug,
764        I: IntoIterator<Item = Recovered<&'a ProviderTx<Self::Provider>>>,
765    {
766        let mut evm = self.evm_config().evm_with_env(db, evm_env);
767        let mut index = 0;
768        for tx in transactions {
769            if *tx.tx_hash() == target_tx_hash {
770                // reached the target transaction
771                break
772            }
773
774            let tx_env = self.evm_config().tx_env(tx);
775            evm.transact_commit(tx_env).map_err(Self::Error::from_evm_err)?;
776            index += 1;
777        }
778        Ok(index)
779    }
780
781    ///
782    /// All `TxEnv` fields are derived from the given [`RpcTxReq`], if fields are
783    /// `None`, they fall back to the [`reth_evm::EvmEnv`]'s settings.
784    fn create_txn_env(
785        &self,
786        evm_env: &EvmEnvFor<Self::Evm>,
787        mut request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
788        mut db: impl Database<Error: Into<EthApiError>>,
789    ) -> Result<TxEnvFor<Self::Evm>, Self::Error> {
790        if request.as_ref().nonce().is_none() {
791            let nonce = db
792                .basic(request.as_ref().from().unwrap_or_default())
793                .map_err(Into::into)?
794                .map(|acc| acc.nonce)
795                .unwrap_or_default();
796            request.as_mut().set_nonce(nonce);
797        }
798
799        Ok(self.converter().tx_env(request, evm_env)?)
800    }
801
802    /// Prepares the [`reth_evm::EvmEnv`] for execution of calls.
803    ///
804    /// Does not commit any changes to the underlying database.
805    ///
806    /// ## EVM settings
807    ///
808    /// 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>:
809    ///
810    ///  - `disable_eip3607` is set to `true`
811    ///  - `disable_base_fee` is set to `true`
812    ///  - `nonce` is set to `None`
813    ///
814    /// In addition, this changes the block's gas limit to the configured [`Self::call_gas_limit`].
815    #[expect(clippy::type_complexity)]
816    fn prepare_call_env<DB>(
817        &self,
818        mut evm_env: EvmEnvFor<Self::Evm>,
819        mut request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
820        db: &mut DB,
821        overrides: EvmOverrides,
822    ) -> Result<(EvmEnvFor<Self::Evm>, TxEnvFor<Self::Evm>), Self::Error>
823    where
824        DB: Database + DatabaseCommit + OverrideBlockHashes,
825        EthApiError: From<<DB as Database>::Error>,
826    {
827        // track whether the request has a gas limit set
828        let request_has_gas_limit = request.as_ref().gas_limit().is_some();
829
830        if let Some(requested_gas) = request.as_ref().gas_limit() {
831            let global_gas_cap = self.call_gas_limit();
832            if global_gas_cap != 0 && global_gas_cap < requested_gas {
833                warn!(target: "rpc::eth::call", ?request, ?global_gas_cap, "Capping gas limit to global gas cap");
834                request.as_mut().set_gas_limit(global_gas_cap);
835            }
836        } else {
837            // cap request's gas limit to call gas limit
838            request.as_mut().set_gas_limit(self.call_gas_limit());
839        }
840
841        // Disable block gas limit check to allow executing transactions with higher gas limit (call
842        // gas limit): https://github.com/paradigmxyz/reth/issues/18577
843        evm_env.cfg_env.disable_block_gas_limit = true;
844
845        // Disabled because eth_call is sometimes used with eoa senders
846        // See <https://github.com/paradigmxyz/reth/issues/1959>
847        evm_env.cfg_env.disable_eip3607 = true;
848
849        // The basefee should be ignored for eth_call
850        // See:
851        // <https://github.com/ethereum/go-ethereum/blob/ee8e83fa5f6cb261dad2ed0a7bbcde4930c41e6c/internal/ethapi/api.go#L985>
852        evm_env.cfg_env.disable_base_fee = true;
853
854        // Disable EIP-7825 transaction gas limit to support larger transactions
855        evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
856
857        // Disable additional fee charges, e.g. opstack operator fee charge
858        // See:
859        // <https://github.com/paradigmxyz/reth/issues/18470>
860        evm_env.cfg_env.disable_fee_charge = true;
861
862        evm_env.cfg_env.memory_limit = self.evm_memory_limit();
863
864        // set nonce to None so that the correct nonce is chosen by the EVM
865        request.as_mut().take_nonce();
866
867        if let Some(block_overrides) = overrides.block {
868            apply_block_overrides(*block_overrides, db, evm_env.block_env.inner_mut());
869        }
870        if let Some(state_overrides) = overrides.state {
871            apply_state_overrides(state_overrides, db)
872                .map_err(EthApiError::from_state_overrides_err)?;
873        }
874
875        let mut tx_env = self.create_txn_env(&evm_env, request, &mut *db)?;
876
877        // lower the basefee to 0 to avoid breaking EVM invariants (basefee < gasprice): <https://github.com/ethereum/go-ethereum/blob/355228b011ef9a85ebc0f21e7196f892038d49f0/internal/ethapi/api.go#L700-L704>
878        if tx_env.gas_price() == 0 {
879            evm_env.block_env.inner_mut().basefee = 0;
880        }
881
882        if !request_has_gas_limit {
883            // No gas limit was provided in the request, so we need to cap the transaction gas limit
884            if tx_env.gas_price() > 0 {
885                // If gas price is specified, cap transaction gas limit with caller allowance
886                trace!(target: "rpc::eth::call", ?tx_env, "Applying gas limit cap with caller allowance");
887                let cap = self.caller_gas_allowance(db, &evm_env, &tx_env)?;
888                // ensure we cap gas_limit to the block's
889                tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit()));
890            }
891        }
892
893        Ok((evm_env, tx_env))
894    }
895}