diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index c2d310e40c..5db4610714 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -427,7 +427,7 @@ pub fn execute_test_suite( let timer = Instant::now(); let res = ctx.inspect_commit_previous( - TracerEip3155::new(Box::new(stderr())).without_summary(), + TracerEip3155::buffered(stderr()).without_summary(), ); *elapsed.lock().unwrap() += timer.elapsed(); @@ -497,9 +497,8 @@ pub fn execute_test_suite( .with_tx(&tx) .with_cfg(&cfg); - let _ = ctx.inspect_commit_previous( - TracerEip3155::new(Box::new(stderr())).without_summary(), - ); + let _ = ctx + .inspect_commit_previous(TracerEip3155::buffered(stderr()).without_summary()); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 4ecaca1c8f..168568e92c 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -38,7 +38,7 @@ pub struct TracerEip3155 { // The CUT MUST output a `json` object for EACH operation. #[derive(Serialize)] #[serde(rename_all = "camelCase")] -struct Output { +struct Output<'a> { // Required fields: /// Program counter pc: u64, @@ -48,22 +48,26 @@ struct Output { /// OpCode op: u8, /// Gas left before executing this operation - gas: String, + #[serde(serialize_with = "serde_hex_u64")] + gas: u64, /// Gas cost of this operation - gas_cost: String, + #[serde(serialize_with = "serde_hex_u64")] + gas_cost: u64, /// Array of all values on the stack - stack: Vec, + stack: &'a [U256], /// Depth of the call stack depth: u64, /// Depth of the EOF function call stack #[serde(default, skip_serializing_if = "Option::is_none")] function_depth: Option, /// Data returned by the function call - return_data: String, + return_data: &'static str, /// Amount of **global** gas refunded - refund: String, + #[serde(serialize_with = "serde_hex_u64")] + refund: u64, /// Size of memory array - mem_size: String, + #[serde(serialize_with = "serde_hex_u64")] + mem_size: u64, // Optional fields: /// Name of the operation @@ -93,7 +97,8 @@ struct Summary { /// Return values of the function output: String, /// All gas used by the transaction - gas_used: String, + #[serde(serialize_with = "serde_hex_u64")] + gas_used: u64, /// Bool whether transaction was executed successfully pass: bool, @@ -111,35 +116,13 @@ where CTX: CfgGetter + TransactionGetter, INTR:, { - /// Sets the writer to use for the output. - pub fn set_writer(&mut self, writer: Box) { - self.output = writer; - } - - /// Resets the Tracer to its initial state of [Self::new]. - /// This makes the inspector ready to be used again. - pub fn clear(&mut self) { - let Self { - gas_inspector, - stack, - pc, - opcode, - gas, - refunded, - mem_size, - skip, - .. - } = self; - *gas_inspector = GasInspector::new(); - stack.clear(); - *pc = 0; - *opcode = 0; - *gas = 0; - *refunded = 0; - *mem_size = 0; - *skip = false; + /// Creates a new EIP-3155 tracer with the given output writer, by first wrapping it in a + /// [`BufWriter`](std::io::BufWriter). + pub fn buffered(output: impl Write + 'static) -> Self { + Self::new(Box::new(std::io::BufWriter::new(output))) } + /// Creates a new EIP-3155 tracer with the given output writer. pub fn new(output: Box) -> Self { Self { output, @@ -160,6 +143,11 @@ where } } + /// Sets the writer to use for the output. + pub fn set_writer(&mut self, writer: Box) { + self.output = writer; + } + /// Don't include a summary at the end of the trace pub fn without_summary(mut self) -> Self { self.print_summary = false; @@ -172,10 +160,29 @@ where self } - fn write_value(&mut self, value: &impl serde::Serialize) -> std::io::Result<()> { - serde_json::to_writer(&mut *self.output, value)?; - self.output.write_all(b"\n")?; - self.output.flush() + /// Resets the tracer to its initial state of [`Self::new`]. + /// + /// This makes the inspector ready to be used again. + pub fn clear(&mut self) { + let Self { + gas_inspector, + stack, + pc, + opcode, + gas, + refunded, + mem_size, + skip, + .. + } = self; + *gas_inspector = GasInspector::new(); + stack.clear(); + *pc = 0; + *opcode = 0; + *gas = 0; + *refunded = 0; + *mem_size = 0; + *skip = false; } fn print_summary(&mut self, result: &InterpreterResult, context: &mut CTX) { @@ -185,7 +192,7 @@ where let value = Summary { state_root: B256::ZERO.to_string(), output: result.output.to_string(), - gas_used: hex_number(gas_limit - self.gas_inspector.gas_remaining()), + gas_used: gas_limit - self.gas_inspector.gas_remaining(), pass: result.is_ok(), time: None, fork: Some(spec.to_string()), @@ -193,15 +200,19 @@ where let _ = self.write_value(&value); } } + + fn write_value(&mut self, value: &impl serde::Serialize) -> std::io::Result<()> { + write_value(&mut *self.output, value) + } } pub trait CloneStack { - fn clone_from(&self) -> Vec; + fn clone_into(&self, stack: &mut Vec); } impl CloneStack for Stack { - fn clone_from(&self) -> Vec { - self.data().to_vec() + fn clone_into(&self, stack: &mut Vec) { + stack.extend_from_slice(self.data()); } } @@ -216,10 +227,11 @@ where fn step(&mut self, interp: &mut Interpreter, _: &mut CTX) { self.gas_inspector.step(interp.control.gas()); - self.stack = interp.stack.clone_from(); + self.stack.clear(); + interp.stack.clone_into(&mut self.stack); self.memory = if self.include_memory { Some(hex::encode_prefixed( - interp.memory.slice(0..usize::MAX).as_ref(), + interp.memory.slice(0..interp.memory.size()).as_ref(), )) } else { None @@ -252,14 +264,14 @@ where pc: self.pc, section: self.section, op: self.opcode, - gas: hex_number(self.gas), - gas_cost: hex_number(self.gas_inspector.last_gas_cost()), - stack: self.stack.iter().map(hex_number_u256).collect(), + gas: self.gas, + gas_cost: self.gas_inspector.last_gas_cost(), + stack: &self.stack, depth: context.journal().depth() as u64, function_depth: self.function_depth, - return_data: "0x".to_string(), - refund: hex_number(self.refunded as u64), - mem_size: self.mem_size.to_string(), + return_data: "0x", + refund: self.refunded as u64, + mem_size: self.mem_size as u64, op_name: OpCode::new(self.opcode).map(|i| i.as_str()), error: if !interp.control.instruction_result().is_ok() { @@ -271,7 +283,7 @@ where storage: None, return_stack: None, }; - let _ = self.write_value(&value); + let _ = write_value(&mut self.output, &value); } fn call_end(&mut self, context: &mut CTX, _: &CallInputs, outcome: &mut CallOutcome) { @@ -279,7 +291,7 @@ where if context.journal().depth() == 0 { self.print_summary(&outcome.result, context); - // Clear the state if we are at the top level + // Clear the state if we are at the top level. self.clear(); } } @@ -289,23 +301,20 @@ where if context.journal().depth() == 0 { self.print_summary(&outcome.result, context); - - // Clear the state if we are at the top level + // Clear the state if we are at the top level. self.clear(); } } } -fn hex_number(uint: u64) -> String { - format!("0x{uint:x}") +fn write_value( + output: &mut dyn std::io::Write, + value: &impl serde::Serialize, +) -> std::io::Result<()> { + serde_json::to_writer(&mut *output, value)?; + output.write_all(b"\n") } -fn hex_number_u256(b: &U256) -> String { - let s = hex::encode(b.to_be_bytes::<32>()); - let s = s.trim_start_matches('0'); - if s.is_empty() { - "0x0".to_string() - } else { - format!("0x{s}") - } +fn serde_hex_u64(n: &u64, serializer: S) -> Result { + serializer.serialize_str(&format!("{:#x}", *n)) }