From 00712d642e975e256319c136445f49e0c393c0d1 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 31 Mar 2023 20:19:56 +0200 Subject: [PATCH] feat(rpc): impl traceCall (#2029) --- .../src/tracing/builder/parity.rs | 19 +++++ .../revm/revm-inspectors/src/tracing/mod.rs | 32 ++----- crates/rpc/rpc/src/eth/api/call.rs | 22 +---- crates/rpc/rpc/src/eth/api/transactions.rs | 85 ++++++++++++++++++- crates/rpc/rpc/src/trace.rs | 34 ++++---- 5 files changed, 126 insertions(+), 66 deletions(-) diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index 26b7df6660..c17d49a415 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs @@ -1,5 +1,6 @@ use crate::tracing::{types::CallTraceNode, TracingInspectorConfig}; use reth_rpc_types::{trace::parity::*, TransactionInfo}; +use revm::primitives::ExecutionResult; use std::collections::HashSet; /// A type for creating parity style traces @@ -82,6 +83,24 @@ impl ParityTraceBuilder { self.into_localized_transaction_traces_iter(info).collect() } + /// Consumes the inspector and returns the trace results according to the configured trace + /// types. + pub fn into_trace_results( + self, + res: ExecutionResult, + trace_types: &HashSet, + ) -> TraceResults { + let output = match res { + ExecutionResult::Success { output, .. } => output.into_data(), + ExecutionResult::Revert { output, .. } => output, + ExecutionResult::Halt { .. } => Default::default(), + }; + + let (trace, vm_trace, state_diff) = self.into_trace_type_traces(trace_types); + + TraceResults { output: output.into(), trace, vm_trace, state_diff } + } + /// Returns the tracing types that are configured in the set pub fn into_trace_type_traces( self, diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index f78215fc61..0401ec47f4 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -1,9 +1,6 @@ -use crate::{ - stack::MaybeOwnedInspector, - tracing::{ - types::{CallKind, LogCallOrder, RawLog}, - utils::{gas_used, get_create_address}, - }, +use crate::tracing::{ + types::{CallKind, LogCallOrder, RawLog}, + utils::{gas_used, get_create_address}, }; pub use arena::CallTraceArena; use reth_primitives::{bytes::Bytes, Address, H256, U256}; @@ -46,10 +43,7 @@ pub struct TracingInspector { /// Tracks the return value of the last call last_call_return_data: Option, /// The gas inspector used to track remaining gas. - /// - /// This is either owned by this inspector directly or part of a stack of inspectors, in which - /// case all delegated functions are no-ops. - gas_inspector: MaybeOwnedInspector, + gas_inspector: GasInspector, } // === impl TracingInspector === @@ -77,20 +71,6 @@ impl TracingInspector { GethTraceBuilder::new(self.traces.arena, self.config) } - /// Configures a [GasInspector] - /// - /// If this [TracingInspector] is part of a stack [InspectorStack](crate::stack::InspectorStack) - /// which already uses a [GasInspector], it can be reused as [MaybeOwnedInspector::Stacked] in - /// which case the `gas_inspector`'s usage will be a no-op within the context of this - /// [TracingInspector]. - pub fn with_stacked_gas_inspector( - mut self, - gas_inspector: MaybeOwnedInspector, - ) -> Self { - self.gas_inspector = gas_inspector; - self - } - /// Returns the last trace [CallTrace] index from the stack. /// /// # Panics @@ -201,7 +181,7 @@ impl TracingInspector { stack, memory, memory_size: interp.memory.len(), - gas: self.gas_inspector.as_ref().gas_remaining(), + gas: self.gas_inspector.gas_remaining(), gas_refund_counter: interp.gas.refunded() as u64, // fields will be populated end of call @@ -251,7 +231,7 @@ impl TracingInspector { }; } - step.gas_cost = step.gas - self.gas_inspector.as_ref().gas_remaining(); + step.gas_cost = step.gas - self.gas_inspector.gas_remaining(); } // set the status diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index b1644c55d5..df54c1461a 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -5,7 +5,7 @@ use crate::{ error::{EthApiError, EthResult, InvalidTransactionError, RevertError}, revm_utils::{ build_call_evm_env, cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect, - prepare_call_env, transact, + transact, }, EthTransactions, }, @@ -18,11 +18,11 @@ use reth_revm::{ access_list::AccessListInspector, database::{State, SubState}, }; -use reth_rpc_types::{state::StateOverride, CallRequest}; +use reth_rpc_types::CallRequest; use reth_transaction_pool::TransactionPool; use revm::{ db::{CacheDB, DatabaseRef}, - primitives::{BlockEnv, CfgEnv, Env, ExecutionResult, Halt, ResultAndState, TransactTo}, + primitives::{BlockEnv, CfgEnv, Env, ExecutionResult, Halt, TransactTo}, }; use tracing::trace; @@ -36,22 +36,6 @@ where Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: Send + Sync + 'static, { - /// Executes the call request at the given [BlockId] - pub(crate) async fn transact_call_at( - &self, - request: CallRequest, - at: BlockId, - state_overrides: Option, - ) -> EthResult<(ResultAndState, Env)> { - let (cfg, block_env, at) = self.evm_env_at(at).await?; - let state = self.state_at(at)?; - let mut db = SubState::new(State::new(state)); - - let env = prepare_call_env(cfg, block_env, request, &mut db, state_overrides)?; - trace!(target: "rpc::eth::call", ?env, "Executing call"); - transact(&mut db, env) - } - /// Estimate gas needed for execution of the `request` at the [BlockId]. pub(crate) async fn estimate_gas_at( &self, diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index fd80a1b7d6..6765d4fd17 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -2,6 +2,7 @@ use crate::{ eth::{ error::{EthApiError, EthResult, SignError}, + revm_utils::{inspect, prepare_call_env, transact}, utils::recover_raw_transaction, }, EthApi, @@ -15,13 +16,18 @@ use reth_primitives::{ TxLegacy, H256, U128, U256, U64, }; use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory}; +use reth_revm::database::{State, SubState}; use reth_rpc_types::{ - Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest, - TypedTransactionRequest, + state::StateOverride, CallRequest, Index, Log, Transaction, TransactionInfo, + TransactionReceipt, TransactionRequest, TypedTransactionRequest, }; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; -use revm::primitives::{BlockEnv, CfgEnv}; -use revm_primitives::utilities::create_address; +use revm::{ + db::CacheDB, + primitives::{BlockEnv, CfgEnv}, + Inspector, +}; +use revm_primitives::{utilities::create_address, Env, ResultAndState}; /// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace #[async_trait::async_trait] @@ -67,6 +73,37 @@ pub trait EthTransactions: Send + Sync { /// Signs transaction with a matching signer, if any and submits the transaction to the pool. /// Returns the hash of the signed transaction. async fn send_transaction(&self, request: TransactionRequest) -> EthResult; + + /// Prepares the state and env for the given [CallRequest] at the given [BlockId] and executes + /// the closure. + async fn with_call_at( + &self, + request: CallRequest, + at: BlockId, + state_overrides: Option, + f: F, + ) -> EthResult + where + F: for<'r> FnOnce(CacheDB>>, Env) -> EthResult + Send; + + /// Executes the call request at the given [BlockId]. + async fn transact_call_at( + &self, + request: CallRequest, + at: BlockId, + state_overrides: Option, + ) -> EthResult<(ResultAndState, Env)>; + + /// Executes the call request at the given [BlockId] + async fn inspect_call_at( + &self, + request: CallRequest, + at: BlockId, + state_overrides: Option, + inspector: I, + ) -> EthResult<(ResultAndState, Env)> + where + I: for<'r> Inspector>>> + Send; } #[async_trait] @@ -212,6 +249,46 @@ where Ok(hash) } + + async fn with_call_at( + &self, + request: CallRequest, + at: BlockId, + state_overrides: Option, + f: F, + ) -> EthResult + where + F: for<'r> FnOnce(CacheDB>>, Env) -> EthResult + Send, + { + let (cfg, block_env, at) = self.evm_env_at(at).await?; + let state = self.state_at(at)?; + let mut db = SubState::new(State::new(state)); + + let env = prepare_call_env(cfg, block_env, request, &mut db, state_overrides)?; + f(db, env) + } + + async fn transact_call_at( + &self, + request: CallRequest, + at: BlockId, + state_overrides: Option, + ) -> EthResult<(ResultAndState, Env)> { + self.with_call_at(request, at, state_overrides, |mut db, env| transact(&mut db, env)).await + } + + async fn inspect_call_at( + &self, + request: CallRequest, + at: BlockId, + state_overrides: Option, + inspector: I, + ) -> EthResult<(ResultAndState, Env)> + where + I: for<'r> Inspector>>> + Send, + { + self.with_call_at(request, at, state_overrides, |db, env| inspect(db, env, inspector)).await + } } // === impl EthApi === diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 4a7d9dbc62..a0023f95bd 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -20,7 +20,7 @@ use reth_rpc_types::{ trace::{filter::TraceFilter, parity::*}, CallRequest, Index, }; -use revm::primitives::{Env, ExecutionResult, ResultAndState}; +use revm::primitives::{Env, ResultAndState}; use std::collections::HashSet; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; @@ -92,11 +92,20 @@ where /// Executes the given call and returns a number of possible traces for it. pub async fn trace_call( &self, - _call: CallRequest, - _trace_types: HashSet, - _block_id: Option, + call: CallRequest, + trace_types: HashSet, + block_id: Option, ) -> EthResult { - todo!() + let _permit = self.acquire_trace_permit().await; + let at = block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)); + let config = tracing_config(&trace_types); + let mut inspector = TracingInspector::new(config); + + let (res, _) = self.eth_api.inspect_call_at(call, at, None, &mut inspector).await?; + + let trace_res = + inspector.into_parity_builder().into_trace_results(res.result, &trace_types); + Ok(trace_res) } /// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces. @@ -119,18 +128,9 @@ where let config = tracing_config(&trace_types); self.trace_at(env, config, at, |inspector, res| { - let output = match res.result { - ExecutionResult::Success { output, .. } => output.into_data(), - ExecutionResult::Revert { output, .. } => output, - ExecutionResult::Halt { .. } => Default::default(), - }; - - let (trace, vm_trace, state_diff) = - inspector.into_parity_builder().into_trace_type_traces(&trace_types); - - let res = TraceResults { output: output.into(), trace, vm_trace, state_diff }; - - Ok(res) + let trace_res = + inspector.into_parity_builder().into_trace_results(res.result, &trace_types); + Ok(trace_res) }) }