Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 90 additions & 68 deletions crates/rpc/rpc-eth-api/src/helpers/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -376,76 +376,89 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
at: BlockId,
request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
state_override: Option<StateOverride>,
) -> Result<AccessListResult, Self::Error>
) -> impl Future<Output = Result<AccessListResult, Self::Error>> + 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 <https://github.com/foundry-rs/foundry/issues/4388>
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:
// <https://github.com/ethereum/go-ethereum/blob/8990c92aea01ca07801597b00c0d83d4e2d9b811/internal/ethapi/api.go#L1476-L1476>
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 <https://github.com/foundry-rs/foundry/issues/4388>
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:
// <https://github.com/ethereum/go-ethereum/blob/8990c92aea01ca07801597b00c0d83d4e2d9b811/internal/ethapi/api.go#L1476-L1476>
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)
})
}
}

Expand All @@ -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<F, R>(&self, at: BlockId, f: F) -> Result<R, Self::Error>
fn with_state_at_block<F, R>(
&self,
at: BlockId,
f: F,
) -> impl Future<Output = Result<R, Self::Error>> + Send
where
F: FnOnce(StateProviderTraitObjWrapper<'_>) -> Result<R, Self::Error>,
R: Send + 'static,
F: FnOnce(Self, StateProviderTraitObjWrapper<'_>) -> Result<R, Self::Error>
+ 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
Expand Down Expand Up @@ -537,8 +559,8 @@ pub trait Call:
F: FnOnce(StateProviderTraitObjWrapper<'_>) -> Result<R, Self::Error> + 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))
})
}
Expand Down Expand Up @@ -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)));

Expand Down
4 changes: 2 additions & 2 deletions crates/rpc/rpc-eth-api/src/helpers/estimate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 32 additions & 21 deletions crates/rpc/rpc-eth-api/src/helpers/state.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -48,9 +50,10 @@ pub trait EthState: LoadState + SpawnBlocking {
address: Address,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<U256, Self::Error>> + 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())
Expand All @@ -64,9 +67,10 @@ pub trait EthState: LoadState + SpawnBlocking {
index: JsonStorageKey,
block_id: Option<BlockId>,
) -> impl Future<Output = Result<B256, Self::Error>> + 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()
Expand Down Expand Up @@ -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::<Vec<_>>();
let proof = state
.proof(Default::default(), address, &storage_keys)
Expand All @@ -127,8 +131,8 @@ pub trait EthState: LoadState + SpawnBlocking {
address: Address,
block_id: BlockId,
) -> impl Future<Output = Result<Option<Account>, 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) };

Expand Down Expand Up @@ -164,8 +168,8 @@ pub trait EthState: LoadState + SpawnBlocking {
address: Address,
block_id: BlockId,
) -> impl Future<Output = Result<AccountInfo, 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)?
Expand Down Expand Up @@ -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 <https://github.com/paradigmxyz/reth/issues/4515>
fn state_at_block_id(&self, at: BlockId) -> Result<StateProviderBox, Self::Error> {
self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err)
fn state_at_block_id(
&self,
at: BlockId,
) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send {
future::ready(self.provider().state_by_block_id(at).map_err(Self::Error::from_eth_err))
}

/// Returns the _latest_ state
Expand All @@ -216,11 +223,13 @@ pub trait LoadState: EthApiTypes + RpcNodeCoreExt {
fn state_at_block_id_or_latest(
&self,
block_id: Option<BlockId>,
) -> Result<StateProviderBox, Self::Error> {
if let Some(block_id) = block_id {
self.state_at_block_id(block_id)
} else {
Ok(self.latest_state()?)
) -> impl Future<Output = Result<StateProviderBox, Self::Error>> + Send {
async move {
if let Some(block_id) = block_id {
self.state_at_block_id(block_id).await
} else {
Ok(self.latest_state()?)
}
}
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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()
Expand Down
19 changes: 11 additions & 8 deletions crates/rpc/rpc-eth-api/src/helpers/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,21 @@ pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> {
config: TracingInspectorConfig,
at: BlockId,
f: F,
) -> Result<R, Self::Error>
) -> impl Future<Output = Result<R, Self::Error>> + Send
where
Self: Call,
R: Send + 'static,
F: FnOnce(
TracingInspector,
ResultAndState<HaltReasonFor<Self::Evm>>,
) -> Result<R, Self::Error>,
TracingInspector,
ResultAndState<HaltReasonFor<Self::Evm>>,
) -> Result<R, Self::Error>
+ 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)
})
}
Expand Down Expand Up @@ -292,7 +295,7 @@ pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> {
}

// 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();
Expand All @@ -302,7 +305,7 @@ pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> {
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)));

Expand Down