geth_tracer/
config.rs

1// Copyright 2023-2024 Paradigm.xyz
2// This file is part of reth.
3// Reth is a modular, contributor-friendly and blazing-fast implementation of
4// the Ethereum protocol
5
6// Permission is hereby granted, free of charge, to any
7// person obtaining a copy of this software and associated
8// documentation files (the "Software"), to deal in the
9// Software without restriction, including without
10// limitation the rights to use, copy, modify, merge,
11// publish, distribute, sublicense, and/or sell copies of
12// the Software, and to permit persons to whom the Software
13// is furnished to do so, subject to the following
14// conditions:
15
16// The above copyright notice and this permission notice
17// shall be included in all copies or substantial portions
18// of the Software.
19
20// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
21// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
22// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
24// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
27// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28// DEALINGS IN THE SOFTWARE.
29use alloy_rpc_types_trace::{
30    geth::{CallConfig, GethDefaultTracingOptions, PreStateConfig},
31    parity::TraceType,
32};
33use std::collections::HashSet;
34
35/// Gives guidance to the [TracingInspector](crate::tracing::TracingInspector).
36///
37/// Use [TracingInspectorConfig::default_parity] or
38/// [TracingInspectorConfig::default_geth] to get the default configs for
39/// specific styles of traces.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct TracingInspectorConfig {
42    /// Whether to record every individual opcode level step.
43    pub record_steps: bool,
44    /// Whether to record individual memory snapshots.
45    pub record_memory_snapshots: bool,
46    /// Whether to record individual stack snapshots.
47    pub record_stack_snapshots: StackSnapshotType,
48    /// Whether to record state diffs.
49    pub record_state_diff: bool,
50    /// Whether to ignore precompile calls.
51    pub exclude_precompile_calls: bool,
52    /// Whether to record logs
53    pub record_logs: bool,
54}
55
56impl TracingInspectorConfig {
57    /// Returns a config with everything enabled.
58    pub const fn all() -> Self {
59        Self {
60            record_steps: true,
61            record_memory_snapshots: true,
62            record_stack_snapshots: StackSnapshotType::Full,
63            record_state_diff: false,
64            exclude_precompile_calls: false,
65            record_logs: true,
66        }
67    }
68
69    /// Returns a config with everything is disabled.
70    pub const fn none() -> Self {
71        Self {
72            record_steps: false,
73            record_memory_snapshots: false,
74            record_stack_snapshots: StackSnapshotType::None,
75            record_state_diff: false,
76            exclude_precompile_calls: false,
77            record_logs: false,
78        }
79    }
80
81    /// Returns a config for parity style traces.
82    ///
83    /// This config does _not_ record opcode level traces and is suited for
84    /// `trace_transaction`
85    pub const fn default_parity() -> Self {
86        Self {
87            record_steps: false,
88            record_memory_snapshots: false,
89            record_stack_snapshots: StackSnapshotType::None,
90            record_state_diff: false,
91            exclude_precompile_calls: true,
92            record_logs: false,
93        }
94    }
95
96    /// Returns a config for geth style traces.
97    ///
98    /// This config does _not_ record opcode level traces and is suited for
99    /// `debug_traceTransaction`
100    ///
101    /// This will configure the default output of geth's default
102    /// [StructLogTracer](alloy_rpc_trace_types::geth::DefaultFrame).
103    pub const fn default_geth() -> Self {
104        Self {
105            record_steps: true,
106            record_memory_snapshots: false,
107            record_stack_snapshots: StackSnapshotType::Full,
108            record_state_diff: true,
109            exclude_precompile_calls: false,
110            record_logs: false,
111        }
112    }
113
114    /// Returns the [TracingInspectorConfig] depending on the enabled
115    /// [TraceType]s
116    ///
117    /// Note: the parity statediffs can be populated entirely via the execution
118    /// result, so we don't need statediff recording
119    #[inline]
120    pub fn from_parity_config(trace_types: &HashSet<TraceType>) -> Self {
121        let needs_vm_trace = trace_types.contains(&TraceType::VmTrace);
122        let snap_type = if needs_vm_trace {
123            StackSnapshotType::Pushes
124        } else {
125            StackSnapshotType::None
126        };
127        Self::default_parity()
128            .set_steps(needs_vm_trace)
129            .set_stack_snapshots(snap_type)
130            .set_memory_snapshots(needs_vm_trace)
131    }
132
133    /// Returns a config for geth style traces based on the given
134    /// [GethDefaultTracingOptions].
135    ///
136    /// This will configure the output of geth's default
137    /// [StructLogTracer](alloy_rpc_trace_types::geth::DefaultFrame) according
138    /// to the given config.
139    #[inline]
140    pub fn from_geth_config(config: &GethDefaultTracingOptions) -> Self {
141        Self {
142            record_memory_snapshots: config.enable_memory.unwrap_or_default(),
143            record_stack_snapshots: if config.disable_stack.unwrap_or_default()
144            {
145                StackSnapshotType::None
146            } else {
147                StackSnapshotType::Full
148            },
149            record_state_diff: !config.disable_storage.unwrap_or_default(),
150            ..Self::default_geth()
151        }
152    }
153
154    /// Returns a config for geth's
155    /// [CallTracer](alloy_rpc_trace_types::geth::CallFrame).
156    ///
157    /// This returns [Self::none] and enables
158    /// [TracingInspectorConfig::record_logs] if configured in
159    /// the given [CallConfig]
160    #[inline]
161    pub fn from_geth_call_config(config: &CallConfig) -> Self {
162        Self::none()
163            // call tracer is similar parity tracer with optional support for
164            // logs
165            .set_record_logs(config.with_log.unwrap_or_default())
166    }
167
168    /// Returns a config for geth's
169    /// [PrestateTracer](alloy_rpc_trace_types::geth::PreStateFrame).
170    ///
171    /// Note: This currently returns [Self::none] because the prestate tracer
172    /// result currently relies on the execution result entirely, see
173    /// [GethTraceBuilder::geth_prestate_traces](crate::tracing::geth::GethTraceBuilder::geth_prestate_traces)
174    #[inline]
175    pub const fn from_geth_prestate_config(_config: &PreStateConfig) -> Self {
176        Self::none()
177    }
178
179    /// Configure whether calls to precompiles should be ignored.
180    ///
181    /// If set to `true`, calls to precompiles without value transfers will be
182    /// ignored.
183    pub const fn set_exclude_precompile_calls(
184        mut self, exclude_precompile_calls: bool,
185    ) -> Self {
186        self.exclude_precompile_calls = exclude_precompile_calls;
187        self
188    }
189
190    /// Disable recording of individual opcode level steps
191    pub const fn disable_steps(self) -> Self { self.set_steps(false) }
192
193    /// Enable recording of individual opcode level steps
194    pub const fn steps(self) -> Self { self.set_steps(true) }
195
196    /// Configure whether individual opcode level steps should be recorded
197    pub const fn set_steps(mut self, record_steps: bool) -> Self {
198        self.record_steps = record_steps;
199        self
200    }
201
202    /// Disable recording of individual memory snapshots
203    pub const fn disable_memory_snapshots(self) -> Self {
204        self.set_memory_snapshots(false)
205    }
206
207    /// Enable recording of individual memory snapshots
208    pub const fn memory_snapshots(self) -> Self {
209        self.set_memory_snapshots(true)
210    }
211
212    /// Configure whether the tracer should record memory snapshots
213    pub const fn set_memory_snapshots(
214        mut self, record_memory_snapshots: bool,
215    ) -> Self {
216        self.record_memory_snapshots = record_memory_snapshots;
217        self
218    }
219
220    /// Disable recording of individual stack snapshots
221    pub const fn disable_stack_snapshots(self) -> Self {
222        self.set_stack_snapshots(StackSnapshotType::None)
223    }
224
225    /// Enable recording of individual stack snapshots
226    pub const fn stack_snapshots(self) -> Self {
227        self.set_stack_snapshots(StackSnapshotType::Full)
228    }
229
230    /// Configure how the tracer should record stack snapshots
231    pub const fn set_stack_snapshots(
232        mut self, record_stack_snapshots: StackSnapshotType,
233    ) -> Self {
234        self.record_stack_snapshots = record_stack_snapshots;
235        self
236    }
237
238    /// Disable recording of state diffs
239    pub const fn disable_state_diffs(self) -> Self {
240        self.set_state_diffs(false)
241    }
242
243    /// Configure whether the tracer should record state diffs
244    pub const fn set_state_diffs(mut self, record_state_diff: bool) -> Self {
245        self.record_state_diff = record_state_diff;
246        self
247    }
248
249    /// Sets state diff recording to true.
250    ///
251    /// Also enables steps recording since state diff recording requires steps
252    /// recording.
253    pub const fn with_state_diffs(self) -> Self {
254        self.set_steps_and_state_diffs(true)
255    }
256
257    /// Configure whether the tracer should record steps and state diffs.
258    ///
259    /// This is a convenience method for setting both
260    /// [TracingInspectorConfig::set_steps] and
261    /// [TracingInspectorConfig::set_state_diffs] since tracking state diffs
262    /// requires steps tracing.
263    pub const fn set_steps_and_state_diffs(
264        mut self, steps_and_diffs: bool,
265    ) -> Self {
266        self.record_steps = steps_and_diffs;
267        self.record_state_diff = steps_and_diffs;
268        self
269    }
270
271    /// Disable recording of individual logs
272    pub const fn disable_record_logs(self) -> Self {
273        self.set_record_logs(false)
274    }
275
276    /// Enable recording of individual logs
277    pub const fn record_logs(self) -> Self { self.set_record_logs(true) }
278
279    /// Configure whether the tracer should record logs
280    pub const fn set_record_logs(mut self, record_logs: bool) -> Self {
281        self.record_logs = record_logs;
282        self
283    }
284}
285
286/// How much of the stack to record. Nothing, just the items pushed, or the full
287/// stack
288#[derive(Clone, Copy, Debug, PartialEq, Eq)]
289pub enum StackSnapshotType {
290    /// Don't record stack snapshots
291    None,
292    /// Record only the items pushed to the stack
293    Pushes,
294    /// Record the full stack
295    Full,
296}
297
298impl StackSnapshotType {
299    /// Returns true if this is the [StackSnapshotType::Full] variant
300    #[inline]
301    pub const fn is_full(self) -> bool { matches!(self, Self::Full) }
302
303    /// Returns true if this is the [StackSnapshotType::Pushes] variant
304    #[inline]
305    pub const fn is_pushes(self) -> bool { matches!(self, Self::Pushes) }
306}
307
308/// What kind of tracing style this is.
309///
310/// This affects things like error messages.
311#[derive(Clone, Copy, Debug, PartialEq, Eq)]
312pub(crate) enum TraceStyle {
313    /// Parity style tracer
314    Parity,
315    /// Geth style tracer
316    Geth,
317}
318
319impl TraceStyle {
320    /// Returns true if this is a parity style tracer.
321    pub(crate) const fn is_parity(self) -> bool { matches!(self, Self::Parity) }
322}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327
328    #[test]
329    fn test_parity_config() {
330        let mut s = HashSet::new();
331        s.insert(TraceType::StateDiff);
332        let config = TracingInspectorConfig::from_parity_config(&s);
333        // not required
334        assert!(!config.record_steps);
335        assert!(!config.record_state_diff);
336
337        let mut s = HashSet::new();
338        s.insert(TraceType::VmTrace);
339        let config = TracingInspectorConfig::from_parity_config(&s);
340        assert!(config.record_steps);
341        assert!(!config.record_state_diff);
342
343        let mut s = HashSet::new();
344        s.insert(TraceType::VmTrace);
345        s.insert(TraceType::StateDiff);
346        let config = TracingInspectorConfig::from_parity_config(&s);
347        assert!(config.record_steps);
348        // not required for StateDiff
349        assert!(!config.record_state_diff);
350    }
351}