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