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