Skip to main content

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    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// # Output
51// The CUT MUST output a `json` object for EACH operation.
52#[derive(Serialize)]
53#[serde(rename_all = "camelCase")]
54struct Output<'a> {
55    // Required fields:
56    /// Program counter
57    pc: u64,
58    /// Depth of the call stack
59    depth: u64,
60    /// Name of the operation
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    op_name: Option<&'static str>,
63    /// OpCode
64    op: u8,
65    /// Gas left before executing this operation
66    #[serde(serialize_with = "serde_hex_u64")]
67    gas: u64,
68    /// State gas reservoir (EIP-8037)
69    #[serde(serialize_with = "serde_hex_u64")]
70    reservoir: u64,
71    /// State gas spent (EIP-8037)
72    #[serde(serialize_with = "serde_hex_u64")]
73    state_gas: u64,
74    /// Gas cost of this operation
75    #[serde(serialize_with = "serde_hex_u64")]
76    gas_cost: u64,
77    /// Array of all values on the stack
78    stack: &'a [U256],
79    /// Data returned by the function call
80    return_data: &'static str,
81    /// Amount of **global** gas refunded
82    #[serde(serialize_with = "serde_hex_u64")]
83    refund: u64,
84    /// Size of memory array
85    #[serde(serialize_with = "serde_hex_u64")]
86    mem_size: u64,
87
88    // Optional fields:
89    /// Description of an error (should contain revert reason if supported)
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    error: Option<String>,
92    /// Array of all allocated values
93    #[serde(default, skip_serializing_if = "Option::is_none")]
94    memory: Option<String>,
95    /// Array of all stored values
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    storage: Option<HashMap<String, String>>,
98    /// Array of values, Stack of the called function
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    return_stack: Option<Vec<String>>,
101}
102
103// # Summary and error handling
104#[derive(Serialize)]
105#[serde(rename_all = "camelCase")]
106struct Summary {
107    // Required fields:
108    /// Root of the state trie after executing the transaction
109    state_root: String,
110    /// Return values of the function
111    output: String,
112    /// All gas used by the transaction
113    #[serde(serialize_with = "serde_hex_u64")]
114    gas_used: u64,
115    /// Bool whether transaction was executed successfully
116    pass: bool,
117
118    // Optional fields:
119    /// Time in nanoseconds needed to execute the transaction
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    time: Option<u128>,
122    /// Name of the fork rules used for execution
123    #[serde(default, skip_serializing_if = "Option::is_none")]
124    fork: Option<String>,
125}
126
127impl TracerEip3155 {
128    /// Creates a new EIP-3155 tracer with the given output writer, by first wrapping it in a
129    /// [`BufWriter`](std::io::BufWriter).
130    pub fn buffered(output: impl Write + 'static) -> Self {
131        Self::new(Box::new(std::io::BufWriter::new(output)))
132    }
133
134    /// Creates a new EIP-3155 tracer with a stdout output.
135    pub fn new_stdout() -> Self {
136        Self::buffered(std::io::stdout())
137    }
138
139    /// Creates a new EIP-3155 tracer with the given output writer.
140    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    /// Sets the writer to use for the output.
159    pub fn set_writer(&mut self, writer: Box<dyn Write>) {
160        self.output = writer;
161    }
162
163    /// Don't include a summary at the end of the trace
164    pub const fn without_summary(mut self) -> Self {
165        self.print_summary = false;
166        self
167    }
168
169    /// Include a memory field for each step. This significantly increases processing time and output size.
170    pub const fn with_memory(mut self) -> Self {
171        self.include_memory = true;
172        self
173    }
174
175    /// Resets the tracer to its initial state of [`Self::new`].
176    ///
177    /// This makes the inspector ready to be used again.
178    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();
260        self.refunded = interp.gas.refunded();
261    }
262
263    fn step_end(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {
264        self.gas_inspector.step_end(&interp.gas);
265        let value = Output {
266            pc: self.pc,
267            op: self.opcode,
268            gas: self.gas,
269            reservoir: self.reservoir,
270            state_gas: self.state_gas,
271            gas_cost: self.gas_inspector.last_gas_cost(),
272            stack: &self.stack,
273            depth: context.journal_mut().depth() as u64,
274            return_data: "0x",
275            refund: self.refunded as u64,
276            mem_size: self.mem_size as u64,
277
278            op_name: OpCode::new(self.opcode).map(|i| i.as_str()),
279            error: interp
280                .bytecode
281                .action()
282                .as_ref()
283                .and_then(|a| a.instruction_result())
284                .map(|ir| format!("{ir:?}")),
285            memory: self.memory.take(),
286            storage: None,
287            return_stack: None,
288        };
289        let _ = write_value(&mut self.output, &value);
290    }
291
292    fn call_end(&mut self, context: &mut CTX, _: &CallInputs, outcome: &mut CallOutcome) {
293        self.gas_inspector.call_end(outcome);
294
295        if context.journal_mut().depth() == 0 {
296            self.print_summary(&outcome.result, context);
297            let _ = self.output.flush();
298            // Clear the state if we are at the top level.
299            self.clear();
300        }
301    }
302
303    fn create_end(&mut self, context: &mut CTX, _: &CreateInputs, outcome: &mut CreateOutcome) {
304        self.gas_inspector.create_end(outcome);
305
306        if context.journal_mut().depth() == 0 {
307            self.print_summary(&outcome.result, context);
308            let _ = self.output.flush();
309            // Clear the state if we are at the top level.
310            self.clear();
311        }
312    }
313}
314
315fn write_value(
316    output: &mut dyn std::io::Write,
317    value: &impl serde::Serialize,
318) -> std::io::Result<()> {
319    serde_json::to_writer(&mut *output, value)?;
320    output.write_all(b"\n")
321}
322
323fn serde_hex_u64<S: serde::Serializer>(n: &u64, serializer: S) -> Result<S::Ok, S::Error> {
324    serializer.serialize_str(&format!("{:#x}", *n))
325}