1use crate::{inspectors::GasInspector, Inspector};
2use context::{Cfg, ContextTr, JournalTr, Transaction};
3use interpreter::{
4 interpreter_types::{Jumps, LoopControl, MemoryTr, StackTr},
5 CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterResult,
6 InterpreterTypes, Stack,
7};
8use primitives::{hex, HashMap, B256, U256};
9use serde::Serialize;
10use state::bytecode::opcode::OpCode;
11use std::io::Write;
12
13pub struct TracerEip3155 {
15 output: Box<dyn Write>,
16 gas_inspector: GasInspector,
17 print_summary: bool,
19 stack: Vec<U256>,
20 pc: u64,
21 opcode: u8,
22 gas: u64,
23 reservoir: u64,
24 state_gas: u64,
25 refunded: i64,
26 mem_size: usize,
27 include_memory: bool,
28 memory: Option<String>,
29}
30
31impl std::fmt::Debug for TracerEip3155 {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("TracerEip3155")
34 .field("gas_inspector", &self.gas_inspector)
35 .field("print_summary", &self.print_summary)
36 .field("stack", &self.stack)
37 .field("pc", &self.pc)
38 .field("opcode", &self.opcode)
39 .field("gas", &self.gas)
40 .field("reservoir", &self.reservoir)
41 .field("state_gas", &self.state_gas)
42 .field("refunded", &self.refunded)
43 .field("mem_size", &self.mem_size)
44 .field("include_memory", &self.include_memory)
45 .field("memory", &self.memory)
46 .finish()
47 }
48}
49
50#[derive(Serialize)]
53#[serde(rename_all = "camelCase")]
54struct Output<'a> {
55 pc: u64,
58 depth: u64,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
62 op_name: Option<&'static str>,
63 op: u8,
65 #[serde(serialize_with = "serde_hex_u64")]
67 gas: u64,
68 #[serde(serialize_with = "serde_hex_u64")]
70 reservoir: u64,
71 #[serde(serialize_with = "serde_hex_u64")]
73 state_gas: u64,
74 #[serde(serialize_with = "serde_hex_u64")]
76 gas_cost: u64,
77 stack: &'a [U256],
79 return_data: &'static str,
81 #[serde(serialize_with = "serde_hex_u64")]
83 refund: u64,
84 #[serde(serialize_with = "serde_hex_u64")]
86 mem_size: u64,
87
88 #[serde(default, skip_serializing_if = "Option::is_none")]
91 error: Option<String>,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
94 memory: Option<String>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
97 storage: Option<HashMap<String, String>>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
100 return_stack: Option<Vec<String>>,
101}
102
103#[derive(Serialize)]
105#[serde(rename_all = "camelCase")]
106struct Summary {
107 state_root: String,
110 output: String,
112 #[serde(serialize_with = "serde_hex_u64")]
114 gas_used: u64,
115 pass: bool,
117
118 #[serde(default, skip_serializing_if = "Option::is_none")]
121 time: Option<u128>,
122 #[serde(default, skip_serializing_if = "Option::is_none")]
124 fork: Option<String>,
125}
126
127impl TracerEip3155 {
128 pub fn buffered(output: impl Write + 'static) -> Self {
131 Self::new(Box::new(std::io::BufWriter::new(output)))
132 }
133
134 pub fn new_stdout() -> Self {
136 Self::buffered(std::io::stdout())
137 }
138
139 pub fn new(output: Box<dyn Write>) -> Self {
141 Self {
142 output,
143 gas_inspector: GasInspector::new(),
144 print_summary: true,
145 include_memory: false,
146 stack: Default::default(),
147 memory: Default::default(),
148 pc: 0,
149 opcode: 0,
150 gas: 0,
151 reservoir: 0,
152 state_gas: 0,
153 refunded: 0,
154 mem_size: 0,
155 }
156 }
157
158 pub fn set_writer(&mut self, writer: Box<dyn Write>) {
160 self.output = writer;
161 }
162
163 pub const fn without_summary(mut self) -> Self {
165 self.print_summary = false;
166 self
167 }
168
169 pub const fn with_memory(mut self) -> Self {
171 self.include_memory = true;
172 self
173 }
174
175 pub fn clear(&mut self) {
179 let Self {
180 gas_inspector,
181 stack,
182 pc,
183 opcode,
184 gas,
185 reservoir,
186 state_gas,
187 refunded,
188 mem_size,
189 ..
190 } = self;
191 *gas_inspector = GasInspector::new();
192 stack.clear();
193 *pc = 0;
194 *opcode = 0;
195 *gas = 0;
196 *reservoir = 0;
197 *state_gas = 0;
198 *refunded = 0;
199 *mem_size = 0;
200 }
201
202 fn print_summary(&mut self, result: &InterpreterResult, context: &mut impl ContextTr) {
203 if !self.print_summary {
204 return;
205 }
206 let spec = context.cfg().spec().into();
207 let gas_limit = context.tx().gas_limit();
208 let value = Summary {
209 state_root: B256::ZERO.to_string(),
210 output: result.output.to_string(),
211 gas_used: gas_limit - self.gas_inspector.gas_remaining(),
212 pass: result.is_ok(),
213 time: None,
214 fork: Some(spec.to_string()),
215 };
216 let _ = self.write_value(&value);
217 }
218
219 fn write_value(&mut self, value: &impl serde::Serialize) -> std::io::Result<()> {
220 write_value(&mut *self.output, value)
221 }
222}
223
224pub trait CloneStack {
225 fn clone_into(&self, stack: &mut Vec<U256>);
226}
227
228impl CloneStack for Stack {
229 fn clone_into(&self, stack: &mut Vec<U256>) {
230 stack.extend_from_slice(self.data());
231 }
232}
233
234impl<CTX, INTR> Inspector<CTX, INTR> for TracerEip3155
235where
236 CTX: ContextTr,
237 INTR: InterpreterTypes<Stack: StackTr + CloneStack>,
238{
239 fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _: &mut CTX) {
240 self.gas_inspector.initialize_interp(&interp.gas);
241 }
242
243 fn step(&mut self, interp: &mut Interpreter<INTR>, _: &mut CTX) {
244 self.gas_inspector.step(&interp.gas);
245 self.stack.clear();
246 interp.stack.clone_into(&mut self.stack);
247 self.memory = if self.include_memory {
248 Some(hex::encode_prefixed(
249 interp.memory.slice(0..interp.memory.size()).as_ref(),
250 ))
251 } else {
252 None
253 };
254 self.pc = interp.bytecode.pc() as u64;
255 self.opcode = interp.bytecode.opcode();
256 self.mem_size = interp.memory.size();
257 self.gas = interp.gas.remaining();
258 self.reservoir = interp.gas.reservoir();
259 self.state_gas = interp.gas.state_gas_spent().max(0) as u64;
263 self.refunded = interp.gas.refunded();
264 }
265
266 fn step_end(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {
267 self.gas_inspector.step_end(&interp.gas);
268 let value = Output {
269 pc: self.pc,
270 op: self.opcode,
271 gas: self.gas,
272 reservoir: self.reservoir,
273 state_gas: self.state_gas,
274 gas_cost: self.gas_inspector.last_gas_cost(),
275 stack: &self.stack,
276 depth: context.journal_mut().depth() as u64,
277 return_data: "0x",
278 refund: self.refunded as u64,
279 mem_size: self.mem_size as u64,
280
281 op_name: OpCode::new(self.opcode).map(|i| i.as_str()),
282 error: interp
283 .bytecode
284 .action()
285 .as_ref()
286 .and_then(|a| a.instruction_result())
287 .map(|ir| format!("{ir:?}")),
288 memory: self.memory.take(),
289 storage: None,
290 return_stack: None,
291 };
292 let _ = write_value(&mut self.output, &value);
293 }
294
295 fn call_end(&mut self, context: &mut CTX, _: &CallInputs, outcome: &mut CallOutcome) {
296 self.gas_inspector.call_end(outcome);
297
298 if context.journal_mut().depth() == 0 {
299 self.print_summary(&outcome.result, context);
300 let _ = self.output.flush();
301 self.clear();
303 }
304 }
305
306 fn create_end(&mut self, context: &mut CTX, _: &CreateInputs, outcome: &mut CreateOutcome) {
307 self.gas_inspector.create_end(outcome);
308
309 if context.journal_mut().depth() == 0 {
310 self.print_summary(&outcome.result, context);
311 let _ = self.output.flush();
312 self.clear();
314 }
315 }
316}
317
318fn write_value(
319 output: &mut dyn std::io::Write,
320 value: &impl serde::Serialize,
321) -> std::io::Result<()> {
322 serde_json::to_writer(&mut *output, value)?;
323 output.write_all(b"\n")
324}
325
326fn serde_hex_u64<S: serde::Serializer>(n: &u64, serializer: S) -> Result<S::Ok, S::Error> {
327 serializer.serialize_str(&format!("{:#x}", *n))
328}