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