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