feat: add trace inspector config (#1729)

This commit is contained in:
Matthias Seitz
2023-03-13 23:02:02 +01:00
committed by GitHub
parent 5d9237c2e8
commit 4c1a35b2b8
2 changed files with 123 additions and 34 deletions

View File

@ -0,0 +1,75 @@
/// Gives guidance to the [TracingInspector](crate::tracing::TracingInspector).
///
/// Use [TraceInspectorConfig::default_parity] or [TraceInspectorConfig::default_geth] to get the
/// default configs for specific styles of traces.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct TraceInspectorConfig {
/// Whether to record every individual opcode level step.
pub record_steps: bool,
/// Whether to record individual memory snapshots.
pub record_memory_snapshots: bool,
/// Whether to record individual stack snapshots.
pub record_stack_snapshots: bool,
/// Whether to record state diffs.
pub record_state_diff: bool,
}
impl TraceInspectorConfig {
/// Returns a config with everything enabled.
pub const fn all() -> Self {
Self {
record_steps: true,
record_memory_snapshots: true,
record_stack_snapshots: true,
record_state_diff: false,
}
}
/// Returns a config for parity style traces.
///
/// This config does _not_ record opcode level traces and is suited for `trace_transaction`
pub const fn default_parity() -> Self {
Self {
record_steps: false,
record_memory_snapshots: false,
record_stack_snapshots: false,
record_state_diff: false,
}
}
/// Returns a config for geth style traces.
///
/// This config does _not_ record opcode level traces and is suited for `debug_traceTransaction`
pub const fn default_geth() -> Self {
Self {
record_steps: true,
record_memory_snapshots: true,
record_stack_snapshots: true,
record_state_diff: true,
}
}
/// Configure whether individual opcode level steps should be recorded
pub fn set_steps(mut self, record_steps: bool) -> Self {
self.record_steps = record_steps;
self
}
/// Configure whether the tracer should record memory snapshots
pub fn set_memory_snapshots(mut self, record_memory_snapshots: bool) -> Self {
self.record_memory_snapshots = record_memory_snapshots;
self
}
/// Configure whether the tracer should record stack snapshots
pub fn set_stack_snapshots(mut self, record_stack_snapshots: bool) -> Self {
self.record_stack_snapshots = record_stack_snapshots;
self
}
/// Configure whether the tracer should record state diffs
pub fn set_state_diffs(mut self, record_state_diff: bool) -> Self {
self.record_state_diff = record_state_diff;
self
}
}

View File

@ -18,8 +18,10 @@ use revm::{
use types::{CallTrace, CallTraceStep};
mod arena;
mod config;
mod types;
mod utils;
pub use config::TraceInspectorConfig;
/// An inspector that collects call traces.
///
@ -29,10 +31,10 @@ mod utils;
/// The [TracingInspector] keeps track of everything by:
/// 1. start tracking steps/calls on [Inspector::step] and [Inspector::call]
/// 2. complete steps/calls on [Inspector::step_end] and [Inspector::call_end]
#[derive(Default, Debug, Clone)]
#[derive(Debug, Clone)]
pub struct TracingInspector {
/// Whether to include individual steps [Inspector::step]
record_steps: bool,
/// Configures what and how the inspector records traces.
config: TraceInspectorConfig,
/// Records all call traces
traces: CallTraceArena,
trace_stack: Vec<usize>,
@ -47,18 +49,22 @@ pub struct TracingInspector {
// === impl TracingInspector ===
impl TracingInspector {
/// Returns a new instance for the given config
pub fn new(config: TraceInspectorConfig) -> Self {
Self {
config,
traces: Default::default(),
trace_stack: vec![],
step_stack: vec![],
gas_inspector: Default::default(),
}
}
/// Consumes the Inspector and returns the recorded.
pub fn finalize(self) -> CallTraceArena {
self.traces
}
/// Enables step recording and uses the configured [GasInspector] to report gas costs for each
/// step.
pub fn with_steps_recording(mut self) -> Self {
self.record_steps = true;
self
}
/// Configures a [GasInspector]
///
/// If this [TracingInspector] is part of a stack [InspectorStack](crate::stack::InspectorStack)
@ -167,14 +173,19 @@ impl TracingInspector {
let pc = interp.program_counter();
let memory =
self.config.record_memory_snapshots.then(|| interp.memory.clone()).unwrap_or_default();
let stack =
self.config.record_stack_snapshots.then(|| interp.stack.clone()).unwrap_or_default();
trace.trace.steps.push(CallTraceStep {
depth: data.journaled_state.depth(),
pc,
op: OpCode::try_from_u8(interp.contract.bytecode.bytecode()[pc])
.expect("is valid opcode;"),
contract: interp.contract.address,
stack: interp.stack.clone(),
memory: interp.memory.clone(),
stack,
memory,
gas: self.gas_inspector.as_ref().gas_remaining(),
gas_refund_counter: interp.gas.refunded() as u64,
@ -199,28 +210,31 @@ impl TracingInspector {
let step = &mut self.traces.arena[trace_idx].trace.steps[step_idx];
if let Some(pc) = interp.program_counter().checked_sub(1) {
let op = interp.contract.bytecode.bytecode()[pc];
if self.config.record_state_diff {
let op = interp.contract.bytecode.bytecode()[pc];
let journal_entry = data
.journaled_state
.journal
.last()
// This should always work because revm initializes it as `vec![vec![]]`
// See [JournaledState::new](revm::JournaledState)
.expect("exists; initialized with vec")
.last();
let journal_entry = data
.journaled_state
.journal
.last()
// This should always work because revm initializes it as `vec![vec![]]`
// See [JournaledState::new](revm::JournaledState)
.expect("exists; initialized with vec")
.last();
step.state_diff = match (op, journal_entry) {
(
opcode::SLOAD | opcode::SSTORE,
Some(JournalEntry::StorageChange { address, key, .. }),
) => {
// SAFETY: (Address,key) exists if part if StorageChange
let value = data.journaled_state.state[address].storage[key].present_value();
Some((*key, value))
}
_ => None,
};
step.state_diff = match (op, journal_entry) {
(
opcode::SLOAD | opcode::SSTORE,
Some(JournalEntry::StorageChange { address, key, .. }),
) => {
// SAFETY: (Address,key) exists if part if StorageChange
let value =
data.journaled_state.state[address].storage[key].present_value();
Some((*key, value))
}
_ => None,
};
}
step.gas_cost = step.gas - self.gas_inspector.as_ref().gas_remaining();
}
@ -249,7 +263,7 @@ where
data: &mut EVMData<'_, DB>,
is_static: bool,
) -> InstructionResult {
if self.record_steps {
if self.config.record_steps {
self.gas_inspector.step(interp, data, is_static);
self.start_step(interp, data);
}
@ -279,7 +293,7 @@ where
is_static: bool,
eval: InstructionResult,
) -> InstructionResult {
if self.record_steps {
if self.config.record_steps {
self.gas_inspector.step_end(interp, data, is_static, eval);
self.fill_step_on_step_end(interp, data, eval);
return eval