From 83ccdf381d21793e472a0f1a4b115928cea86cce Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 6 Dec 2022 22:36:59 -0700 Subject: [PATCH 1/9] EIP 3155 tracing Partial implementation of 3155 tracing. --- .gitignore | 1 + Cargo.lock | 1 + bins/revme/Cargo.toml | 2 +- bins/revme/src/lib.rs | 1 + bins/revme/src/main.rs | 1 + bins/revme/src/statetest/cmd.rs | 4 +- bins/revme/src/statetest/mod.rs | 1 - bins/revme/src/statetest/runner.rs | 37 ++++-- bins/revme/src/statetest/trace.rs | 141 ---------------------- bins/revme/src/tracer_eip3155.rs | 188 +++++++++++++++++++++++++++++ crates/revm/src/inspector.rs | 11 ++ 11 files changed, 233 insertions(+), 155 deletions(-) delete mode 100644 bins/revme/src/statetest/trace.rs create mode 100644 bins/revme/src/tracer_eip3155.rs diff --git a/.gitignore b/.gitignore index 1a2e291d3b..3831db64e9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target .vscode +.idea pkg/ diff --git a/Cargo.lock b/Cargo.lock index a756805ad3..f819280aa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1783,6 +1783,7 @@ version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ + "indexmap", "itoa", "ryu", "serde", diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index f129efd90c..d64ed8dc90 100644 --- a/bins/revme/Cargo.toml +++ b/bins/revme/Cargo.toml @@ -26,7 +26,7 @@ revm = { path = "../../crates/revm", version = "2.3.1", default-features = false rlp = { version = "0.5", default-features = false } ruint = { version = "1.7.0", features = ["rlp", "serde"] } serde = { version = "1.0", features = ["derive", "rc"] } -serde_json = "1.0" +serde_json = { version = "1.0", features = ["preserve_order"] } sha3 = { version = "0.10", default-features = false } structopt = "0.3" thiserror = "1.0" diff --git a/bins/revme/src/lib.rs b/bins/revme/src/lib.rs index dd3b8b28c8..a05f4b554a 100644 --- a/bins/revme/src/lib.rs +++ b/bins/revme/src/lib.rs @@ -1 +1,2 @@ pub mod statetest; +pub mod tracer_eip3155; diff --git a/bins/revme/src/main.rs b/bins/revme/src/main.rs index 70a2999c67..10c4d01f92 100644 --- a/bins/revme/src/main.rs +++ b/bins/revme/src/main.rs @@ -2,6 +2,7 @@ mod cmd; mod exec; mod runner; mod statetest; +pub mod tracer_eip3155; use cmd::Error; use structopt::StructOpt; mod cli_env; diff --git a/bins/revme/src/statetest/cmd.rs b/bins/revme/src/statetest/cmd.rs index 4ca4b588ae..f66aa6d706 100644 --- a/bins/revme/src/statetest/cmd.rs +++ b/bins/revme/src/statetest/cmd.rs @@ -9,6 +9,8 @@ pub struct Cmd { path: Vec, #[structopt(short = "s", long)] single_thread: bool, + #[structopt(long)] + json: bool, } impl Cmd { @@ -16,7 +18,7 @@ impl Cmd { for path in &self.path { println!("Start running tests on: {path:?}"); let test_files = find_all_json_tests(path); - run(test_files, self.single_thread)? + run(test_files, self.single_thread, self.json)? } Ok(()) } diff --git a/bins/revme/src/statetest/mod.rs b/bins/revme/src/statetest/mod.rs index 905334ec68..1a971832cd 100644 --- a/bins/revme/src/statetest/mod.rs +++ b/bins/revme/src/statetest/mod.rs @@ -2,7 +2,6 @@ mod cmd; pub mod merkle_trie; pub mod models; mod runner; -mod trace; pub use cmd::Cmd; pub use runner::TestError as Error; diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index 09cf5eff81..0871a033c1 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -1,3 +1,5 @@ +use std::io::stdout; +use std::sync::atomic::Ordering; use std::{ collections::HashMap, ffi::OsStr, @@ -6,23 +8,25 @@ use std::{ time::{Duration, Instant}, }; +use hex_literal::hex; use indicatif::ProgressBar; +use thiserror::Error; +use walkdir::{DirEntry, WalkDir}; + +use revm::common::keccak256; use revm::{ bits::{B160, B256}, db::AccountState, Bytecode, CreateScheme, Env, ExecutionResult, SpecId, TransactTo, U256, }; -use std::sync::atomic::Ordering; -use walkdir::{DirEntry, WalkDir}; +use tracer_eip3155::TracerEip3155; + +use crate::tracer_eip3155; use super::{ merkle_trie::{log_rlp_hash, state_merkle_trie_root}, models::{SpecName, TestSuit}, - trace::CustomPrintTracer, }; -use hex_literal::hex; -use revm::common::keccak256; -use thiserror::Error; #[derive(Debug, Error)] pub enum TestError { @@ -50,7 +54,11 @@ pub fn find_all_json_tests(path: &Path) -> Vec { .collect::>() } -pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result<(), TestError> { +pub fn execute_test_suit( + path: &Path, + elapsed: &Arc>, + trace: bool, +) -> Result<(), TestError> { // funky test with `bigint 0x00` value in json :) not possible to happen on mainnet and require custom json parser. // https://github.com/ethereum/tests/issues/971 if path.file_name() == Some(OsStr::new("ValueOverflow.json")) { @@ -241,13 +249,20 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< // do the deed let timer = Instant::now(); + let mut exec_result: ExecutionResult; + if trace { + exec_result = + evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); + } else { + exec_result = evm.transact_commit(); + }; let ExecutionResult { exit_reason, gas_used, gas_refunded, logs, .. - } = evm.transact_commit(); + } = exec_result; let timer = timer.elapsed(); *elapsed.lock().unwrap() += timer; @@ -273,7 +288,7 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< ); let mut database_cloned = database.clone(); evm.database(&mut database_cloned); - evm.inspect_commit(CustomPrintTracer::new()); + evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); let db = evm.db().unwrap(); println!("{path:?} UNIT_TEST:{name}\n"); println!( @@ -295,7 +310,7 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< Ok(()) } -pub fn run(test_files: Vec, single_thread: bool) -> Result<(), TestError> { +pub fn run(test_files: Vec, single_thread: bool, trace: bool) -> Result<(), TestError> { let endjob = Arc::new(AtomicBool::new(false)); let console_bar = Arc::new(ProgressBar::new(test_files.len() as u64)); let mut joins: Vec>> = Vec::new(); @@ -325,7 +340,7 @@ pub fn run(test_files: Vec, single_thread: bool) -> Result<(), TestErro return Ok(()); } //println!("Test:{:?}\n",test_path); - if let Err(err) = execute_test_suit(&test_path, &elapsed) { + if let Err(err) = execute_test_suit(&test_path, &elapsed, trace) { endjob.store(true, Ordering::SeqCst); println!("Test[{index}] named:\n{test_path:?} failed: {err}\n"); return Err(err); diff --git a/bins/revme/src/statetest/trace.rs b/bins/revme/src/statetest/trace.rs deleted file mode 100644 index a70b319159..0000000000 --- a/bins/revme/src/statetest/trace.rs +++ /dev/null @@ -1,141 +0,0 @@ -use bytes::Bytes; -pub use revm::Inspector; -use revm::{ - bits::B160, - opcode::{self}, - CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, Return, -}; -#[derive(Clone)] -pub struct CustomPrintTracer { - gas_inspector: GasInspector, -} - -impl CustomPrintTracer { - pub fn new() -> Self { - Self { - gas_inspector: GasInspector::default(), - } - } -} - -impl Inspector for CustomPrintTracer { - fn initialize_interp( - &mut self, - interp: &mut revm::Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> Return { - self.gas_inspector - .initialize_interp(interp, data, is_static); - Return::Continue - } - - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - fn step( - &mut self, - interp: &mut revm::Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> Return { - let opcode = interp.current_opcode(); - let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; - - let gas_remaining = self.gas_inspector.gas_remaining(); - - println!( - "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data size:{}", - data.journaled_state.depth(), - interp.program_counter(), - gas_remaining, - gas_remaining, - opcode_str.unwrap(), - opcode, - interp.gas.refunded(), - interp.gas.refunded(), - interp.stack.data(), - interp.memory.data().len(), - ); - - self.gas_inspector.step(interp, data, is_static); - - Return::Continue - } - - fn step_end( - &mut self, - interp: &mut revm::Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - eval: revm::Return, - ) -> Return { - self.gas_inspector.step_end(interp, data, is_static, eval); - Return::Continue - } - - fn call_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: Return, - out: Bytes, - is_static: bool, - ) -> (Return, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone(), is_static); - (ret, remaining_gas, out) - } - - fn create_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - ret: Return, - address: Option, - remaining_gas: Gas, - out: Bytes, - ) -> (Return, Option, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, ret, address, remaining_gas, out.clone()); - (ret, address, remaining_gas, out) - } - - fn call( - &mut self, - _data: &mut EVMData<'_, DB>, - inputs: &mut CallInputs, - is_static: bool, - ) -> (Return, Gas, Bytes) { - println!( - "SM CALL: {:?},context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", - inputs.contract, - inputs.context, - is_static, - inputs.transfer, - inputs.input.len(), - ); - (Return::Continue, Gas::new(0), Bytes::new()) - } - - fn create( - &mut self, - _data: &mut EVMData<'_, DB>, - inputs: &mut CreateInputs, - ) -> (Return, Option, Gas, Bytes) { - println!( - "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", - inputs.caller, - inputs.scheme, - inputs.value, - hex::encode(&inputs.init_code), - inputs.gas_limit - ); - (Return::Continue, None, Gas::new(0), Bytes::new()) - } - - fn selfdestruct(&mut self) { - //, address: B160, target: B160) { - println!("SELFDESTRUCT on "); //{:?} target: {:?}", address, target); - } -} diff --git a/bins/revme/src/tracer_eip3155.rs b/bins/revme/src/tracer_eip3155.rs new file mode 100644 index 0000000000..a168ff0b52 --- /dev/null +++ b/bins/revme/src/tracer_eip3155.rs @@ -0,0 +1,188 @@ +// https://eips.ethereum.org/EIPS/eip-3155 + +use std::io::Write; + +use bytes::Bytes; +use ruint::aliases::U256; +use serde_json::json; + +pub use revm::Inspector; +use revm::{ + opcode::{self}, + CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, Interpreter, Memory, Return, + Stack, B160, +}; + +pub struct TracerEip3155 { + output: Box, + gas_inspector: GasInspector, + + trace_mem: bool, + trace_return_data: bool, + + stack: Stack, + pc: usize, + opcode: u8, + gas: u64, + mem_size: usize, + memory: Option, + skip: bool, +} + +impl TracerEip3155 { + pub fn new(output: Box, trace_mem: bool, trace_return_data: bool) -> Self { + Self { + output, + gas_inspector: GasInspector::default(), + trace_mem, + trace_return_data, + stack: Stack::new(), + pc: 0, + opcode: 0, + gas: 0, + mem_size: 0, + memory: None, + skip: false, + } + } +} + +impl Inspector for TracerEip3155 { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + data: &mut EVMData<'_, DB>, + is_static: bool, + ) -> Return { + self.gas_inspector + .initialize_interp(interp, data, is_static); + Return::Continue + } + + // get opcode by calling `interp.contract.opcode(interp.program_counter())`. + // all other information can be obtained from interp. + fn step( + &mut self, + interp: &mut Interpreter, + data: &mut EVMData<'_, DB>, + is_static: bool, + ) -> Return { + self.gas_inspector.step(interp, data, is_static); + self.stack = interp.stack.clone(); + self.pc = interp.program_counter(); + self.opcode = interp.current_opcode(); + self.mem_size = interp.memory.len(); + self.gas = self.gas_inspector.gas_remaining(); + // + Return::Continue + } + + fn step_end( + &mut self, + interp: &mut Interpreter, + data: &mut EVMData<'_, DB>, + is_static: bool, + eval: Return, + ) -> Return { + self.gas_inspector.step_end(interp, data, is_static, eval); + if self.skip { + self.skip = false; + return Return::Continue; + }; + + self.print_log_line(data.journaled_state.depth()); + Return::Continue + } + + fn call( + &mut self, + data: &mut EVMData<'_, DB>, + _inputs: &mut CallInputs, + _is_static: bool, + ) -> (Return, Gas, Bytes) { + self.print_log_line(data.journaled_state.depth()); + (Return::Continue, Gas::new(0), Bytes::new()) + } + + fn call_end( + &mut self, + data: &mut EVMData<'_, DB>, + inputs: &CallInputs, + remaining_gas: Gas, + ret: Return, + out: Bytes, + is_static: bool, + ) -> (Return, Gas, Bytes) { + self.gas_inspector + .call_end(data, inputs, remaining_gas, ret, out.clone(), is_static); + // self.log_step(interp, data, is_static, eval); + self.skip = true; + if data.journaled_state.depth() == 0 { + let log_line = json!({ + //stateroot + "output": format!("{:?}", out), + "gasUser": format!("0x{:x}", self.gas_inspector.gas_remaining()), + //time + //fork + }); + + writeln!( + self.output, + "{:?}", + serde_json::to_string(&log_line).unwrap() + ) + .expect("If output fails we can ignore the logging"); + } + (ret, remaining_gas, out) + } + + fn create_end( + &mut self, + data: &mut EVMData<'_, DB>, + inputs: &CreateInputs, + ret: Return, + address: Option, + remaining_gas: Gas, + out: Bytes, + ) -> (Return, Option, Gas, Bytes) { + self.gas_inspector + .create_end(data, inputs, ret, address, remaining_gas, out.clone()); + (ret, address, remaining_gas, out) + } +} + +impl TracerEip3155 { + fn print_log_line(&mut self, depth: u64) { + let short_stack: Vec = self.stack.data().iter().map(|&b| short_hex(b)).collect(); + let log_line = json!({ + "pc": self.pc, + "op": self.opcode, + "gas": format!("0x{:x}", self.gas), + "gasCost": format!("0x{:x}", self.gas_inspector.last_gas_cost()), + //memory? + "memSize": self.mem_size, + "stack": short_stack, + "depth": depth, + //returnData + //refund + "opName": opcode::OPCODE_JUMPMAP[self.opcode as usize], + //error + //storage + //returnStack + }); + + writeln!(self.output, "{}", serde_json::to_string(&log_line).unwrap()) + .expect("If output fails we can ignore the logging"); + } +} + +fn short_hex(b: U256) -> String { + let s = hex::encode(b.to_be_bytes_vec()) + .trim_start_matches('0') + .to_string(); + if s.len() == 0 { + "0x0".to_string() + } else { + format!("0x{}", s) + } +} diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 9b85db0975..8fc3474821 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -135,12 +135,17 @@ pub struct GasInspector { was_jumpi: Option, gas_remaining: u64, + last_gas_cost: u64, } impl GasInspector { pub fn gas_remaining(&self) -> u64 { self.gas_remaining } + + pub fn last_gas_cost(&self) -> u64 { + self.last_gas_cost + } } impl Inspector for GasInspector { @@ -206,7 +211,13 @@ impl Inspector for GasInspector { self.was_return = false; } + let last_gas = self.gas_remaining; self.gas_remaining = interp.gas.remaining() + self.full_gas_block - self.reduced_gas_block; + if last_gas > self.gas_remaining { + self.last_gas_cost = last_gas - self.gas_remaining; + } else { + self.last_gas_cost = 0; + } Return::Continue } From 9ba35952fddfce24f100d4bcf583a7badca2b0d5 Mon Sep 17 00:00:00 2001 From: pistomat Date: Thu, 2 Feb 2023 23:47:23 +0100 Subject: [PATCH 2/9] fix lint issues --- bins/revme/src/statetest/runner.rs | 11 ++++------- bins/revme/src/tracer_eip3155.rs | 9 ++++++--- crates/revm/src/bits.rs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index 0871a033c1..46a647ab3c 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -249,12 +249,10 @@ pub fn execute_test_suit( // do the deed let timer = Instant::now(); - let mut exec_result: ExecutionResult; - if trace { - exec_result = - evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); + let exec_result: ExecutionResult = if trace { + evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) } else { - exec_result = evm.transact_commit(); + evm.transact_commit() }; let ExecutionResult { exit_reason, @@ -292,8 +290,7 @@ pub fn execute_test_suit( let db = evm.db().unwrap(); println!("{path:?} UNIT_TEST:{name}\n"); println!( - "failed reason: {:?} {:?} UNIT_TEST:{}\n gas:{:?} ({:?} refunded)", - exit_reason, path, name, gas_used, gas_refunded, + "failed reason: {exit_reason:?} {path:?} UNIT_TEST:{name}\n gas:{gas_used:?} ({gas_refunded:?} refunded)", ); println!("\nApplied state:{db:?}\n"); println!("\nStateroot: {state_root:?}\n"); diff --git a/bins/revme/src/tracer_eip3155.rs b/bins/revme/src/tracer_eip3155.rs index a168ff0b52..8909fc5b26 100644 --- a/bins/revme/src/tracer_eip3155.rs +++ b/bins/revme/src/tracer_eip3155.rs @@ -17,7 +17,9 @@ pub struct TracerEip3155 { output: Box, gas_inspector: GasInspector, + #[allow(dead_code)] trace_mem: bool, + #[allow(dead_code)] trace_return_data: bool, stack: Stack, @@ -25,6 +27,7 @@ pub struct TracerEip3155 { opcode: u8, gas: u64, mem_size: usize, + #[allow(dead_code)] memory: Option, skip: bool, } @@ -120,7 +123,7 @@ impl Inspector for TracerEip3155 { if data.journaled_state.depth() == 0 { let log_line = json!({ //stateroot - "output": format!("{:?}", out), + "output": format!("{out:?}"), "gasUser": format!("0x{:x}", self.gas_inspector.gas_remaining()), //time //fork @@ -180,9 +183,9 @@ fn short_hex(b: U256) -> String { let s = hex::encode(b.to_be_bytes_vec()) .trim_start_matches('0') .to_string(); - if s.len() == 0 { + if s.is_empty() { "0x0".to_string() } else { - format!("0x{}", s) + format!("0x{s}") } } diff --git a/crates/revm/src/bits.rs b/crates/revm/src/bits.rs index 8672a93ddc..717c55415e 100644 --- a/crates/revm/src/bits.rs +++ b/crates/revm/src/bits.rs @@ -148,7 +148,7 @@ mod serialize { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Self::InvalidHex { character, index } => { - write!(fmt, "invalid hex character: {}, at {}", character, index) + write!(fmt, "invalid hex character: {character}, at {index}") } } } From 85270e4777daddcc2abf44eea8636f3d380fe231 Mon Sep 17 00:00:00 2001 From: pistomat Date: Thu, 2 Feb 2023 23:24:24 +0100 Subject: [PATCH 3/9] restructure tracer eip3155 --- Cargo.lock | 1 + bins/revme/src/lib.rs | 1 - bins/revme/src/main.rs | 1 - bins/revme/src/statetest/runner.rs | 53 ++-- crates/revm/Cargo.toml | 1 + crates/revm/src/inspector.rs | 288 +----------------- crates/revm/src/inspector/gas.rs | 12 + .../revm/src/inspector}/tracer_eip3155.rs | 53 ++-- 8 files changed, 78 insertions(+), 332 deletions(-) rename {bins/revme/src => crates/revm/src/inspector}/tracer_eip3155.rs (80%) diff --git a/Cargo.lock b/Cargo.lock index ef85e89b0f..8f478c2005 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1579,6 +1579,7 @@ dependencies = [ "revm-interpreter", "revm-precompile", "serde", + "serde_json", "tokio", ] diff --git a/bins/revme/src/lib.rs b/bins/revme/src/lib.rs index a05f4b554a..dd3b8b28c8 100644 --- a/bins/revme/src/lib.rs +++ b/bins/revme/src/lib.rs @@ -1,2 +1 @@ pub mod statetest; -pub mod tracer_eip3155; diff --git a/bins/revme/src/main.rs b/bins/revme/src/main.rs index 10c4d01f92..70a2999c67 100644 --- a/bins/revme/src/main.rs +++ b/bins/revme/src/main.rs @@ -2,7 +2,6 @@ mod cmd; mod exec; mod runner; mod statetest; -pub mod tracer_eip3155; use cmd::Error; use structopt::StructOpt; mod cli_env; diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index fb08f27881..ce63a8ad6a 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -1,5 +1,4 @@ use std::io::stdout; -use std::sync::atomic::Ordering; use std::{ collections::HashMap, ffi::OsStr, @@ -8,21 +7,16 @@ use std::{ time::{Duration, Instant}, }; -use hex_literal::hex; use indicatif::ProgressBar; -use thiserror::Error; -use walkdir::{DirEntry, WalkDir}; -use revm::common::keccak256; +use revm::inspectors::TracerEip3155; use revm::{ db::AccountState, - inspectors::CustomPrintTracer, interpreter::CreateScheme, primitives::{Bytecode, Env, ExecutionResult, SpecId, TransactTo, B160, B256, U256}, }; -use tracer_eip3155::TracerEip3155; - -use crate::tracer_eip3155; +use std::sync::atomic::Ordering; +use walkdir::{DirEntry, WalkDir}; use super::{ merkle_trie::{log_rlp_hash, state_merkle_trie_root}, @@ -259,18 +253,12 @@ pub fn execute_test_suit( // do the deed let timer = Instant::now(); - let exec_result: ExecutionResult = if trace { + + let exec_result = if trace { evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) } else { evm.transact_commit() }; - let ExecutionResult { - exit_reason, - gas_used, - gas_refunded, - logs, - .. - } = exec_result; let timer = timer.elapsed(); *elapsed.lock().unwrap() += timer; @@ -291,7 +279,7 @@ pub fn execute_test_suit( }) .map(|(k, v)| (*k, v.clone())), ); - let logs = match &out { + let logs = match &exec_result { Ok(ExecutionResult::Success { logs, .. }) => logs.clone(), _ => Vec::new(), }; @@ -303,12 +291,33 @@ pub fn execute_test_suit( ); let mut database_cloned = database.clone(); evm.database(&mut database_cloned); - let _ = evm.inspect_commit(TracerEip3155::new(Box::default(stdout()), false, false)); + let _ = + evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); let db = evm.db().unwrap(); println!("{path:?} UNIT_TEST:{name}\n"); - println!( - "failed reason: {exit_reason:?} {path:?} UNIT_TEST:{name}\n gas:{gas_used:?} ({gas_refunded:?} refunded)", - ); + match &exec_result { + Ok(ExecutionResult::Success { + reason, + gas_used, + gas_refunded, + .. + }) => { + println!("Failed reason: {reason:?} {path:?} UNIT_TEST:{name}\n gas:{gas_used:?} ({gas_refunded:?} refunded)"); + } + Ok(ExecutionResult::Revert { gas_used, output }) => { + println!( + "Reverted: {output:?} {path:?} UNIT_TEST:{name}\n gas:{gas_used:?}" + ); + } + Ok(ExecutionResult::Halt { reason, gas_used }) => { + println!( + "Halted: {reason:?} {path:?} UNIT_TEST:{name}\n gas:{gas_used:?}" + ); + } + Err(out) => { + println!("Output: {out:?} {path:?} UNIT_TEST:{name}\n"); + } + } println!("\nApplied state:{db:?}\n"); println!("\nStateroot: {state_root:?}\n"); return Err(TestError::RootMissmatch { diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 300e9d47ee..9f6d467913 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -17,6 +17,7 @@ auto_impl = { version = "1.0", default-features = false } # Optional serde = { version = "1.0", features = ["derive", "rc"], optional = true } +serde_json = { version = "1.0", features = ["preserve_order"] } # ethersdb tokio = { version = "1.23", features = [ diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index a83d96f307..0801fb5d22 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -8,6 +8,7 @@ use auto_impl::auto_impl; pub mod customprinter; pub mod gas; pub mod noop; +pub mod tracer_eip3155; /// All Inspectors implementations that revm has. pub mod inspectors { @@ -15,6 +16,7 @@ pub mod inspectors { pub use super::customprinter::CustomPrintTracer; pub use super::gas::GasInspector; pub use super::noop::NoOpInspector; + pub use super::tracer_eip3155::TracerEip3155; } #[auto_impl(&mut, Box)] @@ -135,289 +137,3 @@ pub trait Inspector { /// Called when a contract has been self-destructed. fn selfdestruct(&mut self) {} } - -#[derive(Clone, Copy)] -pub struct NoOpInspector(); - -impl Inspector for NoOpInspector {} - -#[derive(Clone, Copy, Debug, Default)] -pub struct GasInspector { - /// We now batch continual gas_block in one go, that means we need to reduce it if we want - /// to get correct gas remaining. Check revm/interp/contract/analyze for more information - reduced_gas_block: u64, - full_gas_block: u64, - was_return: bool, - was_jumpi: Option, - - gas_remaining: u64, - last_gas_cost: u64, -} - -impl GasInspector { - pub fn gas_remaining(&self) -> u64 { - self.gas_remaining - } - - pub fn last_gas_cost(&self) -> u64 { - self.last_gas_cost - } -} - -impl Inspector for GasInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - _data: &mut EVMData<'_, DB>, - _is_static: bool, - ) -> Return { - self.full_gas_block = interp.contract.first_gas_block(); - self.gas_remaining = interp.gas.limit(); - Return::Continue - } - - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - fn step( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - _is_static: bool, - ) -> Return { - let op = interp.current_opcode(); - - // calculate gas_block - let infos = spec_opcode_gas(data.env.cfg.spec_id); - let info = &infos[op as usize]; - - let pc = interp.program_counter(); - if op == opcode::JUMPI { - self.reduced_gas_block += info.get_gas() as u64; - self.was_jumpi = Some(pc); - } else if info.is_gas_block_end() { - self.reduced_gas_block = 0; - self.full_gas_block = interp.contract.gas_block(pc); - } else { - self.reduced_gas_block += info.get_gas() as u64; - } - - Return::Continue - } - - fn step_end( - &mut self, - interp: &mut Interpreter, - _data: &mut EVMData<'_, DB>, - _is_static: bool, - _eval: Return, - ) -> Return { - let pc = interp.program_counter(); - if let Some(was_pc) = self.was_jumpi { - if let Some(new_pc) = pc.checked_sub(1) { - if was_pc == new_pc { - self.reduced_gas_block = 0; - self.full_gas_block = interp.contract.gas_block(was_pc); - } - } - self.was_jumpi = None; - } else if self.was_return { - // we are ok to decrement PC by one as it is return of call - let previous_pc = pc - 1; - self.full_gas_block = interp.contract.gas_block(previous_pc); - self.was_return = false; - } - - let last_gas = self.gas_remaining; - self.gas_remaining = interp.gas.remaining() + self.full_gas_block - self.reduced_gas_block; - if last_gas > self.gas_remaining { - self.last_gas_cost = last_gas - self.gas_remaining; - } else { - self.last_gas_cost = 0; - } - - Return::Continue - } - - fn call_end( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CallInputs, - remaining_gas: Gas, - ret: Return, - out: Bytes, - _is_static: bool, - ) -> (Return, Gas, Bytes) { - self.was_return = true; - (ret, remaining_gas, out) - } - - fn create_end( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CreateInputs, - ret: Return, - address: Option, - remaining_gas: Gas, - out: Bytes, - ) -> (Return, Option, Gas, Bytes) { - self.was_return = true; - (ret, address, remaining_gas, out) - } -} - -#[cfg(test)] -mod tests { - use crate::db::BenchmarkDB; - use crate::{ - opcode, Bytecode, CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, - Inspector, Interpreter, OpCode, Return, TransactTo, B160, B256, - }; - use bytes::Bytes; - use hex_literal::hex; - - #[derive(Default, Debug)] - struct StackInspector { - pc: usize, - gas_inspector: GasInspector, - gas_remaining_steps: Vec<(usize, u64)>, - } - - impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> Return { - self.gas_inspector - .initialize_interp(interp, data, is_static); - Return::Continue - } - - fn step( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - ) -> Return { - self.pc = interp.program_counter(); - self.gas_inspector.step(interp, data, is_static); - Return::Continue - } - - fn log( - &mut self, - evm_data: &mut EVMData<'_, DB>, - address: &B160, - topics: &[B256], - data: &Bytes, - ) { - self.gas_inspector.log(evm_data, address, topics, data); - } - - fn step_end( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - is_static: bool, - eval: Return, - ) -> Return { - self.gas_inspector.step_end(interp, data, is_static, eval); - self.gas_remaining_steps - .push((self.pc, self.gas_inspector.gas_remaining())); - eval - } - - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - is_static: bool, - ) -> (Return, Gas, Bytes) { - self.gas_inspector.call(data, call, is_static); - - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) - } - - fn call_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: Return, - out: Bytes, - is_static: bool, - ) -> (Return, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone(), is_static); - (ret, remaining_gas, out) - } - - fn create( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (Return, Option, Gas, Bytes) { - self.gas_inspector.create(data, call); - - ( - Return::Continue, - None, - Gas::new(call.gas_limit), - Bytes::new(), - ) - } - - fn create_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - status: Return, - address: Option, - gas: Gas, - retdata: Bytes, - ) -> (Return, Option, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, status, address, gas, retdata.clone()); - (status, address, gas, retdata) - } - } - - #[test] - fn test_gas_inspector() { - let contract_data: Bytes = Bytes::from(vec![ - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0xb, - opcode::JUMPI, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::JUMPDEST, - opcode::STOP, - ]); - let bytecode = Bytecode::new_raw(contract_data); - - let mut evm = crate::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - evm.env.tx.caller = B160(hex!("1000000000000000000000000000000000000000")); - evm.env.tx.transact_to = - TransactTo::Call(B160(hex!("0000000000000000000000000000000000000000"))); - evm.env.tx.gas_limit = 21100; - - let mut inspector = StackInspector::default(); - let (result, state) = evm.inspect(&mut inspector); - println!("{result:?} {state:?} {inspector:?}"); - - for (pc, gas) in inspector.gas_remaining_steps { - println!( - "{pc} {} {gas:?}", - OpCode::try_from_u8(bytecode.bytes()[pc]).unwrap().as_str(), - ); - } - } -} diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 5e7a89f417..66330e76a2 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -14,12 +14,17 @@ pub struct GasInspector { was_jumpi: Option, gas_remaining: u64, + last_gas_cost: u64, } impl GasInspector { pub fn gas_remaining(&self) -> u64 { self.gas_remaining } + + pub fn last_gas_cost(&self) -> u64 { + self.last_gas_cost + } } impl Inspector for GasInspector { @@ -89,8 +94,15 @@ impl Inspector for GasInspector { self.full_gas_block = interp.contract.gas_block(previous_pc); self.was_return = false; } + + let last_gas = self.gas_remaining; self.gas_remaining = interp.gas.remaining() + (self.full_gas_block - self.reduced_gas_block); + if last_gas > self.gas_remaining { + self.last_gas_cost = last_gas - self.gas_remaining; + } else { + self.last_gas_cost = 0; + } InstructionResult::Continue } diff --git a/bins/revme/src/tracer_eip3155.rs b/crates/revm/src/inspector/tracer_eip3155.rs similarity index 80% rename from bins/revme/src/tracer_eip3155.rs rename to crates/revm/src/inspector/tracer_eip3155.rs index 8909fc5b26..44c3363023 100644 --- a/bins/revme/src/tracer_eip3155.rs +++ b/crates/revm/src/inspector/tracer_eip3155.rs @@ -2,16 +2,25 @@ use std::io::Write; -use bytes::Bytes; -use ruint::aliases::U256; +use revm_interpreter::primitives::U256; +use revm_interpreter::{opcode, Interpreter, Memory, Stack}; + +use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; +use crate::primitives::{db::Database, hex, Bytes, B160}; +use crate::{evm_impl::EVMData, Inspector}; + +use crate::inspectors::GasInspector; + +// use bytes::Bytes; +// use ruint::aliases::U256; use serde_json::json; -pub use revm::Inspector; -use revm::{ - opcode::{self}, - CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, Interpreter, Memory, Return, - Stack, B160, -}; +// pub use revm::Inspector; +// use revm::{ +// opcode::{self}, +// CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, Interpreter, Memory, Return, +// Stack, B160, +// }; pub struct TracerEip3155 { output: Box, @@ -56,10 +65,10 @@ impl Inspector for TracerEip3155 { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { self.gas_inspector .initialize_interp(interp, data, is_static); - Return::Continue + InstructionResult::Continue } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. @@ -69,7 +78,7 @@ impl Inspector for TracerEip3155 { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { self.gas_inspector.step(interp, data, is_static); self.stack = interp.stack.clone(); self.pc = interp.program_counter(); @@ -77,7 +86,7 @@ impl Inspector for TracerEip3155 { self.mem_size = interp.memory.len(); self.gas = self.gas_inspector.gas_remaining(); // - Return::Continue + InstructionResult::Continue } fn step_end( @@ -85,16 +94,16 @@ impl Inspector for TracerEip3155 { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - eval: Return, - ) -> Return { + eval: InstructionResult, + ) -> InstructionResult { self.gas_inspector.step_end(interp, data, is_static, eval); if self.skip { self.skip = false; - return Return::Continue; + return InstructionResult::Continue; }; self.print_log_line(data.journaled_state.depth()); - Return::Continue + InstructionResult::Continue } fn call( @@ -102,9 +111,9 @@ impl Inspector for TracerEip3155 { data: &mut EVMData<'_, DB>, _inputs: &mut CallInputs, _is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.print_log_line(data.journaled_state.depth()); - (Return::Continue, Gas::new(0), Bytes::new()) + (InstructionResult::Continue, Gas::new(0), Bytes::new()) } fn call_end( @@ -112,10 +121,10 @@ impl Inspector for TracerEip3155 { data: &mut EVMData<'_, DB>, inputs: &CallInputs, remaining_gas: Gas, - ret: Return, + ret: InstructionResult, out: Bytes, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.gas_inspector .call_end(data, inputs, remaining_gas, ret, out.clone(), is_static); // self.log_step(interp, data, is_static, eval); @@ -143,11 +152,11 @@ impl Inspector for TracerEip3155 { &mut self, data: &mut EVMData<'_, DB>, inputs: &CreateInputs, - ret: Return, + ret: InstructionResult, address: Option, remaining_gas: Gas, out: Bytes, - ) -> (Return, Option, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { self.gas_inspector .create_end(data, inputs, ret, address, remaining_gas, out.clone()); (ret, address, remaining_gas, out) From 3a61276d95b2adc8360a756c43b55bb2b9760712 Mon Sep 17 00:00:00 2001 From: pistomat Date: Mon, 13 Feb 2023 11:48:43 +0100 Subject: [PATCH 4/9] Update crates/revm/Cargo.toml Co-authored-by: rakita --- crates/revm/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 9f6d467913..946f05d594 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -17,7 +17,7 @@ auto_impl = { version = "1.0", default-features = false } # Optional serde = { version = "1.0", features = ["derive", "rc"], optional = true } -serde_json = { version = "1.0", features = ["preserve_order"] } +serde_json = { version = "1.0", features = ["preserve_order"], optional = true } # ethersdb tokio = { version = "1.23", features = [ From 301a0639724af7fd7080d12f26e86b5c3cb9a457 Mon Sep 17 00:00:00 2001 From: pistomat Date: Mon, 13 Feb 2023 11:48:54 +0100 Subject: [PATCH 5/9] Update crates/revm/src/inspector.rs Co-authored-by: rakita --- crates/revm/src/inspector.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 0801fb5d22..a0cb734c9a 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -8,6 +8,7 @@ use auto_impl::auto_impl; pub mod customprinter; pub mod gas; pub mod noop; +#[cfg(feature = "std")] pub mod tracer_eip3155; /// All Inspectors implementations that revm has. From 928e5227cc912468ce0922bd8b3967c8564bcadf Mon Sep 17 00:00:00 2001 From: pistomat Date: Mon, 13 Feb 2023 12:48:44 +0100 Subject: [PATCH 6/9] default to single threaded run for traces --- bins/revme/src/statetest/runner.rs | 6 +++++- crates/revm/src/inspector.rs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index ce63a8ad6a..550e65c701 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -333,7 +333,11 @@ pub fn execute_test_suit( Ok(()) } -pub fn run(test_files: Vec, single_thread: bool, trace: bool) -> Result<(), TestError> { +pub fn run(test_files: Vec, mut single_thread: bool, trace: bool) -> Result<(), TestError> { + if trace { + single_thread = true; + } + let endjob = Arc::new(AtomicBool::new(false)); let console_bar = Arc::new(ProgressBar::new(test_files.len() as u64)); let mut joins: Vec>> = Vec::new(); diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index a0cb734c9a..7ab77be886 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -17,6 +17,7 @@ pub mod inspectors { pub use super::customprinter::CustomPrintTracer; pub use super::gas::GasInspector; pub use super::noop::NoOpInspector; + #[cfg(feature = "std")] pub use super::tracer_eip3155::TracerEip3155; } From de3645486f5610cf9a8f03767bc28df4a1cfe70a Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 16 Feb 2023 12:36:16 +0100 Subject: [PATCH 7/9] Update Cargo.toml --- crates/revm/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 946f05d594..0ec32bef3f 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -50,7 +50,7 @@ optional_eip3607 = ["revm-interpreter/optional_eip3607"] optional_gas_refund = ["revm-interpreter/optional_gas_refund"] std = ["revm-interpreter/std"] ethersdb = ["tokio", "futures", "ethers-providers", "ethers-core"] -serde = ["dep:serde", "revm-interpreter/serde"] +serde = ["dep:serde","dep:serde_json", "revm-interpreter/serde"] # deprecated feature web3db = [] with-serde = [] From 892d48986b0cc565af9d1dfd9ea7eb81d93af933 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 16 Feb 2023 12:37:05 +0100 Subject: [PATCH 8/9] Update crates/revm/src/inspector/tracer_eip3155.rs --- crates/revm/src/inspector/tracer_eip3155.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/revm/src/inspector/tracer_eip3155.rs b/crates/revm/src/inspector/tracer_eip3155.rs index 44c3363023..a3579883f5 100644 --- a/crates/revm/src/inspector/tracer_eip3155.rs +++ b/crates/revm/src/inspector/tracer_eip3155.rs @@ -11,17 +11,8 @@ use crate::{evm_impl::EVMData, Inspector}; use crate::inspectors::GasInspector; -// use bytes::Bytes; -// use ruint::aliases::U256; use serde_json::json; -// pub use revm::Inspector; -// use revm::{ -// opcode::{self}, -// CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, Interpreter, Memory, Return, -// Stack, B160, -// }; - pub struct TracerEip3155 { output: Box, gas_inspector: GasInspector, From cd82324aadcaac70a23e4839bf54632ea979144b Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 16 Feb 2023 12:43:44 +0100 Subject: [PATCH 9/9] fmt --- bins/revme/src/statetest/runner.rs | 6 +++++- crates/revm/src/inspector/tracer_eip3155.rs | 14 +++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index 550e65c701..22103bbd6b 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -333,7 +333,11 @@ pub fn execute_test_suit( Ok(()) } -pub fn run(test_files: Vec, mut single_thread: bool, trace: bool) -> Result<(), TestError> { +pub fn run( + test_files: Vec, + mut single_thread: bool, + trace: bool, +) -> Result<(), TestError> { if trace { single_thread = true; } diff --git a/crates/revm/src/inspector/tracer_eip3155.rs b/crates/revm/src/inspector/tracer_eip3155.rs index a3579883f5..a9b80c9a53 100644 --- a/crates/revm/src/inspector/tracer_eip3155.rs +++ b/crates/revm/src/inspector/tracer_eip3155.rs @@ -1,17 +1,13 @@ -// https://eips.ethereum.org/EIPS/eip-3155 - -use std::io::Write; - -use revm_interpreter::primitives::U256; -use revm_interpreter::{opcode, Interpreter, Memory, Stack}; +//! Inspector that support tracing of EIP-3155 https://eips.ethereum.org/EIPS/eip-3155 +use crate::inspectors::GasInspector; use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; use crate::primitives::{db::Database, hex, Bytes, B160}; use crate::{evm_impl::EVMData, Inspector}; - -use crate::inspectors::GasInspector; - +use revm_interpreter::primitives::U256; +use revm_interpreter::{opcode, Interpreter, Memory, Stack}; use serde_json::json; +use std::io::Write; pub struct TracerEip3155 { output: Box,