Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc): impl traceCall #2029

Merged
merged 2 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions crates/revm/revm-inspectors/src/tracing/builder/parity.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<TraceType>,
) -> 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,
Expand Down
32 changes: 6 additions & 26 deletions crates/revm/revm-inspectors/src/tracing/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -46,10 +43,7 @@ pub struct TracingInspector {
/// Tracks the return value of the last call
last_call_return_data: Option<Bytes>,
/// 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<GasInspector>,
gas_inspector: GasInspector,
}

// === impl TracingInspector ===
Expand Down Expand Up @@ -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<GasInspector>,
) -> Self {
self.gas_inspector = gas_inspector;
self
}

/// Returns the last trace [CallTrace] index from the stack.
///
/// # Panics
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 3 additions & 19 deletions crates/rpc/rpc/src/eth/api/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -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;

Expand All @@ -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<StateOverride>,
) -> 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,
Expand Down
85 changes: 81 additions & 4 deletions crates/rpc/rpc/src/eth/api/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use crate::{
eth::{
error::{EthApiError, EthResult, SignError},
revm_utils::{inspect, prepare_call_env, transact},
utils::recover_raw_transaction,
},
EthApi,
Expand All @@ -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]
Expand Down Expand Up @@ -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<H256>;

/// Prepares the state and env for the given [CallRequest] at the given [BlockId] and executes
/// the closure.
async fn with_call_at<F, R>(
&self,
request: CallRequest,
at: BlockId,
state_overrides: Option<StateOverride>,
f: F,
) -> EthResult<R>
where
F: for<'r> FnOnce(CacheDB<State<StateProviderBox<'r>>>, Env) -> EthResult<R> + Send;

/// Executes the call request at the given [BlockId].
async fn transact_call_at(
&self,
request: CallRequest,
at: BlockId,
state_overrides: Option<StateOverride>,
) -> EthResult<(ResultAndState, Env)>;

/// Executes the call request at the given [BlockId]
async fn inspect_call_at<I>(
&self,
request: CallRequest,
at: BlockId,
state_overrides: Option<StateOverride>,
inspector: I,
) -> EthResult<(ResultAndState, Env)>
where
I: for<'r> Inspector<CacheDB<State<StateProviderBox<'r>>>> + Send;
}

#[async_trait]
Expand Down Expand Up @@ -212,6 +249,46 @@ where

Ok(hash)
}

async fn with_call_at<F, R>(
&self,
request: CallRequest,
at: BlockId,
state_overrides: Option<StateOverride>,
f: F,
) -> EthResult<R>
where
F: for<'r> FnOnce(CacheDB<State<StateProviderBox<'r>>>, Env) -> EthResult<R> + 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<StateOverride>,
) -> EthResult<(ResultAndState, Env)> {
self.with_call_at(request, at, state_overrides, |mut db, env| transact(&mut db, env)).await
}

async fn inspect_call_at<I>(
&self,
request: CallRequest,
at: BlockId,
state_overrides: Option<StateOverride>,
inspector: I,
) -> EthResult<(ResultAndState, Env)>
where
I: for<'r> Inspector<CacheDB<State<StateProviderBox<'r>>>> + Send,
{
self.with_call_at(request, at, state_overrides, |db, env| inspect(db, env, inspector)).await
}
}

// === impl EthApi ===
Expand Down
34 changes: 17 additions & 17 deletions crates/rpc/rpc/src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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<TraceType>,
_block_id: Option<BlockId>,
call: CallRequest,
trace_types: HashSet<TraceType>,
block_id: Option<BlockId>,
) -> EthResult<TraceResults> {
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.
Expand All @@ -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)
})
}

Expand Down