From 3adc627a3500b8ecda6082ee044273cf5f69293b Mon Sep 17 00:00:00 2001 From: Roman Krasiuk Date: Wed, 18 Oct 2023 09:05:26 -0700 Subject: [PATCH] feat(rpc): enable `eth_getProof` (#5071) --- crates/consensus/beacon/src/engine/mod.rs | 2 +- crates/revm/src/processor.rs | 12 ++-- crates/rpc/rpc-builder/src/auth.rs | 6 +- crates/rpc/rpc-builder/src/eth.rs | 4 +- crates/rpc/rpc-builder/src/lib.rs | 27 ++++---- crates/rpc/rpc-builder/tests/it/http.rs | 4 +- crates/rpc/rpc-types-compat/src/lib.rs | 7 +- crates/rpc/rpc-types-compat/src/proof.rs | 27 ++++++++ crates/rpc/rpc-types/src/eth/account.rs | 4 +- .../src/{tracing_call.rs => blocking_pool.rs} | 56 ++++++++-------- crates/rpc/rpc/src/debug.rs | 12 ++-- crates/rpc/rpc/src/eth/api/mod.rs | 14 ++-- crates/rpc/rpc/src/eth/api/server.rs | 29 ++++----- crates/rpc/rpc/src/eth/api/state.rs | 64 ++++++------------- crates/rpc/rpc/src/eth/api/transactions.rs | 18 +++--- crates/rpc/rpc/src/eth/error.rs | 12 ++-- crates/rpc/rpc/src/lib.rs | 4 +- crates/rpc/rpc/src/trace.rs | 10 +-- .../src/providers/bundle_state_provider.rs | 8 +-- .../src/providers/state/historical.rs | 8 +-- .../provider/src/providers/state/latest.rs | 9 +-- .../provider/src/providers/state/macros.rs | 2 +- .../storage/provider/src/test_utils/mock.rs | 12 ++-- .../storage/provider/src/test_utils/noop.rs | 13 ++-- crates/storage/provider/src/traits/state.rs | 10 +-- examples/trace-transaction-cli/src/main.rs | 2 +- 26 files changed, 176 insertions(+), 200 deletions(-) create mode 100644 crates/rpc/rpc-types-compat/src/proof.rs rename crates/rpc/rpc/src/{tracing_call.rs => blocking_pool.rs} (73%) diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index db3f5d8fe..b972259bb 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -33,7 +33,7 @@ use reth_rpc_types::engine::{ CancunPayloadFields, ExecutionPayload, PayloadAttributes, PayloadError, PayloadStatus, PayloadStatusEnum, PayloadValidationError, }; -use reth_rpc_types_compat::payload::{try_into_block, validate_block_hash}; +use reth_rpc_types_compat::engine::payload::{try_into_block, validate_block_hash}; use reth_stages::{ControlFlow, Pipeline, PipelineError}; use reth_tasks::TaskSpawner; use std::{ diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index 157c8154b..bc2f7b6ae 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -553,7 +553,9 @@ mod tests { use reth_primitives::{ bytes, constants::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS}, - keccak256, Account, Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, MAINNET, + keccak256, + trie::AccountProof, + Account, Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, MAINNET, }; use reth_provider::{AccountReader, BlockHashReader, StateRootProvider}; use revm::{Database, TransitionState}; @@ -634,12 +636,8 @@ mod tests { Ok(self.contracts.get(&code_hash).cloned()) } - fn proof( - &self, - _address: Address, - _keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)> { - todo!() + fn proof(&self, _address: Address, _keys: &[B256]) -> RethResult { + unimplemented!("proof generation is not supported") } } diff --git a/crates/rpc/rpc-builder/src/auth.rs b/crates/rpc/rpc-builder/src/auth.rs index ee8d53ff3..6c55fac73 100644 --- a/crates/rpc/rpc-builder/src/auth.rs +++ b/crates/rpc/rpc-builder/src/auth.rs @@ -17,8 +17,8 @@ use reth_provider::{ }; use reth_rpc::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, - AuthLayer, Claims, EngineEthApi, EthApi, EthFilter, EthSubscriptionIdProvider, - JwtAuthValidator, JwtSecret, TracingCallPool, + AuthLayer, BlockingTaskPool, Claims, EngineEthApi, EthApi, EthFilter, + EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret, }; use reth_rpc_api::{servers::*, EngineApiServer}; use reth_tasks::TaskSpawner; @@ -66,7 +66,7 @@ where gas_oracle, EthConfig::default().rpc_gas_cap, Box::new(executor.clone()), - TracingCallPool::build().expect("failed to build tracing pool"), + BlockingTaskPool::build().expect("failed to build tracing pool"), ); let eth_filter = EthFilter::new( provider, diff --git a/crates/rpc/rpc-builder/src/eth.rs b/crates/rpc/rpc-builder/src/eth.rs index fc519fa68..55684ecd2 100644 --- a/crates/rpc/rpc-builder/src/eth.rs +++ b/crates/rpc/rpc-builder/src/eth.rs @@ -5,7 +5,7 @@ use reth_rpc::{ gas_oracle::GasPriceOracleConfig, RPC_DEFAULT_GAS_CAP, }, - EthApi, EthFilter, EthPubSub, TracingCallPool, + BlockingTaskPool, EthApi, EthFilter, EthPubSub, }; use serde::{Deserialize, Serialize}; @@ -21,7 +21,7 @@ pub struct EthHandlers { /// Handler for subscriptions only available for transports that support it (ws, ipc) pub pubsub: EthPubSub, /// The configured tracing call pool - pub tracing_call_pool: TracingCallPool, + pub blocking_task_pool: BlockingTaskPool, } /// Additional config values for the eth namespace diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index b8529f2c7..1395286cf 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -117,9 +117,9 @@ use reth_rpc::{ cache::{cache_new_blocks_task, EthStateCache}, gas_oracle::GasPriceOracle, }, - AdminApi, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub, EthSubscriptionIdProvider, - NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, TracingCallGuard, TracingCallPool, TxPoolApi, - Web3Api, + AdminApi, BlockingTaskGuard, BlockingTaskPool, DebugApi, EngineEthApi, EthApi, EthFilter, + EthPubSub, EthSubscriptionIdProvider, NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, + TxPoolApi, Web3Api, }; use reth_rpc_api::{servers::*, EngineApiServer}; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; @@ -719,7 +719,7 @@ pub struct RethModuleRegistry { /// Holds a clone of all the eth namespace handlers eth: Option>, /// to put trace calls behind semaphore - tracing_call_guard: TracingCallGuard, + blocking_pool_guard: BlockingTaskGuard, /// Contains the [Methods] of a module modules: HashMap, } @@ -745,7 +745,7 @@ impl eth: None, executor, modules: Default::default(), - tracing_call_guard: TracingCallGuard::new(config.eth.max_tracing_requests), + blocking_pool_guard: BlockingTaskGuard::new(config.eth.max_tracing_requests), config, events, } @@ -927,7 +927,7 @@ where filter: eth_filter, pubsub: eth_pubsub, cache: _, - tracing_call_pool: _, + blocking_task_pool: _, } = self.with_eth(|eth| eth.clone()); // Create a copy, so we can list out all the methods for rpc_ api @@ -946,7 +946,7 @@ where self.provider.clone(), eth_api.clone(), Box::new(self.executor.clone()), - self.tracing_call_guard.clone(), + self.blocking_pool_guard.clone(), ) .into_rpc() .into(), @@ -964,7 +964,7 @@ where RethRpcModule::Trace => TraceApi::new( self.provider.clone(), eth_api.clone(), - self.tracing_call_guard.clone(), + self.blocking_pool_guard.clone(), ) .into_rpc() .into(), @@ -1026,7 +1026,8 @@ where ); let executor = Box::new(self.executor.clone()); - let tracing_call_pool = TracingCallPool::build().expect("failed to build tracing pool"); + let blocking_task_pool = + BlockingTaskPool::build().expect("failed to build tracing pool"); let api = EthApi::with_spawner( self.provider.clone(), self.pool.clone(), @@ -1035,7 +1036,7 @@ where gas_oracle, self.config.eth.rpc_gas_cap, executor.clone(), - tracing_call_pool.clone(), + blocking_task_pool.clone(), ); let filter = EthFilter::new( self.provider.clone(), @@ -1053,7 +1054,7 @@ where executor, ); - let eth = EthHandlers { api, cache, filter, pubsub, tracing_call_pool }; + let eth = EthHandlers { api, cache, filter, pubsub, blocking_task_pool }; self.eth = Some(eth); } f(self.eth.as_ref().expect("exists; qed")) @@ -1071,7 +1072,7 @@ where /// Instantiates TraceApi pub fn trace_api(&mut self) -> TraceApi> { let eth = self.eth_handlers(); - TraceApi::new(self.provider.clone(), eth.api, self.tracing_call_guard.clone()) + TraceApi::new(self.provider.clone(), eth.api, self.blocking_pool_guard.clone()) } /// Instantiates OtterscanApi @@ -1087,7 +1088,7 @@ where self.provider.clone(), eth_api, Box::new(self.executor.clone()), - self.tracing_call_guard.clone(), + self.blocking_pool_guard.clone(), ) } diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index cac2c1873..45931efc4 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -114,11 +114,9 @@ where EthApiClient::submit_hashrate(client, U256::default(), B256::default()).await.unwrap(); EthApiClient::gas_price(client).await.unwrap_err(); EthApiClient::max_priority_fee_per_gas(client).await.unwrap_err(); + EthApiClient::get_proof(client, address, vec![], None).await.unwrap(); // Unimplemented - assert!(is_unimplemented( - EthApiClient::get_proof(client, address, vec![], None).await.err().unwrap() - )); assert!(is_unimplemented(EthApiClient::author(client).await.err().unwrap())); assert!(is_unimplemented(EthApiClient::is_mining(client).await.err().unwrap())); assert!(is_unimplemented(EthApiClient::get_work(client).await.err().unwrap())); diff --git a/crates/rpc/rpc-types-compat/src/lib.rs b/crates/rpc/rpc-types-compat/src/lib.rs index a2357c3fc..4f4db3cef 100644 --- a/crates/rpc/rpc-types-compat/src/lib.rs +++ b/crates/rpc/rpc-types-compat/src/lib.rs @@ -12,10 +12,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod block; -pub use block::*; -pub mod transaction; -pub use transaction::*; pub mod engine; -pub use engine::*; pub mod log; -pub use log::*; +pub mod proof; +pub mod transaction; diff --git a/crates/rpc/rpc-types-compat/src/proof.rs b/crates/rpc/rpc-types-compat/src/proof.rs new file mode 100644 index 000000000..aea17f0c7 --- /dev/null +++ b/crates/rpc/rpc-types-compat/src/proof.rs @@ -0,0 +1,27 @@ +//! Compatibility functions for rpc proof related types. + +use reth_primitives::{ + serde_helper::JsonStorageKey, + trie::{AccountProof, StorageProof}, + U64, +}; +use reth_rpc_types::{EIP1186AccountProofResponse, EIP1186StorageProof}; + +/// Creates a new rpc storage proof from a primitive storage proof type. +pub fn from_primitive_storage_proof(proof: StorageProof) -> EIP1186StorageProof { + EIP1186StorageProof { key: JsonStorageKey(proof.key), value: proof.value, proof: proof.proof } +} + +/// Creates a new rpc account proof from a primitive account proof type. +pub fn from_primitive_account_proof(proof: AccountProof) -> EIP1186AccountProofResponse { + let info = proof.info.unwrap_or_default(); + EIP1186AccountProofResponse { + address: proof.address, + balance: info.balance, + code_hash: info.get_bytecode_hash(), + nonce: U64::from(info.nonce), + storage_hash: proof.storage_root, + account_proof: proof.proof, + storage_proof: proof.storage_proofs.into_iter().map(from_primitive_storage_proof).collect(), + } +} diff --git a/crates/rpc/rpc-types/src/eth/account.rs b/crates/rpc/rpc-types/src/eth/account.rs index 3e77166a8..74d99af34 100644 --- a/crates/rpc/rpc-types/src/eth/account.rs +++ b/crates/rpc/rpc-types/src/eth/account.rs @@ -12,7 +12,7 @@ pub struct AccountInfo { /// Data structure with proof for one single storage-entry #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct StorageProof { +pub struct EIP1186StorageProof { /// Storage key. pub key: JsonStorageKey, /// Value that the key holds @@ -31,7 +31,7 @@ pub struct EIP1186AccountProofResponse { pub nonce: U64, pub storage_hash: B256, pub account_proof: Vec, - pub storage_proof: Vec, + pub storage_proof: Vec, } /// Extended account information (used by `parity_allAccountInfo`). diff --git a/crates/rpc/rpc/src/tracing_call.rs b/crates/rpc/rpc/src/blocking_pool.rs similarity index 73% rename from crates/rpc/rpc/src/tracing_call.rs rename to crates/rpc/rpc/src/blocking_pool.rs index 26956ae2d..07a2849d6 100644 --- a/crates/rpc/rpc/src/tracing_call.rs +++ b/crates/rpc/rpc/src/blocking_pool.rs @@ -13,17 +13,19 @@ use tokio::sync::{oneshot, AcquireError, OwnedSemaphorePermit, Semaphore}; /// RPC Tracing call guard semaphore. /// /// This is used to restrict the number of concurrent RPC requests to tracing methods like -/// `debug_traceTransaction` because they can consume a lot of memory and CPU. +/// `debug_traceTransaction` as well as `eth_getProof` because they can consume a lot of +/// memory and CPU. /// -/// This types serves as an entry guard for the [TracingCallPool] and is used to rate limit parallel -/// tracing calls on the pool. +/// This types serves as an entry guard for the [BlockingTaskPool] and is used to rate limit +/// parallel blocking tasks in the pool. #[derive(Clone, Debug)] -pub struct TracingCallGuard(Arc); +pub struct BlockingTaskGuard(Arc); -impl TracingCallGuard { - /// Create a new `TracingCallGuard` with the given maximum number of tracing calls in parallel. - pub fn new(max_tracing_requests: u32) -> Self { - Self(Arc::new(Semaphore::new(max_tracing_requests as usize))) +impl BlockingTaskGuard { + /// Create a new `BlockingTaskGuard` with the given maximum number of blocking tasks in + /// parallel. + pub fn new(max_blocking_tasks: u32) -> Self { + Self(Arc::new(Semaphore::new(max_blocking_tasks as usize))) } /// See also [Semaphore::acquire_owned] @@ -37,24 +39,24 @@ impl TracingCallGuard { } } -/// Used to execute tracing calls on a rayon threadpool from within a tokio runtime. +/// Used to execute blocking tasks on a rayon threadpool from within a tokio runtime. /// -/// This is a dedicated threadpool for tracing calls which are CPU bound. +/// This is a dedicated threadpool for blocking tasks which are CPU bound. /// RPC calls that perform blocking IO (disk lookups) are not executed on this pool but on the tokio /// runtime's blocking pool, which performs poorly with CPU bound tasks. Once the tokio blocking -/// pool is saturated it is converted into a queue, tracing calls could then interfere with the +/// pool is saturated it is converted into a queue, blocking tasks could then interfere with the /// queue and block other RPC calls. /// /// See also [tokio-docs] for more information. /// /// [tokio-docs]: https://docs.rs/tokio/latest/tokio/index.html#cpu-bound-tasks-and-blocking-code #[derive(Clone, Debug)] -pub struct TracingCallPool { +pub struct BlockingTaskPool { pool: Arc, } -impl TracingCallPool { - /// Create a new `TracingCallPool` with the given threadpool. +impl BlockingTaskPool { + /// Create a new `BlockingTaskPool` with the given threadpool. pub fn new(pool: rayon::ThreadPool) -> Self { Self { pool: Arc::new(pool) } } @@ -83,7 +85,7 @@ impl TracingCallPool { /// function's return value. /// /// If the function panics, the future will resolve to an error. - pub fn spawn(&self, func: F) -> TracingCallHandle + pub fn spawn(&self, func: F) -> BlockingTaskHandle where F: FnOnce() -> R + Send + 'static, R: Send + 'static, @@ -94,7 +96,7 @@ impl TracingCallPool { let _result = tx.send(catch_unwind(AssertUnwindSafe(func))); }); - TracingCallHandle { rx } + BlockingTaskHandle { rx } } /// Asynchronous wrapper around Rayon's @@ -104,7 +106,7 @@ impl TracingCallPool { /// function's return value. /// /// If the function panics, the future will resolve to an error. - pub fn spawn_fifo(&self, func: F) -> TracingCallHandle + pub fn spawn_fifo(&self, func: F) -> BlockingTaskHandle where F: FnOnce() -> R + Send + 'static, R: Send + 'static, @@ -115,11 +117,11 @@ impl TracingCallPool { let _result = tx.send(catch_unwind(AssertUnwindSafe(func))); }); - TracingCallHandle { rx } + BlockingTaskHandle { rx } } } -/// Async handle for a blocking tracing task running in a Rayon thread pool. +/// Async handle for a blocking task running in a Rayon thread pool. /// /// ## Panics /// @@ -127,18 +129,18 @@ impl TracingCallPool { #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] #[pin_project::pin_project] -pub struct TracingCallHandle { +pub struct BlockingTaskHandle { #[pin] pub(crate) rx: oneshot::Receiver>, } -impl Future for TracingCallHandle { +impl Future for BlockingTaskHandle { type Output = thread::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match ready!(self.project().rx.poll(cx)) { Ok(res) => Poll::Ready(res), - Err(_) => Poll::Ready(Err(Box::::default())), + Err(_) => Poll::Ready(Err(Box::::default())), } } } @@ -149,23 +151,23 @@ impl Future for TracingCallHandle { #[derive(Debug, Default, thiserror::Error)] #[error("Tokio channel dropped while awaiting result")] #[non_exhaustive] -pub struct TokioTracingCallError; +pub struct TokioBlockingTaskError; #[cfg(test)] mod tests { use super::*; #[tokio::test] - async fn tracing_pool() { - let pool = TracingCallPool::build().unwrap(); + async fn blocking_pool() { + let pool = BlockingTaskPool::build().unwrap(); let res = pool.spawn(move || 5); let res = res.await.unwrap(); assert_eq!(res, 5); } #[tokio::test] - async fn tracing_pool_panic() { - let pool = TracingCallPool::build().unwrap(); + async fn blocking_pool_panic() { + let pool = BlockingTaskPool::build().unwrap(); let res = pool.spawn(move || -> i32 { panic!(); }); diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index f54510caf..ebe662d40 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -8,7 +8,7 @@ use crate::{ EthTransactions, TransactionSource, }, result::{internal_rpc_err, ToRpcResult}, - EthApiSpec, TracingCallGuard, + BlockingTaskGuard, EthApiSpec, }; use alloy_rlp::{Decodable, Encodable}; use async_trait::async_trait; @@ -61,10 +61,10 @@ impl DebugApi { provider: Provider, eth: Eth, task_spawner: Box, - tracing_call_guard: TracingCallGuard, + blocking_task_guard: BlockingTaskGuard, ) -> Self { let inner = - Arc::new(DebugApiInner { provider, eth_api: eth, task_spawner, tracing_call_guard }); + Arc::new(DebugApiInner { provider, eth_api: eth, task_spawner, blocking_task_guard }); Self { inner } } } @@ -78,7 +78,7 @@ where { /// Acquires a permit to execute a tracing call. async fn acquire_trace_permit(&self) -> Result { - self.inner.tracing_call_guard.clone().acquire_owned().await + self.inner.blocking_task_guard.clone().acquire_owned().await } /// Trace the entire block asynchronously @@ -1010,8 +1010,8 @@ struct DebugApiInner { provider: Provider, /// The implementation of `eth` API eth_api: Eth, - // restrict the number of concurrent calls to tracing calls - tracing_call_guard: TracingCallGuard, + // restrict the number of concurrent calls to blocking calls + blocking_task_guard: BlockingTaskGuard, /// The type that can spawn tasks which would otherwise block. task_spawner: Box, } diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index 635f3016b..9b6d00000 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -39,7 +39,7 @@ mod sign; mod state; mod transactions; -use crate::TracingCallPool; +use crate::BlockingTaskPool; pub use transactions::{EthTransactions, TransactionSource}; /// `Eth` API trait. @@ -91,7 +91,7 @@ where eth_cache: EthStateCache, gas_oracle: GasPriceOracle, gas_cap: impl Into, - tracing_call_pool: TracingCallPool, + blocking_task_pool: BlockingTaskPool, ) -> Self { Self::with_spawner( provider, @@ -101,7 +101,7 @@ where gas_oracle, gas_cap.into().into(), Box::::default(), - tracing_call_pool, + blocking_task_pool, ) } @@ -115,7 +115,7 @@ where gas_oracle: GasPriceOracle, gas_cap: u64, task_spawner: Box, - tracing_call_pool: TracingCallPool, + blocking_task_pool: BlockingTaskPool, ) -> Self { // get the block number of the latest block let latest_block = provider @@ -136,7 +136,7 @@ where starting_block: U256::from(latest_block), task_spawner, pending_block: Default::default(), - tracing_call_pool, + blocking_task_pool, }; Self { inner: Arc::new(inner) } } @@ -436,6 +436,6 @@ struct EthApiInner { task_spawner: Box, /// Cached pending block if any pending_block: Mutex>, - /// A pool dedicated to tracing calls - tracing_call_pool: TracingCallPool, + /// A pool dedicated to blocking tasks. + blocking_task_pool: BlockingTaskPool, } diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index d4d867619..9a148a861 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -5,6 +5,7 @@ use super::EthApiSpec; use crate::{ eth::{ api::{EthApi, EthTransactions}, + error::EthApiError, revm_utils::EvmOverrides, }, result::{internal_rpc_err, ToRpcResult}, @@ -368,21 +369,19 @@ where /// Handler for: `eth_getProof` async fn get_proof( &self, - _address: Address, - _keys: Vec, - _block_number: Option, + address: Address, + keys: Vec, + block_number: Option, ) -> Result { - // TODO: uncomment when implemented - // trace!(target: "rpc::eth", ?address, ?keys, ?block_number, "Serving eth_getProof"); - // let res = EthApi::get_proof(self, address, keys, block_number); + trace!(target: "rpc::eth", ?address, ?keys, ?block_number, "Serving eth_getProof"); + let res = EthApi::get_proof(self, address, keys, block_number).await; - // Ok(res.map_err(|e| match e { - // EthApiError::InvalidBlockRange => { - // internal_rpc_err("eth_getProof is unimplemented for historical blocks") - // } - // _ => e.into(), - // })?) - Err(internal_rpc_err("unimplemented")) + Ok(res.map_err(|e| match e { + EthApiError::InvalidBlockRange => { + internal_rpc_err("eth_getProof is unimplemented for historical blocks") + } + _ => e.into(), + })?) } } @@ -390,7 +389,7 @@ where mod tests { use crate::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, - EthApi, TracingCallPool, + BlockingTaskPool, EthApi, }; use jsonrpsee::types::error::INVALID_PARAMS_CODE; use reth_interfaces::test_utils::{generators, generators::Rng}; @@ -428,7 +427,7 @@ mod tests { cache.clone(), GasPriceOracle::new(provider, Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, - TracingCallPool::build().expect("failed to build tracing pool"), + BlockingTaskPool::build().expect("failed to build tracing pool"), ) } diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index 2205c64b3..4cf4a69a6 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -5,14 +5,13 @@ use crate::{ EthApi, }; use reth_primitives::{ - serde_helper::JsonStorageKey, Address, BlockId, BlockNumberOrTag, Bytes, B256, KECCAK_EMPTY, - U256, U64, + serde_helper::JsonStorageKey, Address, BlockId, BlockNumberOrTag, Bytes, B256, U256, }; use reth_provider::{ - AccountReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProvider, - StateProviderFactory, + BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProvider, StateProviderFactory, }; -use reth_rpc_types::{EIP1186AccountProofResponse, StorageProof}; +use reth_rpc_types::EIP1186AccountProofResponse; +use reth_rpc_types_compat::proof::from_primitive_account_proof; use reth_transaction_pool::{PoolTransaction, TransactionPool}; impl EthApi @@ -84,8 +83,7 @@ where Ok(B256::new(value.to_be_bytes())) } - #[allow(unused)] - pub(crate) fn get_proof( + pub(crate) async fn get_proof( &self, address: Address, keys: Vec, @@ -97,7 +95,7 @@ where // if we are trying to create a proof for the latest block, but have a BlockId as input // that is not BlockNumberOrTag::Latest, then we need to figure out whether or not the // BlockId corresponds to the latest block - let is_blockid_latest = match block_id { + let is_latest_block = match block_id { BlockId::Number(BlockNumberOrTag::Number(num)) => num == chain_info.best_number, BlockId::Hash(hash) => hash == chain_info.best_hash.into(), BlockId::Number(BlockNumberOrTag::Latest) => true, @@ -105,43 +103,21 @@ where }; // TODO: remove when HistoricalStateProviderRef::proof is implemented - if !is_blockid_latest { + if !is_latest_block { return Err(EthApiError::InvalidBlockRange) } - let state = self.state_at_block_id(block_id)?; - - let hash_keys = keys.iter().map(|key| key.0).collect::>(); - let (account_proof, storage_hash, stg_proofs) = state.proof(address, &hash_keys)?; - - let storage_proof = keys - .into_iter() - .zip(stg_proofs) - .map(|(key, proof)| { - state.storage(address, key.0).map(|op| StorageProof { - key, - value: op.unwrap_or_default(), - proof, - }) + let this = self.clone(); + self.inner + .blocking_task_pool + .spawn(move || { + let state = this.state_at_block_id(block_id)?; + let storage_keys = keys.iter().map(|key| key.0).collect::>(); + let proof = state.proof(address, &storage_keys)?; + Ok(from_primitive_account_proof(proof)) }) - .collect::>()?; - - let mut proof = EIP1186AccountProofResponse { - address, - code_hash: KECCAK_EMPTY, - account_proof, - storage_hash, - storage_proof, - ..Default::default() - }; - - if let Some(account) = state.basic_account(proof.address)? { - proof.balance = account.balance; - proof.nonce = U64::from(account.nonce); - proof.code_hash = account.get_bytecode_hash(); - } - - Ok(proof) + .await + .map_err(|_| EthApiError::InternalBlockingTaskError)? } } @@ -150,7 +126,7 @@ mod tests { use super::*; use crate::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, - TracingCallPool, + BlockingTaskPool, }; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, StorageKey, StorageValue}; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider, NoopProvider}; @@ -170,7 +146,7 @@ mod tests { cache.clone(), GasPriceOracle::new(NoopProvider::default(), Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, - TracingCallPool::build().expect("failed to build tracing pool"), + BlockingTaskPool::build().expect("failed to build tracing pool"), ); let address = Address::random(); let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap(); @@ -192,7 +168,7 @@ mod tests { cache.clone(), GasPriceOracle::new(mock_provider, Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, - TracingCallPool::build().expect("failed to build tracing pool"), + BlockingTaskPool::build().expect("failed to build tracing pool"), ); let storage_key: U256 = storage_key.into(); diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 815d716c9..a15354ec3 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -32,7 +32,7 @@ use reth_rpc_types::{ BlockError, CallRequest, Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest, TypedTransactionRequest, }; -use reth_rpc_types_compat::from_recovered_with_block_context; +use reth_rpc_types_compat::transaction::from_recovered_with_block_context; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; use revm::{ db::CacheDB, @@ -50,7 +50,7 @@ pub(crate) type StateCacheDB<'r> = CacheDB( &self, hash: B256, @@ -325,13 +325,13 @@ where { let this = self.clone(); self.inner - .tracing_call_pool + .blocking_task_pool .spawn(move || { let state = this.state_at(at)?; f(state) }) .await - .map_err(|_| EthApiError::InternalTracingError)? + .map_err(|_| EthApiError::InternalBlockingTaskError)? } async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)> { @@ -594,7 +594,7 @@ where let (cfg, block_env, at) = self.evm_env_at(at).await?; let this = self.clone(); self.inner - .tracing_call_pool + .blocking_task_pool .spawn(move || { let state = this.state_at(at)?; let mut db = CacheDB::new(StateProviderDatabase::new(state)); @@ -610,7 +610,7 @@ where f(db, env) }) .await - .map_err(|_| EthApiError::InternalTracingError)? + .map_err(|_| EthApiError::InternalBlockingTaskError)? } async fn transact_call_at( @@ -1094,7 +1094,7 @@ mod tests { use super::*; use crate::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, - EthApi, TracingCallPool, + BlockingTaskPool, EthApi, }; use reth_network_api::noop::NoopNetwork; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, hex_literal::hex, Bytes}; @@ -1116,7 +1116,7 @@ mod tests { cache.clone(), GasPriceOracle::new(noop_provider, Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, - TracingCallPool::build().expect("failed to build tracing pool"), + BlockingTaskPool::build().expect("failed to build tracing pool"), ); // https://etherscan.io/tx/0xa694b71e6c128a2ed8e2e0f6770bddbe52e3bb8f10e8472f9a79ab81497a8b5d diff --git a/crates/rpc/rpc/src/eth/error.rs b/crates/rpc/rpc/src/eth/error.rs index d8eb1e0eb..998469ecb 100644 --- a/crates/rpc/rpc/src/eth/error.rs +++ b/crates/rpc/rpc/src/eth/error.rs @@ -83,12 +83,12 @@ pub enum EthApiError { /// Percentile array is invalid #[error("invalid reward percentiles")] InvalidRewardPercentiles, - /// Error thrown when a spawned tracing task failed to deliver an anticipated response. + /// Error thrown when a spawned blocking task failed to deliver an anticipated response. /// - /// This only happens if the tracing task panics and is aborted before it can return a response - /// back to the request handler. - #[error("internal error while tracing")] - InternalTracingError, + /// This only happens if the blocking task panics and is aborted before it can return a + /// response back to the request handler. + #[error("internal blocking task error")] + InternalBlockingTaskError, /// Error thrown when a spawned blocking task failed to deliver an anticipated response. #[error("internal eth error")] InternalEthError, @@ -133,7 +133,7 @@ impl From for ErrorObject<'static> { err @ EthApiError::ExecutionTimedOut(_) => { rpc_error_with_code(CALL_EXECUTION_FAILED_CODE, err.to_string()) } - err @ EthApiError::InternalTracingError => internal_rpc_err(err.to_string()), + err @ EthApiError::InternalBlockingTaskError => internal_rpc_err(err.to_string()), err @ EthApiError::InternalEthError => internal_rpc_err(err.to_string()), err @ EthApiError::CallInputError(_) => invalid_params_rpc_err(err.to_string()), } diff --git a/crates/rpc/rpc/src/lib.rs b/crates/rpc/rpc/src/lib.rs index 1c75b0f30..68259610a 100644 --- a/crates/rpc/rpc/src/lib.rs +++ b/crates/rpc/rpc/src/lib.rs @@ -36,11 +36,11 @@ mod otterscan; mod reth; mod rpc; mod trace; -pub mod tracing_call; mod txpool; mod web3; pub use admin::AdminApi; +pub use blocking_pool::{BlockingTaskGuard, BlockingTaskPool}; pub use debug::DebugApi; pub use engine::{EngineApi, EngineEthApi}; pub use eth::{EthApi, EthApiSpec, EthFilter, EthPubSub, EthSubscriptionIdProvider}; @@ -50,8 +50,8 @@ pub use otterscan::OtterscanApi; pub use reth::RethApi; pub use rpc::RPCApi; pub use trace::TraceApi; -pub use tracing_call::{TracingCallGuard, TracingCallPool}; pub use txpool::TxPoolApi; pub use web3::Web3Api; +pub mod blocking_pool; pub mod result; diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 52bbe49b5..121625dc8 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -5,7 +5,7 @@ use crate::{ utils::recover_raw_transaction, EthTransactions, }, - TracingCallGuard, + BlockingTaskGuard, }; use async_trait::async_trait; use jsonrpsee::core::RpcResult as Result; @@ -44,8 +44,8 @@ impl TraceApi { } /// Create a new instance of the [TraceApi] - pub fn new(provider: Provider, eth_api: Eth, tracing_call_guard: TracingCallGuard) -> Self { - let inner = Arc::new(TraceApiInner { provider, eth_api, tracing_call_guard }); + pub fn new(provider: Provider, eth_api: Eth, blocking_task_guard: BlockingTaskGuard) -> Self { + let inner = Arc::new(TraceApiInner { provider, eth_api, blocking_task_guard }); Self { inner } } @@ -53,7 +53,7 @@ impl TraceApi { async fn acquire_trace_permit( &self, ) -> std::result::Result { - self.inner.tracing_call_guard.clone().acquire_owned().await + self.inner.blocking_task_guard.clone().acquire_owned().await } } @@ -557,7 +557,7 @@ struct TraceApiInner { /// Access to commonly used code of the `eth` namespace eth_api: Eth, // restrict the number of concurrent calls to `trace_*` - tracing_call_guard: TracingCallGuard, + blocking_task_guard: BlockingTaskGuard, } /// Returns the [TracingInspectorConfig] depending on the enabled [TraceType]s diff --git a/crates/storage/provider/src/providers/bundle_state_provider.rs b/crates/storage/provider/src/providers/bundle_state_provider.rs index 0f6e049e7..8c83ebe15 100644 --- a/crates/storage/provider/src/providers/bundle_state_provider.rs +++ b/crates/storage/provider/src/providers/bundle_state_provider.rs @@ -3,7 +3,7 @@ use crate::{ StateProvider, StateRootProvider, }; use reth_interfaces::{provider::ProviderError, RethResult}; -use reth_primitives::{Account, Address, BlockNumber, Bytecode, Bytes, B256}; +use reth_primitives::{trie::AccountProof, Account, Address, BlockNumber, Bytecode, B256}; /// A state provider that either resolves to data in a wrapped [`crate::BundleStateWithReceipts`], /// or an underlying state provider. @@ -92,11 +92,7 @@ impl StateProvider self.state_provider.bytecode_by_hash(code_hash) } - fn proof( - &self, - _address: Address, - _keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)> { + fn proof(&self, _address: Address, _keys: &[B256]) -> RethResult { Err(ProviderError::StateRootNotAvailableForHistoricalBlock.into()) } } diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 72dffaed4..d91d2f589 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -12,7 +12,7 @@ use reth_db::{ }; use reth_interfaces::RethResult; use reth_primitives::{ - Account, Address, BlockNumber, Bytecode, Bytes, StorageKey, StorageValue, B256, + trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, StorageValue, B256, }; /// State provider for a given block number which takes a tx reference. @@ -240,11 +240,7 @@ impl<'b, TX: DbTx> StateProvider for HistoricalStateProviderRef<'b, TX> { } /// Get account and storage proofs. - fn proof( - &self, - _address: Address, - _keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)> { + fn proof(&self, _address: Address, _keys: &[B256]) -> RethResult { Err(ProviderError::StateRootNotAvailableForHistoricalBlock.into()) } } diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index 580229d90..be70c1945 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -9,7 +9,8 @@ use reth_db::{ }; use reth_interfaces::{provider::ProviderError, RethError, RethResult}; use reth_primitives::{ - keccak256, Account, Address, BlockNumber, Bytecode, Bytes, StorageKey, StorageValue, B256, + keccak256, trie::AccountProof, Account, Address, BlockNumber, Bytecode, StorageKey, + StorageValue, B256, }; /// State provider over latest state that takes tx reference. @@ -84,11 +85,7 @@ impl<'b, TX: DbTx> StateProvider for LatestStateProviderRef<'b, TX> { self.db.get::(code_hash).map_err(Into::into) } - fn proof( - &self, - address: Address, - _keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)> { + fn proof(&self, address: Address, _keys: &[B256]) -> RethResult { let _hashed_address = keccak256(address); let _root = self .db diff --git a/crates/storage/provider/src/providers/state/macros.rs b/crates/storage/provider/src/providers/state/macros.rs index 5313ebf98..d14c7235f 100644 --- a/crates/storage/provider/src/providers/state/macros.rs +++ b/crates/storage/provider/src/providers/state/macros.rs @@ -42,7 +42,7 @@ macro_rules! delegate_provider_impls { } StateProvider $(where [$($generics)*])?{ fn storage(&self, account: reth_primitives::Address, storage_key: reth_primitives::StorageKey) -> reth_interfaces::RethResult>; - fn proof(&self, address: reth_primitives::Address, keys: &[reth_primitives::B256]) -> reth_interfaces::RethResult<(Vec, reth_primitives::B256, Vec>)>; + fn proof(&self, address: reth_primitives::Address, keys: &[reth_primitives::B256]) -> reth_interfaces::RethResult; fn bytecode_by_hash(&self, code_hash: reth_primitives::B256) -> reth_interfaces::RethResult>; } ); diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index 3c44f1aa0..032048c62 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -10,9 +10,9 @@ use parking_lot::Mutex; use reth_db::models::StoredBlockBodyIndices; use reth_interfaces::{provider::ProviderError, RethResult}; use reth_primitives::{ - keccak256, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, - BlockWithSenders, Bytecode, Bytes, ChainInfo, ChainSpec, Header, Receipt, SealedBlock, - SealedHeader, StorageKey, StorageValue, TransactionMeta, TransactionSigned, + keccak256, trie::AccountProof, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, + BlockNumber, BlockWithSenders, Bytecode, Bytes, ChainInfo, ChainSpec, Header, Receipt, + SealedBlock, SealedHeader, StorageKey, StorageValue, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, B256, U256, }; use revm::primitives::{BlockEnv, CfgEnv}; @@ -507,11 +507,7 @@ impl StateProvider for MockEthProvider { })) } - fn proof( - &self, - _address: Address, - _keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)> { + fn proof(&self, _address: Address, _keys: &[B256]) -> RethResult { todo!() } } diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index 666329368..b4e2c6b85 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -10,10 +10,11 @@ use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_interfaces::RethResult; use reth_primitives::{ stage::{StageCheckpoint, StageId}, - Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode, Bytes, + trie::AccountProof, + Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode, ChainInfo, ChainSpec, Header, PruneCheckpoint, PruneSegment, Receipt, SealedBlock, SealedHeader, StorageKey, StorageValue, TransactionMeta, TransactionSigned, - TransactionSignedNoHash, TxHash, TxNumber, B256, KECCAK_EMPTY, MAINNET, U256, + TransactionSignedNoHash, TxHash, TxNumber, B256, MAINNET, U256, }; use revm::primitives::{BlockEnv, CfgEnv}; use std::{ @@ -278,12 +279,8 @@ impl StateProvider for NoopProvider { Ok(None) } - fn proof( - &self, - _address: Address, - _keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)> { - Ok((vec![], KECCAK_EMPTY, vec![])) + fn proof(&self, _address: Address, _keys: &[B256]) -> RethResult { + Ok(AccountProof::default()) } } diff --git a/crates/storage/provider/src/traits/state.rs b/crates/storage/provider/src/traits/state.rs index 9d1b5a0f6..9204ff86e 100644 --- a/crates/storage/provider/src/traits/state.rs +++ b/crates/storage/provider/src/traits/state.rs @@ -3,8 +3,8 @@ use crate::{BlockHashReader, BlockIdReader, BundleStateWithReceipts}; use auto_impl::auto_impl; use reth_interfaces::{provider::ProviderError, RethResult}; use reth_primitives::{ - Address, BlockHash, BlockId, BlockNumHash, BlockNumber, BlockNumberOrTag, Bytecode, Bytes, - StorageKey, StorageValue, B256, KECCAK_EMPTY, U256, + trie::AccountProof, Address, BlockHash, BlockId, BlockNumHash, BlockNumber, BlockNumberOrTag, + Bytecode, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256, }; /// Type alias of boxed [StateProvider]. @@ -24,11 +24,7 @@ pub trait StateProvider: BlockHashReader + AccountReader + StateRootProvider + S fn bytecode_by_hash(&self, code_hash: B256) -> RethResult>; /// Get account and storage proofs. - fn proof( - &self, - address: Address, - keys: &[B256], - ) -> RethResult<(Vec, B256, Vec>)>; + fn proof(&self, address: Address, keys: &[B256]) -> RethResult; /// Get account code by its address. /// diff --git a/examples/trace-transaction-cli/src/main.rs b/examples/trace-transaction-cli/src/main.rs index df4c71337..b79ec009c 100644 --- a/examples/trace-transaction-cli/src/main.rs +++ b/examples/trace-transaction-cli/src/main.rs @@ -17,7 +17,7 @@ use reth::{ Cli, }, primitives::{Address, IntoRecoveredTransaction}, - rpc::{compat::transaction_to_call_request, types::trace::parity::TraceType}, + rpc::{compat::transaction::transaction_to_call_request, types::trace::parity::TraceType}, tasks::TaskSpawner, transaction_pool::TransactionPool, };