diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index fc62ef5b17..26b7df6660 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 std::collections::HashSet; /// A type for creating parity style traces #[derive(Clone, Debug)] @@ -81,6 +82,15 @@ impl ParityTraceBuilder { self.into_localized_transaction_traces_iter(info).collect() } + /// Returns the tracing types that are configured in the set + pub fn into_trace_type_traces( + self, + _trace_types: &HashSet, + ) -> (Option>, Option, Option) { + // TODO(mattsse): impl conversion + (None, None, None) + } + /// Returns an iterator over all recorded traces for `trace_transaction` pub fn into_transaction_traces_iter(self) -> impl Iterator { let trace_addresses = self.trace_addresses(); diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index fd91fb9c0b..2659660faa 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -160,21 +160,12 @@ where count: None, }; - assert!(is_unimplemented( - TraceApiClient::trace_call(client, CallRequest::default(), HashSet::default(), None) - .await - .err() - .unwrap() - )); + TraceApiClient::trace_raw_transaction(client, Bytes::default(), HashSet::default(), None) + .await + .unwrap_err(); assert!(is_unimplemented( TraceApiClient::trace_call_many(client, vec![], None).await.err().unwrap() )); - assert!(is_unimplemented( - TraceApiClient::trace_raw_transaction(client, Bytes::default(), HashSet::default(), None) - .await - .err() - .unwrap() - )); assert!(is_unimplemented( TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()) .await diff --git a/crates/rpc/rpc-types/src/eth/trace/parity.rs b/crates/rpc/rpc-types/src/eth/trace/parity.rs index a6f865a172..638e4cc9de 100644 --- a/crates/rpc/rpc-types/src/eth/trace/parity.rs +++ b/crates/rpc/rpc-types/src/eth/trace/parity.rs @@ -24,20 +24,31 @@ impl TraceResult { } } +/// Different Trace diagnostic targets. #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum TraceType { + /// Default trace Trace, + /// Provides a full trace of the VM’s state throughout the execution of the transaction, + /// including for any subcalls. VmTrace, + /// Provides information detailing all altered portions of the Ethereum state made due to the + /// execution of the transaction. StateDiff, } +/// The Outcome of a traced transaction with optional settings #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TraceResults { + /// Output of the trace pub output: Bytes, + /// Enabled if [TraceType::Trace] is provided pub trace: Option>, + /// Enabled if [TraceType::VmTrace] is provided pub vm_trace: Option, + /// Enabled if [TraceType::StateDiff] is provided pub state_diff: Option, } diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index f2479793c8..e82571ee96 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -1,15 +1,18 @@ //! Contains RPC handler implementations specific to transactions use crate::{ - eth::error::{EthApiError, EthResult}, + eth::{ + error::{EthApiError, EthResult}, + utils::recover_raw_transaction, + }, EthApi, }; use async_trait::async_trait; use reth_primitives::{ BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction, - TransactionSigned, TransactionSignedEcRecovered, H256, U256, + TransactionSignedEcRecovered, H256, U256, }; use reth_provider::{providers::ChainState, BlockProvider, EvmEnvProvider, StateProviderFactory}; -use reth_rlp::Decodable; + use reth_rpc_types::{Index, Transaction, TransactionInfo, TransactionRequest}; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; use revm::primitives::{BlockEnv, CfgEnv}; @@ -186,16 +189,7 @@ where /// /// Returns the hash of the transaction. pub(crate) async fn send_raw_transaction(&self, tx: Bytes) -> EthResult { - let mut data = tx.as_ref(); - if data.is_empty() { - return Err(EthApiError::EmptyRawTransactionData) - } - - let transaction = TransactionSigned::decode(&mut data) - .map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?; - - let recovered = - transaction.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?; + let recovered = recover_raw_transaction(tx)?; let pool_transaction = ::from_recovered_transaction(recovered); diff --git a/crates/rpc/rpc/src/eth/mod.rs b/crates/rpc/rpc/src/eth/mod.rs index 6958b1238d..bb07bc88a3 100644 --- a/crates/rpc/rpc/src/eth/mod.rs +++ b/crates/rpc/rpc/src/eth/mod.rs @@ -9,6 +9,7 @@ mod logs_utils; mod pubsub; pub(crate) mod revm_utils; mod signer; +pub(crate) mod utils; pub use api::{EthApi, EthApiSpec, EthTransactions, TransactionSource}; pub use filter::EthFilter; diff --git a/crates/rpc/rpc/src/eth/utils.rs b/crates/rpc/rpc/src/eth/utils.rs new file mode 100644 index 0000000000..f4a1495148 --- /dev/null +++ b/crates/rpc/rpc/src/eth/utils.rs @@ -0,0 +1,18 @@ +//! Commonly used code snippets + +use crate::eth::error::{EthApiError, EthResult}; +use reth_primitives::{Bytes, TransactionSigned, TransactionSignedEcRecovered}; + +/// Recovers a [TransactionSignedEcRecovered] from an enveloped encoded byte stream. +/// +/// See [TransactionSigned::decode_enveloped] +pub(crate) fn recover_raw_transaction(data: Bytes) -> EthResult { + if data.is_empty() { + return Err(EthApiError::EmptyRawTransactionData) + } + + let transaction = TransactionSigned::decode_enveloped(data) + .map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?; + + transaction.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature) +} diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index c1474d8c68..802aeba27e 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -1,22 +1,26 @@ use crate::{ - eth::{cache::EthStateCache, error::EthResult, revm_utils::inspect, EthTransactions}, + eth::{ + cache::EthStateCache, error::EthResult, revm_utils::inspect, + utils::recover_raw_transaction, EthTransactions, + }, result::internal_rpc_err, }; use async_trait::async_trait; use jsonrpsee::core::RpcResult as Result; -use reth_primitives::{BlockId, Bytes, H256}; +use reth_primitives::{BlockId, BlockNumberOrTag, Bytes, H256}; use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory}; use reth_revm::{ database::{State, SubState}, env::tx_env_with_recovered, tracing::{TracingInspector, TracingInspectorConfig}, }; + use reth_rpc_api::TraceApiServer; use reth_rpc_types::{ trace::{filter::TraceFilter, parity::*}, CallRequest, Index, }; -use revm::primitives::Env; +use revm::primitives::{Env, ExecutionResult, ResultAndState}; use std::collections::HashSet; /// `trace` API implementation. @@ -48,6 +52,71 @@ where Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static, Eth: EthTransactions + 'static, { + /// Executes the transaction at the given [BlockId] with a tracer configured by the config. + fn trace_at( + &self, + env: Env, + config: TracingInspectorConfig, + at: BlockId, + f: F, + ) -> EthResult + where + F: FnOnce(TracingInspector, ResultAndState) -> EthResult, + { + self.eth_api.with_state_at(at, |state| { + let db = SubState::new(State::new(state)); + + let mut inspector = TracingInspector::new(config); + let (res, _) = inspect(db, env, &mut inspector)?; + + f(inspector, res) + }) + } + + /// 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, + ) -> EthResult { + todo!() + } + + /// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces. + pub async fn trace_raw_transaction( + &self, + tx: Bytes, + trace_types: HashSet, + block_id: Option, + ) -> EthResult { + let tx = recover_raw_transaction(tx)?; + + let (cfg, block, at) = self + .eth_api + .evm_env_at(block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Pending))) + .await?; + let tx = tx_env_with_recovered(&tx); + let env = Env { cfg, block, tx }; + + 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) + }) + } + /// Returns transaction trace with the given address. pub async fn trace_get( &self, @@ -77,15 +146,11 @@ where let (cfg, block, at) = self.eth_api.evm_env_at(at).await?; let (tx, tx_info) = transaction.split(); + let tx = tx_env_with_recovered(&tx); + let env = Env { cfg, block, tx }; - self.eth_api.with_state_at(at, |state| { - let tx = tx_env_with_recovered(&tx); - let env = Env { cfg, block, tx }; - let db = SubState::new(State::new(state)); - let mut inspector = TracingInspector::new(TracingInspectorConfig::default_parity()); - - inspect(db, env, &mut inspector)?; - + // execute the trace + self.trace_at(env, TracingInspectorConfig::default_parity(), at, |inspector, _| { let traces = inspector.into_parity_builder().into_localized_transaction_traces(tx_info); Ok(Some(traces)) @@ -99,14 +164,16 @@ where Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static, Eth: EthTransactions + 'static, { + /// Executes the given call and returns a number of possible traces for it. + /// /// Handler for `trace_call` async fn trace_call( &self, - _call: CallRequest, - _trace_types: HashSet, - _block_id: Option, + call: CallRequest, + trace_types: HashSet, + block_id: Option, ) -> Result { - Err(internal_rpc_err("unimplemented")) + Ok(TraceApi::trace_call(self, call, trace_types, block_id).await?) } /// Handler for `trace_callMany` @@ -121,11 +188,11 @@ where /// Handler for `trace_rawTransaction` async fn trace_raw_transaction( &self, - _data: Bytes, - _trace_types: HashSet, - _block_id: Option, + data: Bytes, + trace_types: HashSet, + block_id: Option, ) -> Result { - Err(internal_rpc_err("unimplemented")) + Ok(TraceApi::trace_raw_transaction(self, data, trace_types, block_id).await?) } /// Handler for `trace_replayBlockTransactions` @@ -183,3 +250,10 @@ impl std::fmt::Debug for TraceApi { f.debug_struct("TraceApi").finish_non_exhaustive() } } + +/// Returns the [TracingInspectorConfig] depending on the enabled [TraceType]s +fn tracing_config(trace_types: &HashSet) -> TracingInspectorConfig { + TracingInspectorConfig::default_parity() + .set_state_diffs(trace_types.contains(&TraceType::StateDiff)) + .set_steps(trace_types.contains(&TraceType::VmTrace)) +}