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