diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index b95b290de46..c4a04dd428d 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -361,8 +361,8 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA let block_id = block_number.unwrap_or_default(); let (evm_env, at) = self.evm_env_at(block_id).await?; - self.spawn_blocking_io(move |this| { - this.create_access_list_with(evm_env, at, request, state_override) + self.spawn_blocking_io_fut(move |this| async move { + this.create_access_list_with(evm_env, at, request, state_override).await }) .await } @@ -376,76 +376,89 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA at: BlockId, request: RpcTxReq<::Network>, state_override: Option, - ) -> Result + ) -> impl Future> + Send where Self: Trace, { - let state = self.state_at_block_id(at)?; - let mut db = CacheDB::new(StateProviderDatabase::new(state)); - - if let Some(state_overrides) = state_override { - apply_state_overrides(state_overrides, &mut db).map_err(Self::Error::from_eth_err)?; - } + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(at).await?; + let mut db = CacheDB::new(StateProviderDatabase::new(state)); - let mut tx_env = self.create_txn_env(&evm_env, request.clone(), &mut db)?; + if let Some(state_overrides) = state_override { + apply_state_overrides(state_overrides, &mut db) + .map_err(Self::Error::from_eth_err)?; + } - // we want to disable this in eth_createAccessList, since this is common practice used by - // other node impls and providers - evm_env.cfg_env.disable_block_gas_limit = true; + let mut tx_env = this.create_txn_env(&evm_env, request.clone(), &mut db)?; - // The basefee should be ignored for eth_createAccessList - // See: - // - evm_env.cfg_env.disable_base_fee = true; + // we want to disable this in eth_createAccessList, since this is common practice used + // by other node impls and providers + evm_env.cfg_env.disable_block_gas_limit = true; - // Disabled because eth_createAccessList is sometimes used with non-eoa senders - evm_env.cfg_env.disable_eip3607 = true; + // The basefee should be ignored for eth_createAccessList + // See: + // + evm_env.cfg_env.disable_base_fee = true; - if request.as_ref().gas_limit().is_none() && tx_env.gas_price() > 0 { - let cap = caller_gas_allowance(&mut db, &tx_env).map_err(Self::Error::from_eth_err)?; - // no gas limit was provided in the request, so we need to cap the request's gas limit - tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit)); - } + // Disabled because eth_createAccessList is sometimes used with non-eoa senders + evm_env.cfg_env.disable_eip3607 = true; - // can consume the list since we're not using the request anymore - let initial = request.as_ref().access_list().cloned().unwrap_or_default(); + if request.as_ref().gas_limit().is_none() && tx_env.gas_price() > 0 { + let cap = + caller_gas_allowance(&mut db, &tx_env).map_err(Self::Error::from_eth_err)?; + // no gas limit was provided in the request, so we need to cap the request's gas + // limit + tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit)); + } - let mut inspector = AccessListInspector::new(initial); + // can consume the list since we're not using the request anymore + let initial = request.as_ref().access_list().cloned().unwrap_or_default(); + + let mut inspector = AccessListInspector::new(initial); + + let result = this.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?; + let access_list = inspector.into_access_list(); + tx_env.set_access_list(access_list.clone()); + match result.result { + ExecutionResult::Halt { reason, gas_used } => { + let error = + Some(Self::Error::from_evm_halt(reason, tx_env.gas_limit()).to_string()); + return Ok(AccessListResult { + access_list, + gas_used: U256::from(gas_used), + error, + }) + } + ExecutionResult::Revert { output, gas_used } => { + let error = Some(RevertError::new(output).to_string()); + return Ok(AccessListResult { + access_list, + gas_used: U256::from(gas_used), + error, + }) + } + ExecutionResult::Success { .. } => {} + }; - let result = self.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?; - let access_list = inspector.into_access_list(); - tx_env.set_access_list(access_list.clone()); - match result.result { - ExecutionResult::Halt { reason, gas_used } => { - let error = - Some(Self::Error::from_evm_halt(reason, tx_env.gas_limit()).to_string()); - return Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error }) - } - ExecutionResult::Revert { output, gas_used } => { - let error = Some(RevertError::new(output).to_string()); - return Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error }) - } - ExecutionResult::Success { .. } => {} - }; - - // transact again to get the exact gas used - let gas_limit = tx_env.gas_limit(); - let result = self.transact(&mut db, evm_env, tx_env)?; - let res = match result.result { - ExecutionResult::Halt { reason, gas_used } => { - let error = Some(Self::Error::from_evm_halt(reason, gas_limit).to_string()); - AccessListResult { access_list, gas_used: U256::from(gas_used), error } - } - ExecutionResult::Revert { output, gas_used } => { - let error = Some(RevertError::new(output).to_string()); - AccessListResult { access_list, gas_used: U256::from(gas_used), error } - } - ExecutionResult::Success { gas_used, .. } => { - AccessListResult { access_list, gas_used: U256::from(gas_used), error: None } - } - }; + // transact again to get the exact gas used + let gas_limit = tx_env.gas_limit(); + let result = this.transact(&mut db, evm_env, tx_env)?; + let res = match result.result { + ExecutionResult::Halt { reason, gas_used } => { + let error = Some(Self::Error::from_evm_halt(reason, gas_limit).to_string()); + AccessListResult { access_list, gas_used: U256::from(gas_used), error } + } + ExecutionResult::Revert { output, gas_used } => { + let error = Some(RevertError::new(output).to_string()); + AccessListResult { access_list, gas_used: U256::from(gas_used), error } + } + ExecutionResult::Success { gas_used, .. } => { + AccessListResult { access_list, gas_used: U256::from(gas_used), error: None } + } + }; - Ok(res) + Ok(res) + }) } } @@ -467,12 +480,21 @@ pub trait Call: fn max_simulate_blocks(&self) -> u64; /// Executes the closure with the state that corresponds to the given [`BlockId`]. - fn with_state_at_block(&self, at: BlockId, f: F) -> Result + fn with_state_at_block( + &self, + at: BlockId, + f: F, + ) -> impl Future> + Send where - F: FnOnce(StateProviderTraitObjWrapper<'_>) -> Result, + R: Send + 'static, + F: FnOnce(Self, StateProviderTraitObjWrapper<'_>) -> Result + + Send + + 'static, { - let state = self.state_at_block_id(at)?; - f(StateProviderTraitObjWrapper(&state)) + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(at).await?; + f(this, StateProviderTraitObjWrapper(&state)) + }) } /// Executes the `TxEnv` against the given [Database] without committing state @@ -537,8 +559,8 @@ pub trait Call: F: FnOnce(StateProviderTraitObjWrapper<'_>) -> Result + Send + 'static, R: Send + 'static, { - self.spawn_tracing(move |this| { - let state = this.state_at_block_id(at)?; + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(at).await?; f(StateProviderTraitObjWrapper(&state)) }) } @@ -579,8 +601,8 @@ pub trait Call: async move { let (evm_env, at) = self.evm_env_at(at).await?; let this = self.clone(); - self.spawn_blocking_io(move |_| { - let state = this.state_at_block_id(at)?; + self.spawn_blocking_io_fut(move |_| async move { + let state = this.state_at_block_id(at).await?; let mut db = CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state))); diff --git a/crates/rpc/rpc-eth-api/src/helpers/estimate.rs b/crates/rpc/rpc-eth-api/src/helpers/estimate.rs index 3f58d97f7df..8b3df7bbc9b 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/estimate.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/estimate.rs @@ -281,8 +281,8 @@ pub trait EstimateCall: Call { async move { let (evm_env, at) = self.evm_env_at(at).await?; - self.spawn_blocking_io(move |this| { - let state = this.state_at_block_id(at)?; + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(at).await?; EstimateCall::estimate_gas_with(&this, evm_env, request, state, state_override) }) .await diff --git a/crates/rpc/rpc-eth-api/src/helpers/state.rs b/crates/rpc/rpc-eth-api/src/helpers/state.rs index c9daa1790dc..9dcb7a0bb23 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/state.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/state.rs @@ -1,5 +1,6 @@ //! Loads a pending block from database. Helper trait for `eth_` block, transaction, call and trace //! RPC methods. + use super::{EthApiSpec, LoadPendingBlock, SpawnBlocking}; use crate::{EthApiTypes, FromEthApiError, RpcNodeCore, RpcNodeCoreExt}; use alloy_consensus::constants::KECCAK_EMPTY; @@ -15,6 +16,7 @@ use reth_storage_api::{ BlockIdReader, BlockNumReader, StateProvider, StateProviderBox, StateProviderFactory, }; use reth_transaction_pool::TransactionPool; +use std::future; /// Helper methods for `eth_` methods relating to state (accounts). pub trait EthState: LoadState + SpawnBlocking { @@ -48,9 +50,10 @@ pub trait EthState: LoadState + SpawnBlocking { address: Address, block_id: Option, ) -> impl Future> + Send { - self.spawn_blocking_io(move |this| { + self.spawn_blocking_io_fut(move |this| async move { Ok(this - .state_at_block_id_or_latest(block_id)? + .state_at_block_id_or_latest(block_id) + .await? .account_balance(&address) .map_err(Self::Error::from_eth_err)? .unwrap_or_default()) @@ -64,9 +67,10 @@ pub trait EthState: LoadState + SpawnBlocking { index: JsonStorageKey, block_id: Option, ) -> impl Future> + Send { - self.spawn_blocking_io(move |this| { + self.spawn_blocking_io_fut(move |this| async move { Ok(B256::new( - this.state_at_block_id_or_latest(block_id)? + this.state_at_block_id_or_latest(block_id) + .await? .storage(address, index.as_b256()) .map_err(Self::Error::from_eth_err)? .unwrap_or_default() @@ -109,8 +113,8 @@ pub trait EthState: LoadState + SpawnBlocking { return Err(EthApiError::ExceedsMaxProofWindow.into()) } - self.spawn_blocking_io(move |this| { - let state = this.state_at_block_id(block_id)?; + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(block_id).await?; let storage_keys = keys.iter().map(|key| key.as_b256()).collect::>(); let proof = state .proof(Default::default(), address, &storage_keys) @@ -127,8 +131,8 @@ pub trait EthState: LoadState + SpawnBlocking { address: Address, block_id: BlockId, ) -> impl Future, Self::Error>> + Send { - self.spawn_blocking_io(move |this| { - let state = this.state_at_block_id(block_id)?; + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(block_id).await?; let account = state.basic_account(&address).map_err(Self::Error::from_eth_err)?; let Some(account) = account else { return Ok(None) }; @@ -164,8 +168,8 @@ pub trait EthState: LoadState + SpawnBlocking { address: Address, block_id: BlockId, ) -> impl Future> + Send { - self.spawn_blocking_io(move |this| { - let state = this.state_at_block_id(block_id)?; + self.spawn_blocking_io_fut(move |this| async move { + let state = this.state_at_block_id(block_id).await?; let account = state .basic_account(&address) .map_err(Self::Error::from_eth_err)? @@ -201,8 +205,11 @@ pub trait LoadState: EthApiTypes + RpcNodeCoreExt { /// /// Note: if not [`BlockNumberOrTag::Pending`](alloy_eips::BlockNumberOrTag) then this /// will only return canonical state. See also - fn state_at_block_id(&self, at: BlockId) -> Result { - self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err) + fn state_at_block_id( + &self, + at: BlockId, + ) -> impl Future> + Send { + future::ready(self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)) } /// Returns the _latest_ state @@ -216,11 +223,13 @@ pub trait LoadState: EthApiTypes + RpcNodeCoreExt { fn state_at_block_id_or_latest( &self, block_id: Option, - ) -> Result { - if let Some(block_id) = block_id { - self.state_at_block_id(block_id) - } else { - Ok(self.latest_state()?) + ) -> impl Future> + Send { + async move { + if let Some(block_id) = block_id { + self.state_at_block_id(block_id).await + } else { + Ok(self.latest_state()?) + } } } @@ -303,10 +312,11 @@ pub trait LoadState: EthApiTypes + RpcNodeCoreExt { where Self: SpawnBlocking, { - self.spawn_blocking_io(move |this| { + self.spawn_blocking_io_fut(move |this| async move { // first fetch the on chain nonce of the account let on_chain_account_nonce = this - .state_at_block_id_or_latest(block_id)? + .state_at_block_id_or_latest(block_id) + .await? .account_nonce(&address) .map_err(Self::Error::from_eth_err)? .unwrap_or_default(); @@ -348,9 +358,10 @@ pub trait LoadState: EthApiTypes + RpcNodeCoreExt { where Self: SpawnBlocking, { - self.spawn_blocking_io(move |this| { + self.spawn_blocking_io_fut(move |this| async move { Ok(this - .state_at_block_id_or_latest(block_id)? + .state_at_block_id_or_latest(block_id) + .await? .account_code(&address) .map_err(Self::Error::from_eth_err)? .unwrap_or_default() diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index 329b4292f00..404234ea3dc 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -56,18 +56,21 @@ pub trait Trace: LoadState> { config: TracingInspectorConfig, at: BlockId, f: F, - ) -> Result + ) -> impl Future> + Send where Self: Call, + R: Send + 'static, F: FnOnce( - TracingInspector, - ResultAndState>, - ) -> Result, + TracingInspector, + ResultAndState>, + ) -> Result + + Send + + 'static, { - self.with_state_at_block(at, |state| { + self.with_state_at_block(at, move |this, state| { let mut db = CacheDB::new(StateProviderDatabase::new(state)); let mut inspector = TracingInspector::new(config); - let res = self.inspect(&mut db, evm_env, tx_env, &mut inspector)?; + let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?; f(inspector, res) }) } @@ -292,7 +295,7 @@ pub trait Trace: LoadState> { } // replay all transactions of the block - self.spawn_tracing(move |this| { + self.spawn_blocking_io_fut(move |this| async move { // we need to get the state of the parent block because we're replaying this block // on top of its parent block's state let state_at = block.parent_hash(); @@ -302,7 +305,7 @@ pub trait Trace: LoadState> { let base_fee = evm_env.block_env.basefee; // now get the state - let state = this.state_at_block_id(state_at.into())?; + let state = this.state_at_block_id(state_at.into()).await?; let mut db = CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)));