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