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