Skip to main content

reth_rpc_eth_api/helpers/
call.rs

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