diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 9cfa3653de94..dc62f8b9d93f 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -75,7 +75,7 @@ tracing.workspace = true tracing-futures = "0.2" schnellru.workspace = true futures.workspace = true -derive_more = { workspace = true, default-features = false, features = ["deref", "deref_mut", "constructor"] } +derive_more.workspace = true dyn-clone.workspace = true auto_impl.workspace = true diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 026fea2d810a..a1b798274bfc 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -1,6 +1,6 @@ use crate::{ eth::{ - api::{EthTransactions, LoadState, SpawnBlocking}, + api::{EthTransactions, LoadPendingBlock, LoadState, SpawnBlocking}, error::{EthApiError, EthResult}, revm_utils::{prepare_call_env, EvmOverrides}, }, @@ -65,7 +65,7 @@ impl DebugApi { impl DebugApi where Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider + 'static, - Eth: EthTransactions + LoadState + SpawnBlocking + 'static, + Eth: EthTransactions + LoadState + SpawnBlocking + LoadPendingBlock + 'static, { /// Acquires a permit to execute a tracing call. async fn acquire_trace_permit(&self) -> Result { @@ -614,7 +614,7 @@ where impl DebugApiServer for DebugApi where Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider + 'static, - Eth: EthApiSpec + SpawnBlocking + LoadState + 'static, + Eth: EthApiSpec + SpawnBlocking + LoadState + LoadPendingBlock + 'static, { /// Handler for `debug_getRawHeader` async fn raw_header(&self, block_id: BlockId) -> RpcResult { diff --git a/crates/rpc/rpc/src/eth/api/traits/transaction.rs b/crates/rpc/rpc/src/eth/api/traits/transaction.rs index d82ddead88d5..15ba3b519dae 100644 --- a/crates/rpc/rpc/src/eth/api/traits/transaction.rs +++ b/crates/rpc/rpc/src/eth/api/traits/transaction.rs @@ -5,11 +5,13 @@ use std::{fmt, sync::Arc}; use reth_evm::ConfigureEvm; use reth_primitives::{ - revm::env::fill_block_env_with_coinbase, BlockId, Bytes, FromRecoveredPooledTransaction, - Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedBlockWithSenders, - TransactionMeta, TransactionSigned, TxHash, B256, + revm::env::{fill_block_env_with_coinbase, tx_env_with_recovered}, + BlockId, Bytes, FromRecoveredPooledTransaction, Header, IntoRecoveredTransaction, Receipt, + SealedBlock, SealedBlockWithSenders, TransactionMeta, TransactionSigned, TxHash, B256, +}; +use reth_provider::{ + BlockIdReader, BlockReaderIdExt, ReceiptProvider, StateProviderBox, TransactionsProvider, }; -use reth_provider::{BlockReaderIdExt, ReceiptProvider, StateProviderBox, TransactionsProvider}; use reth_revm::database::StateProviderDatabase; use reth_rpc_types::{AnyTransactionReceipt, TransactionInfo, TransactionRequest}; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; @@ -29,10 +31,13 @@ use revm_primitives::{ use crate::{ eth::{ - api::{BuildReceipt, EthState, LoadState, SpawnBlocking}, + api::{ + pending_block::PendingBlockEnv, BuildReceipt, EthState, LoadPendingBlock, LoadState, + SpawnBlocking, + }, cache::EthStateCache, error::{EthApiError, EthResult}, - revm_utils::{EvmOverrides, FillableTransaction}, + revm_utils::{prepare_call_env, EvmOverrides, FillableTransaction}, utils::recover_raw_transaction, TransactionSource, }, @@ -191,7 +196,7 @@ pub trait EthTransactions: Send + Sync { /// Returns default gas limit to use for `eth_call` and tracing RPC methods. fn call_gas_limit(&self) -> u64; - /// Executes the closure with the state that corresponds to the given [BlockId]. + /// Executes the closure with the state that corresponds to the given [`BlockId`]. fn with_state_at_block(&self, at: BlockId, f: F) -> EthResult where Self: LoadState, @@ -201,7 +206,7 @@ pub trait EthTransactions: Send + Sync { f(state) } - /// Executes the closure with the state that corresponds to the given [BlockId] on a new task + /// Executes the closure with the state that corresponds to the given [`BlockId`] on a new task async fn spawn_with_state_at_block(&self, at: BlockId, f: F) -> EthResult where Self: LoadState + SpawnBlocking, @@ -215,15 +220,28 @@ pub trait EthTransactions: Send + Sync { .await } - /// Returns the revm evm env for the requested [BlockId] + /// Returns the revm evm env for the requested [`BlockId`] /// - /// If the [BlockId] this will return the [BlockId] of the block the env was configured + /// If the [`BlockId`] this will return the [`BlockId`] of the block the env was configured /// for. - /// If the [BlockId] is pending, this will return the "Pending" tag, otherwise this returns the - /// hash of the exact block. + /// If the [`BlockId`] is pending, this will return the "Pending" tag, otherwise this returns + /// the hash of the exact block. async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnvWithHandlerCfg, BlockEnv, BlockId)> where - Self: LoadState; + Self: LoadState + LoadPendingBlock, + { + if at.is_pending() { + let PendingBlockEnv { cfg, block_env, origin } = self.pending_block_env_and_cfg()?; + Ok((cfg, block_env, origin.state_block_id())) + } else { + // Use cached values if there is no pending block + let block_hash = EthTransactions::provider(self) + .block_hash_for_id(at)? + .ok_or_else(|| EthApiError::UnknownBlockNumber)?; + let (cfg, env) = self.cache().get_evm_env(block_hash).await?; + Ok((cfg, env, block_hash.into())) + } + } /// Returns the revm evm env for the raw block header /// @@ -233,7 +251,7 @@ pub trait EthTransactions: Send + Sync { header: &Header, ) -> EthResult<(CfgEnvWithHandlerCfg, BlockEnv)> where - Self: LoadState, + Self: LoadState + LoadPendingBlock, { // get the parent config first let (cfg, mut block_env, _) = self.evm_env_at(header.parent_hash.into()).await?; @@ -483,9 +501,29 @@ pub trait EthTransactions: Send + Sync { f: F, ) -> EthResult where - Self: LoadState, + Self: LoadState + SpawnBlocking + LoadPendingBlock, F: FnOnce(&mut StateCacheDB, EnvWithHandlerCfg) -> EthResult + Send + 'static, - R: Send + 'static; + R: Send + 'static, + { + let (cfg, block_env, at) = self.evm_env_at(at).await?; + let this = self.clone(); + self.spawn_tracing(move |_| { + let state = this.state_at_block_id(at)?; + let mut db = CacheDB::new(StateProviderDatabase::new(state)); + + let env = prepare_call_env( + cfg, + block_env, + request, + this.call_gas_limit(), + &mut db, + overrides, + )?; + f(&mut db, env) + }) + .await + .map_err(|_| EthApiError::InternalBlockingTaskError) + } /// Executes the call request at the given [BlockId]. async fn transact_call_at( @@ -495,7 +533,11 @@ pub trait EthTransactions: Send + Sync { overrides: EvmOverrides, ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> where - Self: LoadState; + Self: LoadState + LoadPendingBlock, + { + let this = self.clone(); + self.spawn_with_call_at(request, at, overrides, move |db, env| this.transact(db, env)).await + } /// Executes the call request at the given [BlockId] on a new task and returns the result of the /// inspect call. @@ -507,8 +549,15 @@ pub trait EthTransactions: Send + Sync { inspector: I, ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> where - Self: LoadState, - I: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static; + Self: LoadState + LoadPendingBlock, + I: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, + { + let this = self.clone(); + self.spawn_with_call_at(request, at, overrides, move |db, env| { + this.inspect(db, env, inspector) + }) + .await + } /// Executes the transaction on top of the given [BlockId] with a tracer configured by the /// config. @@ -603,7 +652,7 @@ pub trait EthTransactions: Send + Sync { f: F, ) -> EthResult> where - Self: LoadState, + Self: LoadState + LoadPendingBlock, F: FnOnce(TransactionInfo, TracingInspector, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, @@ -624,9 +673,45 @@ pub trait EthTransactions: Send + Sync { /// [BlockingTaskPool](reth_tasks::pool::BlockingTaskPool). async fn spawn_replay_transaction(&self, hash: B256, f: F) -> EthResult> where - Self: LoadState, + Self: LoadState + LoadPendingBlock, F: FnOnce(TransactionInfo, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, - R: Send + 'static; + R: Send + 'static, + { + let (transaction, block) = match self.transaction_and_block(hash).await? { + None => return Ok(None), + Some(res) => res, + }; + let (tx, tx_info) = transaction.split(); + + let (cfg, block_env, _) = self.evm_env_at(block.hash().into()).await?; + + // we need to get the state of the parent block because we're essentially replaying the + // block the transaction is included in + let parent_block = block.parent_hash; + let block_txs = block.into_transactions_ecrecovered(); + + let this = self.clone(); + self.spawn_with_state_at_block(parent_block.into(), move |state| { + let mut db = CacheDB::new(StateProviderDatabase::new(state)); + + // replay all transactions prior to the targeted transaction + this.replay_transactions_until( + &mut db, + cfg.clone(), + block_env.clone(), + block_txs, + tx.hash, + )?; + + let env = + EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx)); + + let (res, _) = this.transact(&mut db, env)?; + f(tx_info, res, db) + }) + .await + .map(Some) + } /// Retrieves the transaction if it exists and returns its trace. /// @@ -640,16 +725,52 @@ pub trait EthTransactions: Send + Sync { async fn spawn_trace_transaction_in_block_with_inspector( &self, hash: B256, - inspector: Insp, + mut inspector: Insp, f: F, ) -> EthResult> where - Self: LoadState, + Self: LoadState + LoadPendingBlock, F: FnOnce(TransactionInfo, Insp, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, - R: Send + 'static; + R: Send + 'static, + { + let (transaction, block) = match self.transaction_and_block(hash).await? { + None => return Ok(None), + Some(res) => res, + }; + let (tx, tx_info) = transaction.split(); + + let (cfg, block_env, _) = self.evm_env_at(block.hash().into()).await?; + + // we need to get the state of the parent block because we're essentially replaying the + // block the transaction is included in + let parent_block = block.parent_hash; + let block_txs = block.into_transactions_ecrecovered(); + + let this = self.clone(); + self.spawn_with_state_at_block(parent_block.into(), move |state| { + let mut db = CacheDB::new(StateProviderDatabase::new(state)); + + // replay all transactions prior to the targeted transaction + this.replay_transactions_until( + &mut db, + cfg.clone(), + block_env.clone(), + block_txs, + tx.hash, + )?; + + let env = + EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx)); + + let (res, _) = this.inspect(&mut db, env, &mut inspector)?; + f(tx_info, inspector, res, db) + }) + .await + .map(Some) + } /// Executes all transactions of a block and returns a list of callback results invoked for each /// transaction in the block. diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 771bec08c820..eabc8941fbc4 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -28,8 +28,7 @@ use reth_transaction_pool::{TransactionOrigin, TransactionPool}; use revm::{ db::CacheDB, primitives::{ - db::DatabaseCommit, BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, EvmState, - ExecutionResult, ResultAndState, + db::DatabaseCommit, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState, }, Inspector, }; @@ -37,12 +36,12 @@ use revm::{ use crate::{ eth::{ api::{ - pending_block::PendingBlockEnv, BuildReceipt, EthState, EthTransactions, - LoadPendingBlock, LoadState, RawTransactionForwarder, SpawnBlocking, StateCacheDB, + BuildReceipt, EthState, EthTransactions, LoadState, RawTransactionForwarder, + SpawnBlocking, StateCacheDB, }, cache::EthStateCache, error::{EthApiError, EthResult, RpcInvalidTransactionError, SignError}, - revm_utils::{prepare_call_env, EvmOverrides, FillableTransaction}, + revm_utils::FillableTransaction, }, EthApi, EthApiSpec, }; @@ -95,24 +94,6 @@ where self.inner.gas_cap } - async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnvWithHandlerCfg, BlockEnv, BlockId)> - where - Self: LoadState + LoadPendingBlock, - { - if at.is_pending() { - let PendingBlockEnv { cfg, block_env, origin } = self.pending_block_env_and_cfg()?; - Ok((cfg, block_env, origin.state_block_id())) - } else { - // Use cached values if there is no pending block - let block_hash = self - .provider() - .block_hash_for_id(at)? - .ok_or_else(|| EthApiError::UnknownBlockNumber)?; - let (cfg, env) = self.cache().get_evm_env(block_hash).await?; - Ok((cfg, env, block_hash.into())) - } - } - async fn block_by_id(&self, id: BlockId) -> EthResult> { self.block(id).await } @@ -329,163 +310,6 @@ where Ok(hash) } - async fn spawn_with_call_at( - &self, - request: TransactionRequest, - at: BlockId, - overrides: EvmOverrides, - f: F, - ) -> EthResult - where - Self: LoadState, - F: FnOnce(&mut StateCacheDB, EnvWithHandlerCfg) -> EthResult + Send + 'static, - R: Send + 'static, - { - let (cfg, block_env, at) = self.evm_env_at(at).await?; - let this = self.clone(); - self.inner - .blocking_task_pool - .spawn(move || { - let state = this.state_at_block_id(at)?; - let mut db = CacheDB::new(StateProviderDatabase::new(state)); - - let env = prepare_call_env( - cfg, - block_env, - request, - this.call_gas_limit(), - &mut db, - overrides, - )?; - f(&mut db, env) - }) - .await - .map_err(|_| EthApiError::InternalBlockingTaskError)? - } - - async fn transact_call_at( - &self, - request: TransactionRequest, - at: BlockId, - overrides: EvmOverrides, - ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> - where - Self: LoadState, - { - let this = self.clone(); - self.spawn_with_call_at(request, at, overrides, move |db, env| this.transact(db, env)).await - } - - async fn spawn_inspect_call_at( - &self, - request: TransactionRequest, - at: BlockId, - overrides: EvmOverrides, - inspector: I, - ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> - where - Self: LoadState, - I: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, - { - let this = self.clone(); - self.spawn_with_call_at(request, at, overrides, move |db, env| { - this.inspect(db, env, inspector) - }) - .await - } - - async fn spawn_replay_transaction(&self, hash: B256, f: F) -> EthResult> - where - Self: LoadState, - F: FnOnce(TransactionInfo, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, - R: Send + 'static, - { - let (transaction, block) = match self.transaction_and_block(hash).await? { - None => return Ok(None), - Some(res) => res, - }; - let (tx, tx_info) = transaction.split(); - - let (cfg, block_env, _) = self.evm_env_at(block.hash().into()).await?; - - // we need to get the state of the parent block because we're essentially replaying the - // block the transaction is included in - let parent_block = block.parent_hash; - let block_txs = block.into_transactions_ecrecovered(); - - let this = self.clone(); - self.spawn_with_state_at_block(parent_block.into(), move |state| { - let mut db = CacheDB::new(StateProviderDatabase::new(state)); - - // replay all transactions prior to the targeted transaction - this.replay_transactions_until( - &mut db, - cfg.clone(), - block_env.clone(), - block_txs, - tx.hash, - )?; - - let env = - EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx)); - - let (res, _) = this.transact(&mut db, env)?; - f(tx_info, res, db) - }) - .await - .map(Some) - } - - async fn spawn_trace_transaction_in_block_with_inspector( - &self, - hash: B256, - mut inspector: Insp, - f: F, - ) -> EthResult> - where - Self: LoadState, - F: FnOnce(TransactionInfo, Insp, ResultAndState, StateCacheDB) -> EthResult - + Send - + 'static, - Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, - R: Send + 'static, - { - let (transaction, block) = match self.transaction_and_block(hash).await? { - None => return Ok(None), - Some(res) => res, - }; - let (tx, tx_info) = transaction.split(); - - let (cfg, block_env, _) = self.evm_env_at(block.hash().into()).await?; - - // we need to get the state of the parent block because we're essentially replaying the - // block the transaction is included in - let parent_block = block.parent_hash; - let block_txs = block.into_transactions_ecrecovered(); - - let this = self.clone(); - self.spawn_with_state_at_block(parent_block.into(), move |state| { - let mut db = CacheDB::new(StateProviderDatabase::new(state)); - - // replay all transactions prior to the targeted transaction - this.replay_transactions_until( - &mut db, - cfg.clone(), - block_env.clone(), - block_txs, - tx.hash, - )?; - - let env = - EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx)); - - let (res, _) = this.inspect(&mut db, env, &mut inspector)?; - f(tx_info, inspector, res, db) - }) - .await - .map(Some) - } - async fn trace_block_until_with_inspector( &self, block_id: BlockId, diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index 6d07ba536ee2..ef63d22dc5aa 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -26,6 +26,8 @@ use crate::eth::{ utils::recover_raw_transaction, }; +use super::api::LoadPendingBlock; + /// `Eth` bundle implementation. pub struct EthBundle { /// All nested fields bundled together. @@ -41,7 +43,7 @@ impl EthBundle { impl EthBundle where - Eth: EthTransactions + LoadState + SpawnBlocking + 'static, + Eth: EthTransactions + LoadState + SpawnBlocking + LoadPendingBlock + 'static, { /// Simulates a bundle of transactions at the top of a given block number with the state of /// another (or the same) block. This can be used to simulate future blocks with the current diff --git a/crates/rpc/rpc/src/otterscan.rs b/crates/rpc/rpc/src/otterscan.rs index 064ff9f14ea0..7c10d0b269f6 100644 --- a/crates/rpc/rpc/src/otterscan.rs +++ b/crates/rpc/rpc/src/otterscan.rs @@ -15,7 +15,7 @@ use reth_rpc_types::{ }; use crate::{ - eth::api::{EthTransactions, LoadState}, + eth::api::{EthTransactions, LoadPendingBlock, LoadState}, result::internal_rpc_err, }; @@ -37,7 +37,7 @@ impl OtterscanApi { #[async_trait] impl OtterscanServer for OtterscanApi where - Eth: EthApiServer + EthTransactions + LoadState, + Eth: EthApiServer + EthTransactions + LoadState + LoadPendingBlock, { /// Handler for `ots_hasCode` async fn has_code(&self, address: Address, block_number: Option) -> RpcResult { diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 6a9c925621e4..8d68d6ce8d70 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -1,5 +1,5 @@ use crate::eth::{ - api::{EthTransactions, LoadState, SpawnBlocking}, + api::{EthTransactions, LoadPendingBlock, LoadState, SpawnBlocking}, error::{EthApiError, EthResult}, revm_utils::{prepare_call_env, EvmOverrides}, utils::recover_raw_transaction, @@ -72,7 +72,7 @@ impl TraceApi { impl TraceApi where Provider: BlockReader + StateProviderFactory + EvmEnvProvider + ChainSpecProvider + 'static, - Eth: EthTransactions + LoadState + SpawnBlocking + 'static, + Eth: EthTransactions + LoadState + SpawnBlocking + LoadPendingBlock + 'static, { /// Executes the given call and returns a number of possible traces for it. pub async fn trace_call(&self, trace_request: TraceCallRequest) -> EthResult { @@ -485,7 +485,7 @@ where impl TraceApiServer for TraceApi where Provider: BlockReader + StateProviderFactory + EvmEnvProvider + ChainSpecProvider + 'static, - Eth: EthTransactions + LoadState + SpawnBlocking + 'static, + Eth: EthTransactions + LoadState + SpawnBlocking + LoadPendingBlock + 'static, { /// Executes the given call and returns a number of possible traces for it. ///