mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(rpc): add trace_rawTransaction handler (#1861)
This commit is contained in:
@ -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<TraceType>,
|
||||
) -> (Option<Vec<TransactionTrace>>, Option<VmTrace>, Option<StateDiff>) {
|
||||
// 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<Item = TransactionTrace> {
|
||||
let trace_addresses = self.trace_addresses();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<Vec<TransactionTrace>>,
|
||||
/// Enabled if [TraceType::VmTrace] is provided
|
||||
pub vm_trace: Option<VmTrace>,
|
||||
/// Enabled if [TraceType::StateDiff] is provided
|
||||
pub state_diff: Option<StateDiff>,
|
||||
}
|
||||
|
||||
|
||||
@ -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<H256> {
|
||||
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 = <Pool::Transaction>::from_recovered_transaction(recovered);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
18
crates/rpc/rpc/src/eth/utils.rs
Normal file
18
crates/rpc/rpc/src/eth/utils.rs
Normal file
@ -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<TransactionSignedEcRecovered> {
|
||||
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)
|
||||
}
|
||||
@ -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<F, R>(
|
||||
&self,
|
||||
env: Env,
|
||||
config: TracingInspectorConfig,
|
||||
at: BlockId,
|
||||
f: F,
|
||||
) -> EthResult<R>
|
||||
where
|
||||
F: FnOnce(TracingInspector, ResultAndState) -> EthResult<R>,
|
||||
{
|
||||
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<TraceType>,
|
||||
_block_id: Option<BlockId>,
|
||||
) -> EthResult<TraceResults> {
|
||||
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<TraceType>,
|
||||
block_id: Option<BlockId>,
|
||||
) -> EthResult<TraceResults> {
|
||||
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<TraceType>,
|
||||
_block_id: Option<BlockId>,
|
||||
call: CallRequest,
|
||||
trace_types: HashSet<TraceType>,
|
||||
block_id: Option<BlockId>,
|
||||
) -> Result<TraceResults> {
|
||||
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<TraceType>,
|
||||
_block_id: Option<BlockId>,
|
||||
data: Bytes,
|
||||
trace_types: HashSet<TraceType>,
|
||||
block_id: Option<BlockId>,
|
||||
) -> Result<TraceResults> {
|
||||
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<Client, Eth> std::fmt::Debug for TraceApi<Client, Eth> {
|
||||
f.debug_struct("TraceApi").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [TracingInspectorConfig] depending on the enabled [TraceType]s
|
||||
fn tracing_config(trace_types: &HashSet<TraceType>) -> TracingInspectorConfig {
|
||||
TracingInspectorConfig::default_parity()
|
||||
.set_state_diffs(trace_types.contains(&TraceType::StateDiff))
|
||||
.set_steps(trace_types.contains(&TraceType::VmTrace))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user