revm_inspector/
eip3155.rs

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