From 4c1a35b2b80e54e1507d8364d16458cdfef1b79a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 13 Mar 2023 23:02:02 +0100 Subject: [PATCH] feat: add trace inspector config (#1729) --- .../revm-inspectors/src/tracing/config.rs | 75 +++++++++++++++++ .../revm/revm-inspectors/src/tracing/mod.rs | 82 +++++++++++-------- 2 files changed, 123 insertions(+), 34 deletions(-) create mode 100644 crates/revm/revm-inspectors/src/tracing/config.rs diff --git a/crates/revm/revm-inspectors/src/tracing/config.rs b/crates/revm/revm-inspectors/src/tracing/config.rs new file mode 100644 index 000000000..14abb668a --- /dev/null +++ b/crates/revm/revm-inspectors/src/tracing/config.rs @@ -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 + } +} diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index e5285a3e6..958fc2c64 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -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, @@ -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