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}