revm_inspector/
eip3155.rs

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
13/// [EIP-3155](https://eips.ethereum.org/EIPS/eip-3155) tracer [Inspector].
14pub struct TracerEip3155 {
15    output: Box<dyn Write>,
16    gas_inspector: GasInspector,
17    /// Print summary of the execution.
18    print_summary: bool,
19    stack: Vec<U256>,
20    pc: u64,
21    opcode: u8,
22    gas: u64,
23    refunded: i64,
24    mem_size: usize,
25    include_memory: bool,
26    memory: Option<String>,
27}
28
29impl std::fmt::Debug for TracerEip3155 {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        f.debug_struct("TracerEip3155")
32            .field("gas_inspector", &self.gas_inspector)
33            .field("print_summary", &self.print_summary)
34            .field("stack", &self.stack)
35            .field("pc", &self.pc)
36            .field("opcode", &self.opcode)
37            .field("gas", &self.gas)
38            .field("refunded", &self.refunded)
39            .field("mem_size", &self.mem_size)
40            .field("include_memory", &self.include_memory)
41            .field("memory", &self.memory)
42            .finish()
43    }
44}
45
46// # Output
47// The CUT MUST output a `json` object for EACH operation.
48#[derive(Serialize)]
49#[serde(rename_all = "camelCase")]
50struct Output<'a> {
51    // Required fields:
52    /// Program counter
53    pc: u64,
54    /// Depth of the call stack
55    depth: u64,
56    /// Name of the operation
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    op_name: Option<&'static str>,
59    /// OpCode
60    op: u8,
61    /// Gas left before executing this operation
62    #[serde(serialize_with = "serde_hex_u64")]
63    gas: u64,
64    /// Gas cost of this operation
65    #[serde(serialize_with = "serde_hex_u64")]
66    gas_cost: u64,
67    /// Array of all values on the stack
68    stack: &'a [U256],
69    /// Data returned by the function call
70    return_data: &'static str,
71    /// Amount of **global** gas refunded
72    #[serde(serialize_with = "serde_hex_u64")]
73    refund: u64,
74    /// Size of memory array
75    #[serde(serialize_with = "serde_hex_u64")]
76    mem_size: u64,
77
78    // Optional fields:
79    /// Description of an error (should contain revert reason if supported)
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    error: Option<String>,
82    /// Array of all allocated values
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    memory: Option<String>,
85    /// Array of all stored values
86    #[serde(default, skip_serializing_if = "Option::is_none")]
87    storage: Option<HashMap<String, String>>,
88    /// Array of values, Stack of the called function
89    #[serde(default, skip_serializing_if = "Option::is_none")]
90    return_stack: Option<Vec<String>>,
91}
92
93// # Summary and error handling
94#[derive(Serialize)]
95#[serde(rename_all = "camelCase")]
96struct Summary {
97    // Required fields:
98    /// Root of the state trie after executing the transaction
99    state_root: String,
100    /// Return values of the function
101    output: String,
102    /// All gas used by the transaction
103    #[serde(serialize_with = "serde_hex_u64")]
104    gas_used: u64,
105    /// Bool whether transaction was executed successfully
106    pass: bool,
107
108    // Optional fields:
109    /// Time in nanoseconds needed to execute the transaction
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    time: Option<u128>,
112    /// Name of the fork rules used for execution
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    fork: Option<String>,
115}
116
117impl TracerEip3155 {
118    /// Creates a new EIP-3155 tracer with the given output writer, by first wrapping it in a
119    /// [`BufWriter`](std::io::BufWriter).
120    pub fn buffered(output: impl Write + 'static) -> Self {
121        Self::new(Box::new(std::io::BufWriter::new(output)))
122    }
123
124    /// Creates a new EIP-3155 tracer with a stdout output.
125    pub fn new_stdout() -> Self {
126        Self::buffered(std::io::stdout())
127    }
128
129    /// Creates a new EIP-3155 tracer with the given output writer.
130    pub fn new(output: Box<dyn Write>) -> Self {
131        Self {
132            output,
133            gas_inspector: GasInspector::new(),
134            print_summary: true,
135            include_memory: false,
136            stack: Default::default(),
137            memory: Default::default(),
138            pc: 0,
139            opcode: 0,
140            gas: 0,
141            refunded: 0,
142            mem_size: 0,
143        }
144    }
145
146    /// Sets the writer to use for the output.
147    pub fn set_writer(&mut self, writer: Box<dyn Write>) {
148        self.output = writer;
149    }
150
151    /// Don't include a summary at the end of the trace
152    pub fn without_summary(mut self) -> Self {
153        self.print_summary = false;
154        self
155    }
156
157    /// Include a memory field for each step. This significantly increases processing time and output size.
158    pub fn with_memory(mut self) -> Self {
159        self.include_memory = true;
160        self
161    }
162
163    /// Resets the tracer to its initial state of [`Self::new`].
164    ///
165    /// This makes the inspector ready to be used again.
166    pub fn clear(&mut self) {
167        let Self {
168            gas_inspector,
169            stack,
170            pc,
171            opcode,
172            gas,
173            refunded,
174            mem_size,
175            ..
176        } = self;
177        *gas_inspector = GasInspector::new();
178        stack.clear();
179        *pc = 0;
180        *opcode = 0;
181        *gas = 0;
182        *refunded = 0;
183        *mem_size = 0;
184    }
185
186    fn print_summary(&mut self, result: &InterpreterResult, context: &mut impl ContextTr) {
187        if !self.print_summary {
188            return;
189        }
190        let spec = context.cfg().spec().into();
191        let gas_limit = context.tx().gas_limit();
192        let value = Summary {
193            state_root: B256::ZERO.to_string(),
194            output: result.output.to_string(),
195            gas_used: gas_limit - self.gas_inspector.gas_remaining(),
196            pass: result.is_ok(),
197            time: None,
198            fork: Some(spec.to_string()),
199        };
200        let _ = self.write_value(&value);
201    }
202
203    fn write_value(&mut self, value: &impl serde::Serialize) -> std::io::Result<()> {
204        write_value(&mut *self.output, value)
205    }
206}
207
208pub trait CloneStack {
209    fn clone_into(&self, stack: &mut Vec<U256>);
210}
211
212impl CloneStack for Stack {
213    fn clone_into(&self, stack: &mut Vec<U256>) {
214        stack.extend_from_slice(self.data());
215    }
216}
217
218impl<CTX, INTR> Inspector<CTX, INTR> for TracerEip3155
219where
220    CTX: ContextTr,
221    INTR: InterpreterTypes<Stack: StackTr + CloneStack>,
222{
223    fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _: &mut CTX) {
224        self.gas_inspector.initialize_interp(&interp.gas);
225    }
226
227    fn step(&mut self, interp: &mut Interpreter<INTR>, _: &mut CTX) {
228        self.gas_inspector.step(&interp.gas);
229        self.stack.clear();
230        interp.stack.clone_into(&mut self.stack);
231        self.memory = if self.include_memory {
232            Some(hex::encode_prefixed(
233                interp.memory.slice(0..interp.memory.size()).as_ref(),
234            ))
235        } else {
236            None
237        };
238        self.pc = interp.bytecode.pc() as u64;
239        self.opcode = interp.bytecode.opcode();
240        self.mem_size = interp.memory.size();
241        self.gas = interp.gas.remaining();
242        self.refunded = interp.gas.refunded();
243    }
244
245    fn step_end(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {
246        self.gas_inspector.step_end(&interp.gas);
247        let value = Output {
248            pc: self.pc,
249            op: self.opcode,
250            gas: self.gas,
251            gas_cost: self.gas_inspector.last_gas_cost(),
252            stack: &self.stack,
253            depth: context.journal_mut().depth() as u64,
254            return_data: "0x",
255            refund: self.refunded as u64,
256            mem_size: self.mem_size as u64,
257
258            op_name: OpCode::new(self.opcode).map(|i| i.as_str()),
259            error: interp
260                .bytecode
261                .action()
262                .as_ref()
263                .and_then(|a| a.instruction_result())
264                .map(|ir| format!("{ir:?}")),
265            memory: self.memory.take(),
266            storage: None,
267            return_stack: None,
268        };
269        let _ = write_value(&mut self.output, &value);
270    }
271
272    fn call_end(&mut self, context: &mut CTX, _: &CallInputs, outcome: &mut CallOutcome) {
273        self.gas_inspector.call_end(outcome);
274
275        if context.journal_mut().depth() == 0 {
276            self.print_summary(&outcome.result, context);
277            let _ = self.output.flush();
278            // Clear the state if we are at the top level.
279            self.clear();
280        }
281    }
282
283    fn create_end(&mut self, context: &mut CTX, _: &CreateInputs, outcome: &mut CreateOutcome) {
284        self.gas_inspector.create_end(outcome);
285
286        if context.journal_mut().depth() == 0 {
287            self.print_summary(&outcome.result, context);
288            let _ = self.output.flush();
289            // Clear the state if we are at the top level.
290            self.clear();
291        }
292    }
293}
294
295fn write_value(
296    output: &mut dyn std::io::Write,
297    value: &impl serde::Serialize,
298) -> std::io::Result<()> {
299    serde_json::to_writer(&mut *output, value)?;
300    output.write_all(b"\n")
301}
302
303fn serde_hex_u64<S: serde::Serializer>(n: &u64, serializer: S) -> Result<S::Ok, S::Error> {
304    serializer.serialize_str(&format!("{:#x}", *n))
305}