From 20383b9a63e5292394c11ce71e3f951a168bafda Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 10 Jul 2024 11:05:58 +0800 Subject: [PATCH 01/40] add inspect logger --- src/lib.rs | 94 +++++++++++++++++++++++++--------------------- src/logs.rs | 10 ++--- tests/revm_test.rs | 2 +- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7399e00..6127e37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,7 @@ pub struct TinyEvmContext {} #[pyclass(unsendable)] pub struct TinyEVM { /// REVM instance - pub exe: Option>, + pub exe: Option>, pub owner: Address, /// Snapshots of account state pub snapshots: HashMap, @@ -140,7 +140,7 @@ impl TinyEVM { /// Create a new TinyEVM instance without fork pub fn new_offline() -> Result { - Self::new(None, None) + Self::new_instance(None, None, false) } /// Set account balance, if the account does not exist, will create one @@ -235,26 +235,14 @@ impl TinyEVM { debug!("Calculated addresss: {:?}", address); - let mut traces = vec![]; - let mut logs = vec![]; - let mut override_addresses = HashMap::with_capacity(1); - let trace_enabled = matches!(env::var("TINYEVM_CALL_TRACE_ENABLED"), Ok(val) if val == "1"); - - let inspector = LogsInspector { - trace_enabled, - traces: &mut traces, - logs: &mut logs, - override_addresses: &override_addresses, - }; - // todo add the inspector to the exe let result = self.exe.as_mut().unwrap().transact_commit(); - if let Some(force_address) = force_address { - override_addresses.insert(address, force_address); - self.clone_account(address, force_address, true)?; - } + // if let Some(force_address) = force_address { + // override_addresses.insert(address, force_address); + // self.clone_account(address, force_address, true)?; + // } // debug!("db {:?}", self.exe.as_ref().unwrap().db()); // debug!("sender {:?}", owner.encode_hex::(),); @@ -360,16 +348,16 @@ impl TinyEVM { self.exe = Some(exe); } - let mut traces = vec![]; - let mut logs = vec![]; - let trace_enabled = matches!(env::var("TINYEVM_CALL_TRACE_ENABLED"), Ok(val) if val == "1"); + // let mut traces = vec![]; + // let mut logs = vec![]; + // let trace_enabled = matches!(env::var("TINYEVM_CALL_TRACE_ENABLED"), Ok(val) if val == "1"); - let inspector = LogsInspector { - trace_enabled, - traces: &mut traces, - logs: &mut logs, - override_addresses: &mut Default::default(), - }; + // let inspector = LogsInspector { + // trace_enabled, + // traces: &mut traces, + // logs: &mut logs, + // override_addresses: &mut Default::default(), + // }; let result = { let exe = self.exe.as_mut().unwrap(); @@ -401,13 +389,15 @@ impl TinyEVM { let ignored_addresses = db.ignored_addresses.clone(); let ignored_addresses = ignored_addresses.into_iter().map(Into::into).collect(); + // let logs = self.exe.as_ref().unwrap().context.external; + let revm_result = RevmResult { result: result.map_err(|e| eyre!(e)), bug_data, heuristics, seen_pcs, - transient_logs: logs, - traces, + transient_logs: Vec::new(), // todo logs, + traces: Vec::new(), // todo ignored_addresses, }; revm_result.into() @@ -515,21 +505,12 @@ impl TinyEVM { Ok(()) } -} - -impl Default for TinyEVM { - fn default() -> Self { - Self::new(None, None).unwrap() - } -} -// Implementations for use in Python and Rust -#[pymethods] -impl TinyEVM { - /// Create a new TinyEVM instance - #[new] - #[pyo3(signature = (fork_url = None, block_id = None))] - pub fn new(fork_url: Option, block_id: Option) -> Result { + pub fn new_instance( + fork_url: Option, + block_id: Option, + enable_call_trace: bool, // Whether to show call and event traces + ) -> Result { dotenv().ok(); let owner = Address::default(); @@ -585,10 +566,20 @@ impl TinyEVM { }; db.insert_account_info(owner, account); + // let mut builder = Evm::builder(); + let inspector = LogsInspector { + trace_enabled: enable_call_trace, + traces: Vec::new(), + logs: Vec::new(), + override_addresses: HashMap::with_capacity(32), + }; + + // builder = builder.with_external_context(inspector); let exe = Evm::builder() .modify_env(|e| *e = Box::new(env.clone())) .with_db(db.clone()) + .with_external_context(inspector) .build(); let tinyevm = Self { exe: Some(exe), @@ -599,6 +590,23 @@ impl TinyEVM { Ok(tinyevm) } +} + +impl Default for TinyEVM { + fn default() -> Self { + Self::new_instance(None, None, false).unwrap() + } +} + +// Implementations for use in Python and Rust +#[pymethods] +impl TinyEVM { + /// Create a new TinyEVM instance + #[new] + #[pyo3(signature = (fork_url = None, block_id = None))] + pub fn new(fork_url: Option, block_id: Option) -> Result { + Self::new_instance(fork_url, block_id, false) + } /// Get addresses loaded remotely as string pub fn get_forked_addresses(&self) -> Result> { diff --git a/src/logs.rs b/src/logs.rs index 13b5a01..28bf07a 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -40,18 +40,18 @@ pub struct Log { /// An inspector that collects call traces. #[derive(Debug)] -pub struct LogsInspector<'a> { +pub struct LogsInspector { /// Traced enabled? pub trace_enabled: bool, /// The collected traces - pub traces: &'a mut Vec, + pub traces: Vec, /// EVM events/logs collected during execution - pub logs: &'a mut Vec, + pub logs: Vec, /// Overrides for addresses. Any address created in this map keys will be overriden with the value - pub override_addresses: &'a HashMap, + pub override_addresses: HashMap, } -impl Inspector for LogsInspector<'_> +impl Inspector for LogsInspector where DB: Database, { diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 20ab96d..ffb32c4 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1515,7 +1515,7 @@ fn test_events() -> Result<()> { let contract = format!("0x{:0>40}", hex::encode(&resp.data)); println!("Contract address: {}", contract); let data = format!( - "{}{:x}", + "{}{:064x}", "1401d2b5", // makeEvent(3232) U256::from(3232) ); From 1f33858ced170c3ea7d20061f3d93a726b8bf32e Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 10 Jul 2024 11:26:18 +0800 Subject: [PATCH 02/40] wip: bug inspector --- src/instrument/bug_inspector.rs | 44 +++++++++++++++++++++++++++++++++ src/instrument/mod.rs | 1 + src/logs.rs | 4 +++ 3 files changed, 49 insertions(+) create mode 100644 src/instrument/bug_inspector.rs diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs new file mode 100644 index 0000000..d9ce17d --- /dev/null +++ b/src/instrument/bug_inspector.rs @@ -0,0 +1,44 @@ +use hashbrown::HashMap; +use revm::{ + interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, + primitives::{Address, Log as EvmLog}, + Database, EvmContext, Inspector, +}; + +pub struct BugInspector { + /// Change the created address to another address + pub create_address_overrides: HashMap, +} + +impl Inspector for BugInspector +where + DB: Database, +{ + #[inline] + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + let _ = interp; + let _ = context; + } + + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + let _ = interp; + let _ = context; + } + + #[inline] + fn create_end( + &mut self, + _context: &mut EvmContext, + _inputs: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + let CreateOutcome { result, address } = outcome; + if let Some(address) = address { + if let Some(override_address) = self.create_address_overrides.get(&address) { + return CreateOutcome::new(result, Some(*override_address)); + } + } + CreateOutcome::new(result, address) + } +} diff --git a/src/instrument/mod.rs b/src/instrument/mod.rs index ec7b02c..5adeb1d 100644 --- a/src/instrument/mod.rs +++ b/src/instrument/mod.rs @@ -1,2 +1,3 @@ pub mod bug; pub use bug::*; +pub mod bug_inspector; diff --git a/src/logs.rs b/src/logs.rs index 28bf07a..5e53c79 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -55,6 +55,7 @@ impl Inspector for LogsInspector where DB: Database, { + #[inline] fn log(&mut self, _context: &mut EvmContext, evm_log: &EvmLog) { if !self.trace_enabled { return; @@ -72,6 +73,7 @@ where }); } + #[inline] fn call( &mut self, _context: &mut EvmContext, @@ -116,6 +118,7 @@ where None } + #[inline] fn call_end( &mut self, _context: &mut EvmContext, @@ -138,6 +141,7 @@ where result } + #[inline] fn create_end( &mut self, _context: &mut EvmContext, From 457d309c2e7dd0647006261a5d2d22371539caa7 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 10 Jul 2024 16:37:09 +0800 Subject: [PATCH 03/40] adding bug inspector --- src/chain_inspector.rs | 145 ++++++++++++++++++++++++++++++++ src/instrument/bug.rs | 3 - src/instrument/bug_inspector.rs | 143 +++++++++++++++++++++++++++++-- src/lib.rs | 5 +- 4 files changed, 284 insertions(+), 12 deletions(-) create mode 100644 src/chain_inspector.rs diff --git a/src/chain_inspector.rs b/src/chain_inspector.rs new file mode 100644 index 0000000..9a6740b --- /dev/null +++ b/src/chain_inspector.rs @@ -0,0 +1,145 @@ +use revm::interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs}; +use revm::primitives::{Address, Log, U256}; +use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; + +/// A chain of inspectors, ecch inspector will be executed in order. +pub struct ChainInspector { + /// The inspector which modifies the execution should be placed at the end of the chain. + pub inspectors: Vec>>, +} + +impl Default for ChainInspector { + fn default() -> Self { + Self { + inspectors: Vec::new(), + } + } +} + +impl ChainInspector { + pub fn add + 'static>(&mut self, inspector: I) { + self.inspectors.push(Box::new(inspector)); + } +} + +impl Inspector for ChainInspector { + #[inline] + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + for inspector in self.inspectors.iter_mut() { + inspector.initialize_interp(interp, context); + } + } + + #[inline] + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + for inspector in self.inspectors.iter_mut() { + inspector.step(interp, context); + } + } + + #[inline] + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + for inspector in self.inspectors.iter_mut() { + inspector.step_end(interp, context); + } + } + + #[inline] + fn log(&mut self, context: &mut EvmContext, log: &Log) { + for inspector in self.inspectors.iter_mut() { + inspector.log(context, log); + } + } + + /// Call the inspectors in order, if any of them returns a `Some`, return that value. + /// If all of them return `None`, the execution will continue normally. + #[inline] + fn call( + &mut self, + context: &mut EvmContext, + inputs: &mut CallInputs, + ) -> Option { + for inspector in self.inspectors.iter_mut() { + if let Some(outcome) = inspector.call(context, inputs) { + return Some(outcome); + } + } + None + } + + #[inline] + fn call_end( + &mut self, + context: &mut EvmContext, + inputs: &CallInputs, + outcome: CallOutcome, + ) -> CallOutcome { + let mut outcome = outcome; + for inspector in self.inspectors.iter_mut() { + outcome = inspector.call_end(context, inputs, outcome); + } + outcome + } + + /// Call the inspectors in order, if any of them returns a `Some`, return that value. + #[inline] + fn create( + &mut self, + context: &mut EvmContext, + inputs: &mut CreateInputs, + ) -> Option { + for inspector in self.inspectors.iter_mut() { + if let Some(outcome) = inspector.create(context, inputs) { + return Some(outcome); + } + } + None + } + + #[inline] + fn create_end( + &mut self, + context: &mut EvmContext, + inputs: &CreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + let mut outcome = outcome; + for inspector in self.inspectors.iter_mut() { + outcome = inspector.create_end(context, inputs, outcome); + } + outcome + } + + fn eofcreate( + &mut self, + context: &mut EvmContext, + inputs: &mut EOFCreateInputs, + ) -> Option { + for inspector in self.inspectors.iter_mut() { + if let Some(outcome) = inspector.eofcreate(context, inputs) { + return Some(outcome); + } + } + None + } + + fn eofcreate_end( + &mut self, + context: &mut EvmContext, + inputs: &EOFCreateInputs, + outcome: CreateOutcome, + ) -> CreateOutcome { + let mut outcome = outcome; + for inspector in self.inspectors.iter_mut() { + outcome = inspector.eofcreate_end(context, inputs, outcome); + } + outcome + } + + #[inline] + fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + for inspector in self.inspectors.iter_mut() { + inspector.selfdestruct(contract, target, value); + } + } +} diff --git a/src/instrument/bug.rs b/src/instrument/bug.rs index 9782f9b..9065251 100644 --- a/src/instrument/bug.rs +++ b/src/instrument/bug.rs @@ -168,8 +168,6 @@ impl Heuristics { #[derive(Clone, Debug)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] pub struct InstrumentConfig { - /// Master switch to toggle instrumentation - pub enabled: bool, /// Enable recording seen PCs by current contract address pub pcs_by_address: bool, /// Enable heuristics which will record list of jumpi destinations @@ -189,7 +187,6 @@ pub struct InstrumentConfig { impl Default for InstrumentConfig { fn default() -> InstrumentConfig { InstrumentConfig { - enabled: true, pcs_by_address: true, heuristics: true, record_branch_for_target_only: false, diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index d9ce17d..f47cdd7 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -1,13 +1,102 @@ -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use revm::{ - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, - primitives::{Address, Log as EvmLog}, + interpreter::{ + opcode::{self}, + CreateInputs, CreateOutcome, InstructionResult, Interpreter, OpCode, + }, + primitives::{Address, U256}, Database, EvmContext, Inspector, }; +use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; + pub struct BugInspector { /// Change the created address to another address pub create_address_overrides: HashMap, + pub bug_data: BugData, + pub heuristics: Heuristics, + // Mapping from contract address to a set of PCs seen in the execution + pub pcs_by_address: HashMap>, + pub instrument_config: InstrumentConfig, + // Holding the addresses created in the current transaction, + // must be cleared by transaction caller before or after each transaction + pub created_addresses: Vec
, + // Managed addresses: contract -> addresses created by any transaction from the contract + pub managed_addresses: HashMap>, + pub opcode_index: usize, + /// Stack inputs of the current opcodes. Only updated when the opcode is interesting + inputs: Vec, +} + +impl BugInspector { + fn record_seen_address(&mut self, address: Address) -> isize { + // make sure target_address is the first address added + if self.instrument_config.record_branch_for_target_only { + if self.heuristics.seen_addresses.is_empty() { + self.heuristics + .seen_addresses + .push(self.instrument_config.target_address); + } + + if self.instrument_config.target_address == address { + return 0; + } + } + + let idx = self + .heuristics + .seen_addresses + .iter() + .position(|a| *a == address); + if let Some(i) = idx { + return i as isize; + } + + self.heuristics.seen_addresses.push(address); + self.heuristics.seen_addresses.len() as isize - 1 + } + + /// Record the program counter for the given contract address + pub fn record_pc(&mut self, address: Address, pc: usize) { + let pcs = self.pcs_by_address.entry(address).or_insert(HashSet::new()); + pcs.insert(pc); + } + + pub fn add_bug(&mut self, bug: Bug) { + match bug.bug_type { + BugType::Jumpi(dest) => { + if self.instrument_config.heuristics { + // March 15 bug patch: keep last 256 elements + self.heuristics.coverage.push_back(dest); + if self.heuristics.coverage.len() > 256 { + self.heuristics.coverage.pop_front(); + } + } + } + BugType::Sload(_key) => { + if self.bug_data.len() > 256 { + // this will lead to poor performance + // self.bug_data.retain(|front| { + // !(front.address_index == address_idx + // && matches!(front.bug_type, BugType::Sload(k) if k == key)) + // }); + self.bug_data.pop_front(); + } + self.bug_data.push_back(bug); + } + BugType::Sstore(_key, _) => { + if self.bug_data.len() > 256 { + // self.bug_data.retain(|front| { + // !(front.address_index == address_idx + // && matches!(front.bug_type, BugType::Sstore(k, _) if k == key)) + // }); + self.bug_data.pop_front(); + } + self.bug_data.push_back(bug); + } + _ => self.bug_data.push_back(bug), + } + } } impl Inspector for BugInspector @@ -18,14 +107,58 @@ where fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; + let opcode = OpCode::new(interp.current_opcode()); + match opcode { + Some(OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV | OpCode::SDIV) => { + let a = interp.stack().peek(0).unwrap(); + let b = interp.stack().peek(1).unwrap(); + self.inputs.push(a); + self.inputs.push(b); + } + _ => {} + } } #[inline] fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - let _ = interp; - let _ = context; + let address = interp.contract().target_address; + let address_index = self.record_seen_address(address); + let pc = interp.program_counter(); + let opcode = interp.current_opcode(); + + if self.instrument_config.pcs_by_address { + self.record_pc(address.into(), pc); + } + + // Check for revert or invalid opcode + match &interp.instruction_result { + InstructionResult::Revert | InstructionResult::InvalidEFOpcode => { + let bug = Bug::new(BugType::RevertOrInvalid, opcode, pc, address_index); + self.add_bug(bug); + } + _ => {} + } + + // Check for overflow and underflow + match opcode { + opcode::ADD => { + // todo_cl + } + _ => {} + } } + // #[inline] + // fn call_end( + // &mut self, + // context: &mut EvmContext, + // inputs: &CallInputs, + // outcome: CallOutcome, + // ) -> CallOutcome { + + // outcome + // } + #[inline] fn create_end( &mut self, diff --git a/src/lib.rs b/src/lib.rs index 6127e37..39d0de6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ use thread_local::ThreadLocal; use tokio::runtime::Runtime; /// Caching for Web3 provider mod cache; +mod chain_inspector; /// Common functions shared by both EVMs mod common; @@ -1088,8 +1089,6 @@ impl TinyEVM { /// REVM::InstrumentConfig #[pyclass(set_all, get_all)] pub struct REVMConfig { - /// Master switch to toggle instrumentation - pub enabled: bool, /// Enable recording seen PCs by current contract address pub pcs_by_address: bool, /// Enable heuristics which will record list of jumpi destinations @@ -1134,7 +1133,6 @@ impl REVMConfig { Ok(InstrumentConfig { target_address, - enabled: self.enabled, pcs_by_address: self.pcs_by_address, heuristics: self.heuristics, record_branch_for_target_only: self.record_branch_for_target_only, @@ -1145,7 +1143,6 @@ impl REVMConfig { /// Convert to `REVMConfig` from internal Rust struct fn from(config: &InstrumentConfig) -> Self { Self { - enabled: config.enabled, pcs_by_address: config.pcs_by_address, heuristics: config.heuristics, record_branch_for_target_only: config.record_branch_for_target_only, From 759a9f9249b0827c165b63e83cfcc1f89a6bc8da Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 10 Jul 2024 17:40:58 +0800 Subject: [PATCH 04/40] adding bug data in instruction step --- src/instrument/bug_inspector.rs | 136 ++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 31 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index f47cdd7..d2e2568 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -26,6 +26,8 @@ pub struct BugInspector { pub opcode_index: usize, /// Stack inputs of the current opcodes. Only updated when the opcode is interesting inputs: Vec, + /// Current opcode + opcode: u8, } impl BugInspector { @@ -58,7 +60,7 @@ impl BugInspector { /// Record the program counter for the given contract address pub fn record_pc(&mut self, address: Address, pc: usize) { - let pcs = self.pcs_by_address.entry(address).or_insert(HashSet::new()); + let pcs = self.pcs_by_address.entry(address).or_default(); pcs.insert(pc); } @@ -107,15 +109,29 @@ where fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; - let opcode = OpCode::new(interp.current_opcode()); - match opcode { - Some(OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV | OpCode::SDIV) => { - let a = interp.stack().peek(0).unwrap(); - let b = interp.stack().peek(1).unwrap(); + self.opcode = interp.current_opcode(); + let opcode = OpCode::new(self.opcode); + + // it's possible to handle REVERT, INVALID here + if let Some( + OpCode::ADD + | OpCode::SUB + | OpCode::MUL + | OpCode::DIV + | OpCode::SDIV + | OpCode::SMOD + | OpCode::ADDMOD + | OpCode::MULMOD + | OpCode::EXP, + ) = opcode + { + self.inputs.clear(); + let a = interp.stack().peek(0); + let b = interp.stack().peek(1); + if let (Ok(a), Ok(b)) = (a, b) { self.inputs.push(a); self.inputs.push(b); } - _ => {} } } @@ -124,41 +140,90 @@ where let address = interp.contract().target_address; let address_index = self.record_seen_address(address); let pc = interp.program_counter(); - let opcode = interp.current_opcode(); + let opcode = self.opcode; if self.instrument_config.pcs_by_address { - self.record_pc(address.into(), pc); + self.record_pc(address, pc); } - // Check for revert or invalid opcode - match &interp.instruction_result { - InstructionResult::Revert | InstructionResult::InvalidEFOpcode => { + let success = interp.instruction_result.is_ok(); + + // Check for overflow and underflow + match (opcode, success) { + (opcode::ADD, true) => { + if let Ok(r) = interp.stack().peek(0) { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + if r < *a || r < *b { + let bug = Bug::new(BugType::IntegerOverflow, opcode, pc, address_index); + self.add_bug(bug); + } + } + } + } + (opcode::MUL, true) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + if mul_overflow(*a, *b) { + let bug = Bug::new(BugType::IntegerOverflow, opcode, pc, address_index); + self.add_bug(bug); + } + } + } + (opcode::SUB, true) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + if a < b { + let bug = Bug::new(BugType::IntegerSubUnderflow, opcode, pc, address_index); + self.add_bug(bug); + } + } + } + (opcode::DIV | opcode::SDIV | opcode::SMOD, true) => { + if let Some(b) = self.inputs.get(1) { + if b == &U256::ZERO { + let bug = Bug::new(BugType::IntegerDivByZero, opcode, pc, address_index); + self.add_bug(bug); + } + } + } + (opcode::ADDMOD | opcode::MULMOD, true) => { + if let Some(n) = self.inputs.get(2) { + if n == &U256::ZERO { + let bug = Bug::new(BugType::IntegerDivByZero, opcode, pc, address_index); + self.add_bug(bug); + } + } + } + (opcode::EXP, true) => { + // todo_cl check for overflow + } + (opcode::BLOBHASH, _) => { + let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); + self.add_bug(bug); + } + (opcode::COINBASE, _) => { + let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); + self.add_bug(bug); + } + (opcode::TIMESTAMP, _) => { + let bug = Bug::new(BugType::TimestampDependency, opcode, pc, address_index); + self.add_bug(bug); + } + (opcode::NUMBER, _) => { + let bug = Bug::new(BugType::BlockNumberDependency, opcode, pc, address_index); + self.add_bug(bug); + } + (opcode::DIFFICULTY, _) => { + let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); + self.add_bug(bug); + } + (opcode::REVERT | opcode::INVALID, _) => { let bug = Bug::new(BugType::RevertOrInvalid, opcode, pc, address_index); self.add_bug(bug); } - _ => {} - } - // Check for overflow and underflow - match opcode { - opcode::ADD => { - // todo_cl - } - _ => {} + _ => (), } } - // #[inline] - // fn call_end( - // &mut self, - // context: &mut EvmContext, - // inputs: &CallInputs, - // outcome: CallOutcome, - // ) -> CallOutcome { - - // outcome - // } - #[inline] fn create_end( &mut self, @@ -175,3 +240,12 @@ where CreateOutcome::new(result, address) } } + +fn mul_overflow(a: U256, b: U256) -> bool { + let zero = U256::ZERO; + if a == zero || b == zero { + false + } else { + a > U256::MAX.wrapping_div(b) + } +} From a465314663385a0cd4fd1ba404ae840de76c7a10 Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 11 Jul 2024 12:02:54 +0800 Subject: [PATCH 05/40] branch distance, etc --- src/instrument/bug.rs | 71 ++++++--- src/instrument/bug_inspector.rs | 271 +++++++++++++++++++++++++++++--- 2 files changed, 299 insertions(+), 43 deletions(-) diff --git a/src/instrument/bug.rs b/src/instrument/bug.rs index 9065251..344ae44 100644 --- a/src/instrument/bug.rs +++ b/src/instrument/bug.rs @@ -62,21 +62,46 @@ impl std::fmt::Display for Bug { #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] pub struct MissedBranch { - /// Previous program counter - // pub prev_pc: usize, + // The pc imediately before the conditional jumpi pub prev_pc: usize, - /// Missed program counter - pub pc: usize, + /// Condition of the jumpi, true jump to `dest_pc`, false jump to `prev_pc + 1` + pub cond: bool, + /// Destination pc if condition is true + pub dest_pc: usize, /// Distiance required to reach the missed branch pub distance: U256, + /// Address of the contract in which this operation is executed + pub address_index: isize, +} + +impl MissedBranch { + pub fn new( + prev_pc: usize, + dest_pc: usize, + cond: bool, + distance: U256, + address_index: isize, + ) -> Self { + Self { + prev_pc, + dest_pc, + cond, + distance, + address_index, + } + } } -impl From<(usize, usize, U256)> for MissedBranch { - fn from((prev_pc, pc, distance): (usize, usize, U256)) -> Self { +impl From<(usize, usize, bool, U256, isize)> for MissedBranch { + fn from( + (prev_pc, dest_pc, cond, distance, address_index): (usize, usize, bool, U256, isize), + ) -> Self { Self { prev_pc, - pc, + dest_pc, + cond, distance, + address_index, } } } @@ -134,30 +159,28 @@ impl Heuristics { } /// Record missing branch data - pub fn record_missed_branch(&mut self, missed_pc: usize) { - let prev_pc = self.coverage.back(); - if prev_pc.is_none() { - return; - } - - let prev_pc = prev_pc.unwrap(); - let pc = missed_pc; + pub fn record_missed_branch( + &mut self, + prev_pc: usize, + dest_pc: usize, + cond: bool, + address_index: isize, + ) { let distance = self.distance; if self.missed_branches.iter_mut().any(|x| { - if x.pc == pc && x.prev_pc == *prev_pc { - if distance != x.distance { - x.distance = distance - } - true - } else { - false - } + matches!(x, MissedBranch { prev_pc: p, dest_pc: d, distance: dist, .. } if *p == prev_pc && *d == dest_pc && *dist == distance) }) { return; } - self.missed_branches.push((*prev_pc, pc, distance).into()); + self.missed_branches.push(MissedBranch::new( + prev_pc, + dest_pc, + cond, + distance, + address_index, + )); // if self.missed_branchs.len() > 2 { // self.missed_branchs.drain(0..self.missed_branchs.len() - 2); // } diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index d2e2568..173cc90 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -28,9 +28,24 @@ pub struct BugInspector { inputs: Vec, /// Current opcode opcode: u8, + /// Current index in the execution. For tracking peephole optimized if-statement + step_index: u64, + last_index_sub: u64, + last_index_eq: u64, } impl BugInspector { + pub fn inc_step_index(&mut self) { + self.step_index += 1; + } + + /// Returns true if this is possible peephole optimized code, + /// assuming when calling this function the current opcode is + /// JUMPI + pub fn possibly_if_equal(&self) -> bool { + self.step_index < self.last_index_sub + 10 && self.step_index > self.last_index_eq + 10 + } + fn record_seen_address(&mut self, address: Address) -> isize { // make sure target_address is the first address added if self.instrument_config.record_branch_for_target_only { @@ -112,27 +127,60 @@ where self.opcode = interp.current_opcode(); let opcode = OpCode::new(self.opcode); - // it's possible to handle REVERT, INVALID here - if let Some( - OpCode::ADD - | OpCode::SUB - | OpCode::MUL - | OpCode::DIV - | OpCode::SDIV - | OpCode::SMOD - | OpCode::ADDMOD - | OpCode::MULMOD - | OpCode::EXP, - ) = opcode - { - self.inputs.clear(); - let a = interp.stack().peek(0); - let b = interp.stack().peek(1); - if let (Ok(a), Ok(b)) = (a, b) { - self.inputs.push(a); - self.inputs.push(b); + if let Some(OpCode::EQ) = opcode { + self.last_index_eq = self.step_index; + } + + if let Some(OpCode::SUB) = opcode { + self.last_index_sub = self.step_index; + } + + // it's also possible to handle REVERT, INVALID here + + match opcode { + Some( + OpCode::JUMPI + // possible overflows / underflows + | OpCode::ADD + | OpCode::SUB + | OpCode::MUL + | OpCode::DIV + | OpCode::SDIV + | OpCode::SMOD + | OpCode::EXP + // heuristic distance + | OpCode::LT + | OpCode::SLT + | OpCode::GT + | OpCode::SGT + | OpCode::EQ + // possible truncation + | OpCode::AND + ) => { + self.inputs.clear(); + let a = interp.stack().peek(0); + let b = interp.stack().peek(1); + if let (Ok(a), Ok(b)) = (a, b) { + self.inputs.push(a); + self.inputs.push(b); + } + }, + Some( OpCode::ADDMOD | OpCode::MULMOD) => { + self.inputs.clear(); + let a = interp.stack().peek(0); + let b = interp.stack().peek(1); + let n = interp.stack().peek(2); + + if let (Ok(a), Ok(b), Ok(n)) = (a, b, n) { + self.inputs.push(a); + self.inputs.push(b); + self.inputs.push(n); + } } + _ => {} } + + self.inc_step_index(); } #[inline] @@ -194,6 +242,169 @@ where } (opcode::EXP, true) => { // todo_cl check for overflow + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + if exp_overflow(*a, *b, r) { + let bug = Bug::new(BugType::IntegerOverflow, opcode, pc, address_index); + self.add_bug(bug); + } + } + } + (opcode::LT, true) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + let distance = if a >= b { + a.overflowing_sub(*b).0.saturating_add(U256::from(1)) + } else { + b.overflowing_sub(*a).0 + }; + self.heuristics.distance = distance; + } + } + (opcode::GT, true) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + let distance = if a >= b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0.saturating_add(U256::from(1)) + }; + self.heuristics.distance = distance; + } + } + (opcode::SLT, true) => { + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let mut distance = if a >= b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0 + }; + if r == U256::ZERO { + distance = distance.saturating_add(U256::from(1)); + } + self.heuristics.distance = distance; + } + } + (opcode::SGT, true) => { + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let mut distance = if a >= b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0 + }; + if r == U256::ZERO { + distance = distance.saturating_add(U256::from(1)); + } + self.heuristics.distance = distance; + } + } + (opcode::EQ, true) => { + if let (Some(a), Some(b), Ok(r)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let mut distance = if a >= b { + a.overflowing_sub(*b).0 + } else { + b.overflowing_sub(*a).0 + }; + if r == U256::ZERO { + distance = distance.saturating_add(U256::from(1)); + } + self.heuristics.distance = distance; + } + } + (opcode::AND, true) => { + if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { + // check if there is an possible truncation + + // For AND operator, if either side of the operands equals + // u8, u16, ..., and the other side is larger than this + // operand, generate possible integer truncation signal + let mut i = 1; + let possible_overflow = loop { + if i == 32 { + break false; + } + + let r = U256::MAX >> (i * 8); + + if r == *a && b > a { + break true; + } + + if r == *b && a > b { + break true; + } + i += 1; + }; + if possible_overflow { + let bug = Bug::new( + BugType::PossibleIntegerTruncation, + opcode, + pc, + address_index, + ); + self.add_bug(bug); + } + } + } + (opcode::JUMPI, true) => { + // Check for missed branches + let target_address = self.instrument_config.target_address; + macro_rules! update_heuritics { + // (prev_pc, dest_pc_if_cond_is_true, cond) + ($prev_pc: ident, $dest_pc: expr, $cond: expr) => { + if !self.instrument_config.record_branch_for_target_only + || address == target_address + { + let heuristics = &mut self.heuristics; + heuristics.record_missed_branch( + $prev_pc, + $dest_pc, + $cond, + address_index, + ); + let target = if $cond { $dest_pc } else { $prev_pc + 1 }; + let bug = + Bug::new(BugType::Jumpi(target), opcode, $prev_pc, address_index); + self.add_bug(bug); + } + }; + } + + // NOTE: invalid jumps are ignored + if let (Some(dest), Some(value)) = (self.inputs.first(), self.inputs.get(1)) { + // Check for distance in peephole optimized if-statement + if self.possibly_if_equal() { + let max = U256::MAX; + let mut half = U256::MAX; + half.set_bit(31, false); + let h = &mut self.heuristics; + h.distance = { + // smallest distance from the `value` to U256::MAX and 0 + if *value > half { + max - value + U256::from(1) + } else { + *value + } + }; + } + + let dest = usize::try_from(dest).unwrap(); + let cond = *value != U256::ZERO; + update_heuritics!(pc, dest, cond); + } } (opcode::BLOBHASH, _) => { let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); @@ -215,6 +426,7 @@ where let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); self.add_bug(bug); } + (opcode::REVERT | opcode::INVALID, _) => { let bug = Bug::new(BugType::RevertOrInvalid, opcode, pc, address_index); self.add_bug(bug); @@ -249,3 +461,24 @@ fn mul_overflow(a: U256, b: U256) -> bool { a > U256::MAX.wrapping_div(b) } } + +fn exp_overflow(a: U256, b: U256, r: U256) -> bool { + let max_value: U256 = U256::MAX; + let mut result: U256 = U256::from(1u64); + + if b == U256::ZERO { + return r != U256::from(1u64); + } + + let mut i = U256::ZERO; + + while i < b { + if result > max_value / a { + return true; + } + result *= a; + i += U256::from(1u64); + } + + result != r +} From b62b72e136cc3e886b173757fbbc10108f27070b Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 11 Jul 2024 13:42:27 +0800 Subject: [PATCH 06/40] update bug_inspector --- src/response.rs | 11 +++-- tests/revm_test.rs | 105 ++++++++++++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/response.rs b/src/response.rs index 932b4c1..c2c417e 100644 --- a/src/response.rs +++ b/src/response.rs @@ -57,12 +57,13 @@ pub struct WrappedBug { #[derive(Clone, Debug)] pub struct WrappedMissedBranch { /// Previous program counter - // pub prev_pc: usize, pub prev_pc: usize, - /// Missed program counter - pub pc: usize, + /// Destination pc if condition is true + pub dest_pc: usize, + pub cond: bool, /// Distiance required to reach the missed branch pub distance: BigInt, + pub address_index: isize, } /// Wrapper around Heuristics @@ -103,8 +104,10 @@ impl From for WrappedHeuristics { .iter() .map(|x| WrappedMissedBranch { prev_pc: x.prev_pc, - pc: x.pc, + dest_pc: x.dest_pc, + cond: x.cond, distance: ruint_u256_to_bigint(&x.distance), + address_index: x.address_index, }) .collect(); let mut sha3_mapping = StdHashMap::new(); diff --git a/tests/revm_test.rs b/tests/revm_test.rs index ffb32c4..4d2f1a0 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -466,57 +466,84 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { Ok(()) } -#[test] -fn test_heuristics() { - deploy_hex!("../tests/contracts/heuristics.hex", vm, address); +fn test_heuristics_inner( + input: u64, // `i` in the function `coverage(uint256 i)` + expected_missed_branches: Vec, // expected list of jumpi + expected_coverages: Vec, // expected list of coverage PCs +) { + deploy_hex!("../tests/contracts/heuristics.hex", exe, address); let fn_sig = "coverage(uint256)"; let fn_sig_hex = fn_sig_to_prefix(fn_sig); - let fn_args_hex = format!("{:0>64x}", U256::from(50)); - - let expected_missed_branches: Vec = vec![ - // (prev_pc, pc, distance) - (24, 40, 0), // Function signature selection input size check (4 bytes) - (45, 61, 1), // Function signature selection - (65, 120, 0x26df), - (127, 136, 0x33), - (143, 152, 0x30), - ] - .into_iter() - .map(|(prev_pc, pc, distance)| (prev_pc, pc, U256::from(distance as u64)).into()) - .collect(); + let fn_args_hex = format!("{:0>64x}", U256::from(input)); let fn_hex = format!("{}{}", fn_sig_hex, fn_args_hex); let tx_data = hex::decode(fn_hex).unwrap(); - let resp = vm.contract_call_helper(Address::new(address.0), *OWNER, tx_data, UZERO, None); + let resp = exe.contract_call_helper(Address::new(address.0), *OWNER, tx_data, UZERO, None); - assert!(resp.success, "Transaction should succeed."); + assert!( + resp.success, + "Transaction should succeed with input {}", + input + ); let heuristics = resp.heuristics; - let missed_branches: Vec<_> = heuristics.missed_branches.into_iter().collect(); - let coverage: Vec = heuristics.coverage.into_iter().collect(); - - assert_eq!( - expected_missed_branches.len(), - missed_branches.len(), - "All missed branches should be found" - ); + let missed_branches: Vec<_> = heuristics.missed_branches.into_iter().skip(4).collect(); + let coverage: Vec = heuristics + .coverage + .into_iter() + .skip(4) // skip 4 from function selector operations + .collect(); assert_eq!( expected_missed_branches, missed_branches, - "All missed branches should be found with expected distances" + "All missed branches should be found with expected distances with input {}", + input ); assert_eq!( - coverage, - vec![15, 24, 45, 65, 127, 143, 159], - "List of coverage PCs should match " + expected_coverages, coverage, + "List of coverage PCs should match with input {}", + input ); } +#[test] +fn test_heuristics() { + // Test coverage(200) + let input = 200; + let expected_missed_branches: Vec = vec![ + // (prev_pc, pc, is_jump_to_target, distance) + // skips 4 from function selector operations + (119, 127, true, 0x2649), + (135, 143, false, 0x64), + ] + .into_iter() + .map(|(prev_pc, pc, cond, distance)| (prev_pc, pc, cond, U256::from(distance as u64), 0).into()) + .collect(); + + let expected_coverages = vec![127, 136]; + test_heuristics_inner(input, expected_missed_branches, expected_coverages); + + // Test coverage(50) + let input = 50; + let expected_missed_branches: Vec = vec![ + // (prev_pc, pc, is_jump_to_target, distance) + (119, 127, true, 0x26df), + (135, 143, true, 0x33), + (151, 159, true, 0x30), + ] + .into_iter() + .map(|(prev_pc, pc, cond, distance)| (prev_pc, pc, cond, U256::from(distance as u64), 0).into()) + .collect(); + + let expected_coverages = vec![127, 143, 159]; + test_heuristics_inner(input, expected_missed_branches, expected_coverages); +} + #[test] fn test_heuristics_signed_int() { deploy_hex!("../tests/contracts/heuristics-signed-int.hex", exe, address); @@ -533,14 +560,13 @@ fn test_heuristics_signed_int() { let expected_missed_branches: Vec = vec![ // (prev_pc, pc, distance) - // (26, 43, 0), // skip two from function selector operations - // (48, 66, 1), - (70, 156, 9950), - (195, 236, 51), - (275, 316, 48), + // skips 4 jumpis: callvalue, calldatasize, selector, calldata argument size check + (155, 195, 9950), + (235, 275, 51), + (315, 355, 48), ] .into_iter() - .map(|(prev_pc, pc, distance)| (prev_pc, pc, U256::from(distance as u64)).into()) + .map(|(prev_pc, pc, distance)| (prev_pc, pc, true, U256::from(distance as u64), 0).into()) .collect(); let fn_hex = format!("{}{}", fn_sig_hex, fn_args_hex); @@ -558,7 +584,7 @@ fn test_heuristics_signed_int() { .heuristics .missed_branches .into_iter() - .skip(2) + .skip(4) .collect(); assert_eq!( @@ -1342,9 +1368,10 @@ fn test_peephole_optimized_if_equal() { let found_missed_branch_at_func1 = resp.heuristics.missed_branches.iter().any( |MissedBranch { prev_pc, - pc, + dest_pc, distance, - }| { (*prev_pc, *pc, *distance) == expected_missed_branches }, + .. + }| { (*prev_pc, *dest_pc, *distance) == expected_missed_branches }, ); assert!( found_missed_branch_at_func1, From 3a581d1d52ee5a8db3fda38bed70d96730d88658 Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 11 Jul 2024 15:32:17 +0800 Subject: [PATCH 07/40] refactor ChainInspector --- src/chain_inspector.rs | 141 +++++++++++++++----------------- src/instrument/bug_inspector.rs | 3 +- src/lib.rs | 20 +++-- src/logs.rs | 6 +- 4 files changed, 84 insertions(+), 86 deletions(-) diff --git a/src/chain_inspector.rs b/src/chain_inspector.rs index 9a6740b..1ca5913 100644 --- a/src/chain_inspector.rs +++ b/src/chain_inspector.rs @@ -1,53 +1,47 @@ use revm::interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs}; -use revm::primitives::{Address, Log, U256}; +use revm::primitives::Log; use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; +use crate::instrument::bug_inspector::BugInspector; +use crate::logs::LogInspector; + /// A chain of inspectors, ecch inspector will be executed in order. -pub struct ChainInspector { - /// The inspector which modifies the execution should be placed at the end of the chain. - pub inspectors: Vec>>, +pub struct ChainInspector { + pub log_inspector: Option, + pub bug_inspector: Option, } -impl Default for ChainInspector { - fn default() -> Self { - Self { - inspectors: Vec::new(), - } - } -} - -impl ChainInspector { - pub fn add + 'static>(&mut self, inspector: I) { - self.inspectors.push(Box::new(inspector)); - } -} - -impl Inspector for ChainInspector { - #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - for inspector in self.inspectors.iter_mut() { - inspector.initialize_interp(interp, context); - } - } +impl Inspector for ChainInspector { + // #[inline] + // fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) {} #[inline] fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - for inspector in self.inspectors.iter_mut() { - inspector.step(interp, context); + if let Some(ins) = self.log_inspector.as_mut() { + ins.step(interp, context); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.step(interp, context); } } #[inline] fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - for inspector in self.inspectors.iter_mut() { - inspector.step_end(interp, context); + if let Some(ins) = self.log_inspector.as_mut() { + ins.step_end(interp, context); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.step_end(interp, context); } } #[inline] fn log(&mut self, context: &mut EvmContext, log: &Log) { - for inspector in self.inspectors.iter_mut() { - inspector.log(context, log); + if let Some(ins) = self.log_inspector.as_mut() { + ins.log(context, log); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.log(context, log); } } @@ -59,12 +53,14 @@ impl Inspector for ChainInspector { context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option { - for inspector in self.inspectors.iter_mut() { - if let Some(outcome) = inspector.call(context, inputs) { - return Some(outcome); - } + if let Some(ins) = self.log_inspector.as_mut() { + ins.call(context, inputs); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.call(context, inputs) + } else { + None } - None } #[inline] @@ -75,8 +71,11 @@ impl Inspector for ChainInspector { outcome: CallOutcome, ) -> CallOutcome { let mut outcome = outcome; - for inspector in self.inspectors.iter_mut() { - outcome = inspector.call_end(context, inputs, outcome); + if let Some(ins) = self.log_inspector.as_mut() { + outcome = ins.call_end(context, inputs, outcome); + } + if let Some(ins) = self.bug_inspector.as_mut() { + outcome = ins.call_end(context, inputs, outcome); } outcome } @@ -88,12 +87,14 @@ impl Inspector for ChainInspector { context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option { - for inspector in self.inspectors.iter_mut() { - if let Some(outcome) = inspector.create(context, inputs) { - return Some(outcome); - } + if let Some(ins) = self.log_inspector.as_mut() { + ins.create(context, inputs); + } + if let Some(ins) = self.bug_inspector.as_mut() { + ins.create(context, inputs) + } else { + None } - None } #[inline] @@ -104,42 +105,32 @@ impl Inspector for ChainInspector { outcome: CreateOutcome, ) -> CreateOutcome { let mut outcome = outcome; - for inspector in self.inspectors.iter_mut() { - outcome = inspector.create_end(context, inputs, outcome); + if let Some(ins) = self.log_inspector.as_mut() { + outcome = ins.create_end(context, inputs, outcome); + } + if let Some(ins) = self.bug_inspector.as_mut() { + outcome = ins.create_end(context, inputs, outcome); } outcome } - fn eofcreate( - &mut self, - context: &mut EvmContext, - inputs: &mut EOFCreateInputs, - ) -> Option { - for inspector in self.inspectors.iter_mut() { - if let Some(outcome) = inspector.eofcreate(context, inputs) { - return Some(outcome); - } - } - None - } + // fn eofcreate( + // &mut self, + // context: &mut EvmContext, + // inputs: &mut EOFCreateInputs, + // ) -> Option { + // None + // } - fn eofcreate_end( - &mut self, - context: &mut EvmContext, - inputs: &EOFCreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - let mut outcome = outcome; - for inspector in self.inspectors.iter_mut() { - outcome = inspector.eofcreate_end(context, inputs, outcome); - } - outcome - } + // fn eofcreate_end( + // &mut self, + // context: &mut EvmContext, + // inputs: &EOFCreateInputs, + // outcome: CreateOutcome, + // ) -> CreateOutcome { + // outcome + // } - #[inline] - fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - for inspector in self.inspectors.iter_mut() { - inspector.selfdestruct(contract, target, value); - } - } + // #[inline] + // fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {} } diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 173cc90..904a126 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -2,7 +2,7 @@ use hashbrown::{HashMap, HashSet}; use revm::{ interpreter::{ opcode::{self}, - CreateInputs, CreateOutcome, InstructionResult, Interpreter, OpCode, + CreateInputs, CreateOutcome, Interpreter, OpCode, }, primitives::{Address, U256}, Database, EvmContext, Inspector, @@ -10,6 +10,7 @@ use revm::{ use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; +#[derive(Default)] pub struct BugInspector { /// Change the created address to another address pub create_address_overrides: HashMap, diff --git a/src/lib.rs b/src/lib.rs index 39d0de6..7197995 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use crate::{fork_provider::ForkProvider, logs::LogsInspector, response::RevmResult}; +use crate::{fork_provider::ForkProvider, logs::LogInspector, response::RevmResult}; use ::revm::{ db::DbAccount, primitives::{ @@ -9,6 +9,7 @@ use ::revm::{ }; #[cfg(feature = "redis")] use cache::redis_cache::RedisProviderCache as DefaultProviderCache; +use chain_inspector::ChainInspector; use hashbrown::{HashMap, HashSet}; #[cfg(not(feature = "redis"))] @@ -43,7 +44,7 @@ mod logs; pub mod response; pub use common::*; use hex::ToHex; -use instrument::{BugData, Heuristics, InstrumentConfig}; +use instrument::{bug_inspector::BugInspector, BugData, Heuristics, InstrumentConfig}; use ruint::aliases::U256; use std::{cell::Cell, env, str::FromStr}; use tracing::{debug, info, trace}; @@ -85,7 +86,7 @@ pub struct TinyEvmContext {} #[pyclass(unsendable)] pub struct TinyEVM { /// REVM instance - pub exe: Option>, + pub exe: Option>, pub owner: Address, /// Snapshots of account state pub snapshots: HashMap, @@ -568,11 +569,16 @@ impl TinyEVM { db.insert_account_info(owner, account); // let mut builder = Evm::builder(); - let inspector = LogsInspector { + let log_inspector = LogInspector { trace_enabled: enable_call_trace, - traces: Vec::new(), - logs: Vec::new(), - override_addresses: HashMap::with_capacity(32), + ..LogInspector::default() + }; + + let bug_inspector = BugInspector::default(); + + let inspector = ChainInspector { + log_inspector: Some(log_inspector), + bug_inspector: Some(bug_inspector), }; // builder = builder.with_external_context(inspector); diff --git a/src/logs.rs b/src/logs.rs index 5e53c79..46dac63 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -39,8 +39,8 @@ pub struct Log { } /// An inspector that collects call traces. -#[derive(Debug)] -pub struct LogsInspector { +#[derive(Debug, Default)] +pub struct LogInspector { /// Traced enabled? pub trace_enabled: bool, /// The collected traces @@ -51,7 +51,7 @@ pub struct LogsInspector { pub override_addresses: HashMap, } -impl Inspector for LogsInspector +impl Inspector for LogInspector where DB: Database, { From e9458c1b62508f022c031fcc77c1fe632496dbdc Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 11 Jul 2024 16:35:20 +0800 Subject: [PATCH 08/40] tweaking inspector --- src/fork_db.rs | 4 - src/instrument/bug_inspector.rs | 5 +- src/lib.rs | 127 +++++++++++++++++--------------- tests/revm_test.rs | 22 ++---- 4 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/fork_db.rs b/src/fork_db.rs index fad0bf9..2cd6ac4 100644 --- a/src/fork_db.rs +++ b/src/fork_db.rs @@ -60,8 +60,6 @@ pub struct ForkDB { /// Optional instrument config pub instrument_config: Option, - /// Instrument data collected - pub instrument_data: InstrumentData, } impl Clone for ForkDB { @@ -78,7 +76,6 @@ impl Clone for ForkDB { ignored_addresses: self.ignored_addresses.clone(), max_fork_depth: self.max_fork_depth, instrument_config: self.instrument_config.clone(), - instrument_data: self.instrument_data.clone(), } } } @@ -157,7 +154,6 @@ impl ForkDB { ignored_addresses: Default::default(), max_fork_depth, instrument_config: Some(InstrumentConfig::default()), - instrument_data: InstrumentData::default(), } } diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 904a126..6b9fa16 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -7,6 +7,7 @@ use revm::{ primitives::{Address, U256}, Database, EvmContext, Inspector, }; +use tracing::debug; use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; @@ -185,12 +186,14 @@ where } #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { let address = interp.contract().target_address; let address_index = self.record_seen_address(address); let pc = interp.program_counter(); let opcode = self.opcode; + debug!("Step end {}", opcode); + if self.instrument_config.pcs_by_address { self.record_pc(address, pc); } diff --git a/src/lib.rs b/src/lib.rs index 7197995..d37a374 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ use cache::filesystem_cache::FileSystemProviderCache as DefaultProviderCache; use dotenv::dotenv; use ethers_providers::{Http, Provider}; use eyre::{eyre, ContextCompat, Result}; -use fork_db::{ForkDB, InstrumentData}; +use fork_db::ForkDB; use lazy_static::lazy_static; use num_bigint::BigInt; use pyo3::prelude::*; @@ -46,7 +46,7 @@ pub use common::*; use hex::ToHex; use instrument::{bug_inspector::BugInspector, BugData, Heuristics, InstrumentConfig}; use ruint::aliases::U256; -use std::{cell::Cell, env, str::FromStr}; +use std::{cell::Cell, str::FromStr}; use tracing::{debug, info, trace}; lazy_static! { @@ -120,24 +120,53 @@ pub fn enable_tracing() -> Result<()> { // Implementations for use in Rust impl TinyEVM { - pub fn instrument_data(&self) -> &InstrumentData { - &self.exe.as_ref().unwrap().context.evm.db.instrument_data + fn log_inspector(&self) -> &LogInspector { + self.exe + .as_ref() + .unwrap() + .context + .external + .log_inspector + .as_ref() + .unwrap() + } + + fn bug_inspector(&self) -> &BugInspector { + self.exe + .as_ref() + .unwrap() + .context + .external + .bug_inspector + .as_ref() + .unwrap() + } + + fn bug_inspector_mut(&mut self) -> &mut BugInspector { + self.exe + .as_mut() + .unwrap() + .context + .external + .bug_inspector + .as_mut() + .unwrap() } pub fn bug_data(&self) -> &BugData { - &self.instrument_data().bug_data + &self.bug_inspector().bug_data } pub fn heuristics(&self) -> &Heuristics { - &self.instrument_data().heuristics + &self.bug_inspector().heuristics } pub fn pcs_by_address(&self) -> &HashMap> { - &self.instrument_data().pcs_by_address + &self.bug_inspector().pcs_by_address } pub fn created_addresses(&self) -> &Vec
{ - &self.instrument_data().created_addresses + &self.bug_inspector().created_addresses } /// Create a new TinyEVM instance without fork @@ -185,7 +214,7 @@ impl TinyEVM { let db = &mut self.exe.as_mut().unwrap().context.evm.db; db.accounts.remove(&addr); - let managed_addresses = &mut db.instrument_data.managed_addresses; + let managed_addresses = &mut self.bug_inspector_mut().managed_addresses; managed_addresses.remove(&addr); Ok(()) @@ -213,23 +242,15 @@ impl TinyEVM { // Reset instrumentation, self.clear_instrumentation(); - let db = &mut self.exe.as_mut().unwrap().context.evm.db; + self.bug_inspector_mut().pcs_by_address.clear(); // If don't want to trace the deploy PCs - db.instrument_data.pcs_by_address.clear(); // If don't want to trace the deploy PCs - - if let Some(exe) = self.exe.take() { - let exe = exe - .modify() - .modify_tx_env(|tx| { - tx.caller = owner; - tx.transact_to = TransactTo::Create; - tx.data = contract_bytecode.clone().into(); - tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); - }) - .modify_cfg_env(|env| env.limit_contract_code_size = Some(0x24000)) - .build(); - self.exe = Some(exe); + { + let tx = self.exe.as_mut().unwrap().tx_mut(); + tx.caller = owner; + tx.transact_to = TransactTo::Create; + tx.data = contract_bytecode.clone().into(); + tx.value = value; + tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); } let nonce = self.exe.as_ref().unwrap().tx().nonce.unwrap_or_default(); @@ -290,17 +311,14 @@ impl TinyEVM { addresses, address ); if !addresses.is_empty() { - self.exe - .as_mut() - .unwrap() - .context - .evm - .db - .instrument_data + self.bug_inspector_mut() .managed_addresses .insert(address, addresses); } + let logs = self.log_inspector().logs.clone(); + let traces = self.log_inspector().traces.clone(); + trace!("deploy result: {:?}", result); let revm_result = RevmResult { @@ -308,8 +326,8 @@ impl TinyEVM { bug_data, heuristics, seen_pcs, - transient_logs: vec![], - traces: vec![], + traces, + transient_logs: logs, ignored_addresses: Default::default(), }; let mut resp: Response = revm_result.into(); @@ -336,18 +354,13 @@ impl TinyEVM { debug!("db in contract_call: {:?}", self.exe.as_ref().unwrap().db()); debug!("sender {:?}", sender.encode_hex::(),); - if let Some(exe) = self.exe.take() { - let exe = exe - .modify() - .modify_tx_env(|tx| { - tx.caller = sender; - tx.transact_to = TransactTo::Call(contract); - tx.data = data.into(); - tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); - }) - .build(); - self.exe = Some(exe); + { + let tx = self.exe.as_mut().unwrap().tx_mut(); + tx.caller = sender; + tx.transact_to = TransactTo::Call(contract); + tx.data = data.into(); + tx.value = value; + tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); } // let mut traces = vec![]; @@ -378,11 +391,7 @@ impl TinyEVM { debug!("contract_call result: {:?}", result); if !addresses.is_empty() { - let exe = self.exe.as_mut().unwrap(); - exe.context - .evm - .db - .instrument_data + self.bug_inspector_mut() .managed_addresses .insert(contract, addresses); } @@ -391,15 +400,17 @@ impl TinyEVM { let ignored_addresses = db.ignored_addresses.clone(); let ignored_addresses = ignored_addresses.into_iter().map(Into::into).collect(); - // let logs = self.exe.as_ref().unwrap().context.external; + let log_inspector = self.log_inspector(); + let logs = log_inspector.logs.clone(); + let traces = log_inspector.traces.clone(); let revm_result = RevmResult { result: result.map_err(|e| eyre!(e)), bug_data, heuristics, seen_pcs, - transient_logs: Vec::new(), // todo logs, - traces: Vec::new(), // todo + traces, + transient_logs: logs, ignored_addresses, }; revm_result.into() @@ -1074,10 +1085,10 @@ impl TinyEVM { } pub fn clear_instrumentation(&mut self) { - let db = &mut self.exe.as_mut().unwrap().context.evm.db; - db.instrument_data.bug_data.clear(); - db.instrument_data.created_addresses.clear(); - db.instrument_data.heuristics = Default::default(); + let bug_inspector = self.bug_inspector_mut(); + bug_inspector.bug_data.clear(); + bug_inspector.created_addresses.clear(); + bug_inspector.heuristics = Default::default(); } /// Restore a snapshot for an account, raise error if there is no snapshot for the account diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 4d2f1a0..921a429 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -219,6 +219,7 @@ fn single_bugtype_test_helper( #[test] fn test_overflow() { + let _ = enable_tracing(); let u256_max_as_hex = format!("{:#x}", U256::MAX); let contract_hex = include_str!("../tests/contracts/IntegerOverflowAdd_deploy.hex"); let num_runs = 1; @@ -1006,10 +1007,9 @@ fn test_get_set_balance() { let balance = vm.get_eth_balance(owner).unwrap(); assert_eq!(UZERO, balance, "Expect empty account has zero balance"); - vm.set_account_balance(owner.into(), target_balance) - .unwrap(); + vm.set_account_balance(owner, target_balance).unwrap(); - let balance = vm.get_eth_balance(owner.into()).unwrap(); + let balance = vm.get_eth_balance(owner).unwrap(); assert_eq!( balance, target_balance, "Expect changed to the target balance" @@ -1024,7 +1024,7 @@ fn test_get_set_balance() { assert!(resp.success, "Deployment should succeed"); let addr = Address::from_slice(&resp.data); - vm.set_account_balance(addr.into(), target_balance).unwrap(); + vm.set_account_balance(addr, target_balance).unwrap(); let bin = hex::decode(fn_sig_to_prefix("selfbalance()")).unwrap(); let resp = vm.contract_call_helper(addr, owner, bin, UZERO, None); @@ -1102,10 +1102,7 @@ fn test_seen_pcs() { ); assert!(resp.success, "Call error {:?}", resp); - let seen_pcs = &vm - .instrument_data() - .pcs_by_address - .get(&Address::new(address.0)); + let seen_pcs = &vm.pcs_by_address().get(&Address::new(address.0)); assert!( seen_pcs.is_some(), "Seen PCs should be found for the target contract " @@ -1121,13 +1118,6 @@ fn test_runtime_configuration() { deploy_hex!("../tests/contracts/contract_creation_B.hex", vm, address); let address = Address::new(address.0); - let config = InstrumentConfig { - pcs_by_address: false, - ..Default::default() - }; - - // vm.db.instrument_config = Some(config); - vm.set_account_balance( *OWNER, U256::from_str_radix("9999999999999999999999", 16).unwrap(), @@ -1145,7 +1135,7 @@ fn test_runtime_configuration() { ); assert!(resp.success, "Call error {:?}", resp); - let seen_pcs = &vm.instrument_data().pcs_by_address.get(&address); + let seen_pcs = &vm.pcs_by_address().get(&address); assert!( seen_pcs.is_none() || seen_pcs.unwrap().is_empty(), "No PCs by address should be recorded" From eb5b1c006170f7c1ffea78618ce236c8f7fcf3c1 Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 11 Jul 2024 17:21:59 +0800 Subject: [PATCH 09/40] added bug data --- src/chain_inspector.rs | 5 +---- src/instrument/bug_inspector.rs | 9 ++++----- src/lib.rs | 9 ++++++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/chain_inspector.rs b/src/chain_inspector.rs index 1ca5913..d09fac6 100644 --- a/src/chain_inspector.rs +++ b/src/chain_inspector.rs @@ -1,4 +1,4 @@ -use revm::interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs}; +use revm::interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome}; use revm::primitives::Log; use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; @@ -12,9 +12,6 @@ pub struct ChainInspector { } impl Inspector for ChainInspector { - // #[inline] - // fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) {} - #[inline] fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { if let Some(ins) = self.log_inspector.as_mut() { diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 6b9fa16..d3c0a5f 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -7,7 +7,6 @@ use revm::{ primitives::{Address, U256}, Database, EvmContext, Inspector, }; -use tracing::debug; use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; @@ -25,11 +24,12 @@ pub struct BugInspector { pub created_addresses: Vec
, // Managed addresses: contract -> addresses created by any transaction from the contract pub managed_addresses: HashMap>, - pub opcode_index: usize, /// Stack inputs of the current opcodes. Only updated when the opcode is interesting inputs: Vec, /// Current opcode opcode: u8, + // Current program counter + pc: usize, /// Current index in the execution. For tracking peephole optimized if-statement step_index: u64, last_index_sub: u64, @@ -128,6 +128,7 @@ where let _ = context; self.opcode = interp.current_opcode(); let opcode = OpCode::new(self.opcode); + self.pc = interp.program_counter(); if let Some(OpCode::EQ) = opcode { self.last_index_eq = self.step_index; @@ -189,10 +190,8 @@ where fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { let address = interp.contract().target_address; let address_index = self.record_seen_address(address); - let pc = interp.program_counter(); let opcode = self.opcode; - - debug!("Step end {}", opcode); + let pc = self.pc; if self.instrument_config.pcs_by_address { self.record_pc(address, pc); diff --git a/src/lib.rs b/src/lib.rs index d37a374..f3fcabe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ use ::revm::{ use cache::redis_cache::RedisProviderCache as DefaultProviderCache; use chain_inspector::ChainInspector; use hashbrown::{HashMap, HashSet}; +use revm::inspector_handle_register; #[cfg(not(feature = "redis"))] use cache::filesystem_cache::FileSystemProviderCache as DefaultProviderCache; @@ -379,9 +380,6 @@ impl TinyEVM { exe.transact_commit() }; - let bug_data = self.bug_data().clone(); - let heuristics = self.heuristics().clone(); - let seen_pcs = self.pcs_by_address().clone(); let addresses = self.created_addresses().clone(); info!( "created addresses from contract call: {:?} for {:?}", @@ -396,6 +394,10 @@ impl TinyEVM { .insert(contract, addresses); } + let bug_data = self.bug_data().clone(); + let heuristics = self.heuristics().clone(); + let seen_pcs = self.pcs_by_address().clone(); + let db = &self.exe.as_ref().unwrap().context.evm.db; let ignored_addresses = db.ignored_addresses.clone(); let ignored_addresses = ignored_addresses.into_iter().map(Into::into).collect(); @@ -598,6 +600,7 @@ impl TinyEVM { .modify_env(|e| *e = Box::new(env.clone())) .with_db(db.clone()) .with_external_context(inspector) + .append_handler_register(inspector_handle_register) .build(); let tinyevm = Self { exe: Some(exe), From 154f948c6ade7b549a8b66f7276182871a1af15b Mon Sep 17 00:00:00 2001 From: chen Date: Thu, 11 Jul 2024 18:11:20 +0800 Subject: [PATCH 10/40] create override --- src/instrument/bug_inspector.rs | 24 +++++++++++++++++----- src/lib.rs | 35 ++++++++++++++++++--------------- src/logs.rs | 18 ----------------- tests/revm_test.rs | 8 ++++++-- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index d3c0a5f..1a1a659 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -7,6 +7,7 @@ use revm::{ primitives::{Address, U256}, Database, EvmContext, Inspector, }; +use tracing::{debug, warn}; use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; @@ -442,17 +443,30 @@ where #[inline] fn create_end( &mut self, - _context: &mut EvmContext, + context: &mut EvmContext, _inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { - let CreateOutcome { result, address } = outcome; + let CreateOutcome { result, address } = &outcome; if let Some(address) = address { - if let Some(override_address) = self.create_address_overrides.get(&address) { - return CreateOutcome::new(result, Some(*override_address)); + if let Some(override_address) = self.create_address_overrides.get(address) { + debug!( + "Overriding created address {:?} with {:?}", + address, override_address + ); + let state = &mut context.journaled_state.state; + if let Some(value) = state.remove(address) { + state.insert(*override_address, value); + } else { + warn!( + "Contract created but no state associated with it? Contract address: {:?}", + address + ); + } + return CreateOutcome::new(result.to_owned(), Some(*override_address)); } } - CreateOutcome::new(result, address) + outcome } } diff --git a/src/lib.rs b/src/lib.rs index f3fcabe..404c7b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,20 +254,28 @@ impl TinyEVM { tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); } - let nonce = self.exe.as_ref().unwrap().tx().nonce.unwrap_or_default(); + // todo_cl this is read from global state, might be wrong + let nonce = self + .exe + .as_ref() + .unwrap() + .context + .evm + .db + .accounts + .get(&owner) + .map_or(0, |a| a.info.nonce); let address = owner.create(nonce); debug!("Calculated addresss: {:?}", address); - // todo add the inspector to the exe - + if let Some(force_address) = force_address { + self.bug_inspector_mut() + .create_address_overrides + .insert(address, force_address); + } let result = self.exe.as_mut().unwrap().transact_commit(); - // if let Some(force_address) = force_address { - // override_addresses.insert(address, force_address); - // self.clone_account(address, force_address, true)?; - // } - // debug!("db {:?}", self.exe.as_ref().unwrap().db()); // debug!("sender {:?}", owner.encode_hex::(),); @@ -292,11 +300,6 @@ impl TinyEVM { }; if collision { - info!( - "Found address collision, reset the existing account: {}", - address.encode_hex::() - ); - return Err(eyre!( "Address collision for {}", address.encode_hex::() @@ -332,9 +335,9 @@ impl TinyEVM { ignored_addresses: Default::default(), }; let mut resp: Response = revm_result.into(); - if let Some(force_address) = force_address { - resp.data = force_address.0.to_vec(); - } + // if let Some(force_address) = force_address { + // resp.data = force_address.0.to_vec(); + // } Ok(resp) } diff --git a/src/logs.rs b/src/logs.rs index 46dac63..47c8cb7 100644 --- a/src/logs.rs +++ b/src/logs.rs @@ -47,8 +47,6 @@ pub struct LogInspector { pub traces: Vec, /// EVM events/logs collected during execution pub logs: Vec, - /// Overrides for addresses. Any address created in this map keys will be overriden with the value - pub override_addresses: HashMap, } impl Inspector for LogInspector @@ -140,20 +138,4 @@ where result } - - #[inline] - fn create_end( - &mut self, - _context: &mut EvmContext, - _inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - let CreateOutcome { result, address } = outcome; - if let Some(address) = address { - if let Some(override_address) = self.override_addresses.get(&address) { - return CreateOutcome::new(result, Some(*override_address)); - } - } - CreateOutcome::new(result, address) - } } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 921a429..c64ccc6 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -397,6 +397,7 @@ fn test_deterministic_deploy() { #[test] fn test_deterministic_deploy_overwrite() -> Result<()> { + let _ = enable_tracing(); let contract_deploy_hex = include_str!("../tests/contracts/coverage.hex"); let contract_deploy_bin = hex::decode(contract_deploy_hex).unwrap(); let target_address = Address::from_slice(H160::random().as_bytes()); @@ -427,6 +428,7 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { let c1_code = { let accounts = &vm.exe.as_ref().unwrap().db().accounts; + println!("accounts: {:?}", accounts); let account = accounts .get(&target_address) .context("Expecting first account has non nil value")?; @@ -450,9 +452,11 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { c2 ); + let c2_address = Address::from_slice(&c2.data); + assert_eq!( - c1.data, c2.data, - "Deploy same contract with same salt should have same address" + c1_address, c2_address, + "Deploy same contract to the same forced addess should result in the same address" ); let c2_code = { From bc8c72ad0fab2cebbbd88306f2647817fc84ded3 Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 12 Jul 2024 10:20:39 +0800 Subject: [PATCH 11/40] pass test_distance_signed --- src/common.rs | 13 +++++++++++++ src/instrument/bug_inspector.rs | 13 ++++--------- tests/revm_test.rs | 17 ++++++++++++++--- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/common.rs b/src/common.rs index c7a6b3a..1dd1b98 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,8 +1,11 @@ +use std::cmp::Ordering; + /// Common constants, data structures and functions to be used by both rust-evm and revm use eyre::Result; use hex::ToHex; use num_bigint::BigInt; use primitive_types::H256; +use revm::interpreter::instructions::i256::i256_cmp; use ruint::aliases::U256; use sha3::{Digest, Keccak256}; @@ -68,3 +71,13 @@ pub fn bigint_to_ruint_u256(b: &BigInt) -> Result { Ok(U256::from_be_slice(&padded_bytes)) } + +/// Returns the distance between two U256 numbers +#[inline(always)] +pub fn i256_diff(first: &U256, second: &U256) -> (U256, bool) { + match i256_cmp(first, second) { + Ordering::Equal => (U256::ZERO, false), + Ordering::Greater => first.overflowing_sub(*second), + Ordering::Less => second.overflowing_sub(*first), + } +} diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 1a1a659..e248914 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -1,14 +1,13 @@ use hashbrown::{HashMap, HashSet}; use revm::{ - interpreter::{ - opcode::{self}, - CreateInputs, CreateOutcome, Interpreter, OpCode, - }, + interpreter::{instructions::i256, opcode, CreateInputs, CreateOutcome, Interpreter, OpCode}, primitives::{Address, U256}, Database, EvmContext, Inspector, }; use tracing::{debug, warn}; +use crate::i256_diff; + use super::{Bug, BugData, BugType, Heuristics, InstrumentConfig}; #[derive(Default)] @@ -300,11 +299,7 @@ where self.inputs.get(1), interp.stack().peek(0), ) { - let mut distance = if a >= b { - a.overflowing_sub(*b).0 - } else { - b.overflowing_sub(*a).0 - }; + let (mut distance, _) = i256_diff(a, b); if r == U256::ZERO { distance = distance.saturating_add(U256::from(1)); } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index c64ccc6..17866b5 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1292,6 +1292,7 @@ fn test_seen_addresses() { #[test] fn test_distance_signed() { + let _ = enable_tracing(); deploy_hex!("../tests/contracts/test_distance_signed.hex", vm, address); let address = Address::new(address.0); let fn_sig = "sign_distance(int256)"; @@ -1326,11 +1327,21 @@ fn test_distance_signed() { .map(|b| b.distance) .collect::>(); - println!("Missed branches: {:?}", missed_branches_distance); + println!("Missed branches distances: {:?}", missed_branches_distance); - assert!(expected_distances + let failed_to_find = expected_distances .iter() - .all(|d| { missed_branches_distance.contains(&U256::from(*d)) })); + .filter(|d| !(missed_branches_distance.contains(&U256::from(**d)))) + .collect::>(); + + println!( + "Failed to find missed branches distances: {:?}", + failed_to_find + ); + assert!( + failed_to_find.is_empty(), + "All expected distances should be found" + ); } #[test] From 308df43a5eb02e0dabf7be979fe3432d60cee689 Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 12 Jul 2024 14:28:42 +0800 Subject: [PATCH 12/40] fix test_exp_overflow --- Cargo.lock | 1135 +++++++++++++++++- Cargo.toml | 3 +- src/chain_inspector.rs | 2 +- src/instrument/bug_inspector.rs | 5 +- src/{logs.rs => instrument/log_inspector.rs} | 6 +- src/instrument/mod.rs | 1 + src/lib.rs | 29 +- src/response.rs | 6 +- tests/revm_test.rs | 23 +- 9 files changed, 1130 insertions(+), 80 deletions(-) rename src/{logs.rs => instrument/log_inspector.rs} (95%) diff --git a/Cargo.lock b/Cargo.lock index 3b90efa..66a660d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,11 +65,181 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "alloy" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba1c79677c9ce51c8d45e20845b05e6fb070ea2c863fba03ad6af2c778474bd" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", +] + +[[package]] +name = "alloy-chains" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1752d7d62e2665da650a36d84abbf239f812534475d51f072a49a533513b7cdd" +dependencies = [ + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da374e868f54c7f4ad2ad56829827badca388efd645f8cf5fccc61c2b5343504" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47b2a620fd588d463ccf0f5931b41357664b293a8d31592768845a2a101bb9e" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow 0.6.13", +] + +[[package]] +name = "alloy-eips" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76ecab54890cdea1e4808fc0891c7e6cfcf71fe1a9fe26810c7280ef768f4ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca15afde1b6d15e3fc1c97421262b1bbb37aee45752e3c8b6d6f13f776554ff" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6f34930b7e3e2744bcc79056c217f00cb2abb33bc5d4ff88da7623c5bb078b" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f6895fc31b48fa12306ef9b4f78b7764f8bd6d7d91cdb0a40e233704a0f23f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + [[package]] name = "alloy-primitives" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" dependencies = [ "alloy-rlp", "bytes", @@ -87,16 +257,339 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-provider" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c538bfa893d07e27cb4f3c1ab5f451592b7c526d511d62b576a2ce59e146e4a" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-pubsub" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702f330b7da123a71465ab9d39616292f8344a2811c28f2cc8d8438a69d79e35" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "alloy-rlp" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" dependencies = [ + "alloy-rlp-derive", "arrayvec", "bytes", ] +[[package]] +name = "alloy-rlp-derive" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83524c1f6162fcb5b0decf775498a125066c86dda6066ed609531b0e912f85a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba31bae67773fd5a60020bea900231f8396202b7feca4d0c70c6b59308ab4a8" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f2fbe956a3e0f0975c798f488dc6be96b669544df3737e18f4a325b42f4c86" +dependencies = [ + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd473d98ec552f8229cd6d566bd2b0bbfc5bb4efcefbb5288c834aa8fd832020" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "jsonwebtoken 9.3.0", + "rand", + "serde", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4123ee21f99ba4bd31bfa36ba89112a18a500f8b452f02b35708b1b951e2b9" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-serde" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9416c52959e66ead795a11f4a86c248410e9e368a0765710e57055b8a1774dd6" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33753c09fa1ad85e5b092b8dc2372f1e337a42e84b9b4cff9fede75ba4adb32" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-local" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40a37dc216c269b8a7244047cb1c18a9c69f7a0332ab2c4c2aa4cbb1a31468b" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand", + "thiserror", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.68", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.68", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +dependencies = [ + "serde", + "winnow 0.6.13", +] + +[[package]] +name = "alloy-sol-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b51a291f949f755e6165c3ed562883175c97423703703355f4faa4b7d0a57c" +dependencies = [ + "alloy-json-rpc", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d65871f9f1cafe1ed25cde2f1303be83e6473e995a2d56c275ae4fcce6119c" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest 0.12.5", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173cefa110afac7a53cf2e75519327761f2344d305eea2993f3af1b2c1fc1c44" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c0aff8af5be5e58856c5cdd1e46db2c67c7ecd3a652d9100b4822c96c899947" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures", + "http 1.1.0", + "rustls 0.23.11", + "serde_json", + "tokio", + "tokio-tungstenite 0.23.1", + "tracing", + "ws_stream_wasm", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -127,7 +620,7 @@ dependencies = [ "ark-std 0.4.0", "derivative", "digest 0.10.7", - "itertools", + "itertools 0.10.5", "num-bigint", "num-traits", "paste", @@ -227,6 +720,28 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -338,6 +853,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bit-set" version = "0.5.3" @@ -666,7 +1187,7 @@ dependencies = [ "clap", "criterion-plot", "csv", - "itertools", + "itertools 0.10.5", "lazy_static", "num-traits", "oorandom", @@ -688,7 +1209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -774,6 +1295,19 @@ dependencies = [ "cipher", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -844,6 +1378,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "dotenv" version = "0.15.0" @@ -1147,7 +1687,7 @@ dependencies = [ "futures-locks", "futures-util", "instant", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", @@ -1174,17 +1714,17 @@ dependencies = [ "futures-timer", "futures-util", "hashers", - "http", + "http 0.2.12", "instant", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "once_cell", "pin-project", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", "thiserror", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.20.1", "tracing", "tracing-futures", "url", @@ -1267,6 +1807,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1391,6 +1946,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "fxhash" version = "0.2.1" @@ -1418,8 +1979,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1468,7 +2031,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap", "slab", "tokio", @@ -1558,6 +2121,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1565,7 +2139,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1592,8 +2189,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1605,6 +2202,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" version = "0.24.2" @@ -1612,11 +2228,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", - "hyper", - "rustls", + "http 0.2.12", + "hyper 0.14.29", + "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -1707,6 +2359,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "interprocess" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67bafc2f5dbdad79a6d925649758d5472647b416028099f0b829d1b67fdd47d3" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -1722,6 +2389,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1744,13 +2420,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ "base64 0.21.7", - "pem", + "pem 1.1.1", "ring 0.16.20", "serde", "serde_json", "simple_asn1", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem 3.0.4", + "ring 0.17.8", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" version = "0.13.3" @@ -1825,7 +2516,16 @@ dependencies = [ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] [[package]] name = "maplit" @@ -1883,6 +2583,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2059,6 +2776,50 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" @@ -2148,6 +2909,16 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2217,6 +2988,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "plotters" version = "0.3.6" @@ -2296,6 +3073,30 @@ dependencies = [ "toml_edit 0.21.1", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -2471,6 +3272,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redis" version = "0.25.4" @@ -2551,9 +3358,9 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", "hyper-rustls", "ipnet", "js-sys", @@ -2562,22 +3369,61 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-pemfile", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.4.1", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -2796,10 +3642,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +dependencies = [ + "once_cell", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki 0.102.5", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -2809,6 +3669,22 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2819,6 +3695,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -2885,6 +3772,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2946,6 +3842,29 @@ dependencies = [ "cc", ] +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.11.0" @@ -3264,12 +4183,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.68", +] + [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3319,6 +4256,7 @@ dependencies = [ name = "tevm" version = "0.1.0" dependencies = [ + "alloy", "criterion", "dotenv", "ethers", @@ -3494,14 +4432,47 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.11", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -3512,11 +4483,27 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", + "tungstenite 0.20.1", + "webpki-roots 0.25.4", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.11", + "rustls-pki-types", "tokio", - "tokio-rustls", - "tungstenite", - "webpki-roots", + "tokio-rustls 0.26.0", + "tungstenite 0.23.0", + "webpki-roots 0.26.3", ] [[package]] @@ -3577,6 +4564,28 @@ dependencies = [ "winnow 0.6.13", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3589,6 +4598,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3669,17 +4679,37 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.12", "httparse", "log", "rand", - "rustls", + "rustls 0.21.12", "sha1", "thiserror", "url", "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "rustls 0.23.11", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -3794,6 +4824,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -3916,6 +4952,21 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -4114,6 +5165,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "ws_stream_wasm" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index 6d04358..0c16274 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,10 +36,11 @@ tokio = { version = "1.38.0", features = ["full"] } strum_macros = "0.26.4" hashbrown = "*" redis = { version= "0.25.4", optional = true} +alloy = { version = "0.1.4", features = ["full"] } [dev-dependencies] criterion = {version="0.3.6", features=["html_reports"] } [features] default = [] -redis=["dep:redis"] \ No newline at end of file +redis=["dep:redis"] diff --git a/src/chain_inspector.rs b/src/chain_inspector.rs index d09fac6..1e99929 100644 --- a/src/chain_inspector.rs +++ b/src/chain_inspector.rs @@ -3,7 +3,7 @@ use revm::primitives::Log; use revm::{interpreter::Interpreter, Database, EvmContext, Inspector}; use crate::instrument::bug_inspector::BugInspector; -use crate::logs::LogInspector; +use crate::instrument::log_inspector::LogInspector; /// A chain of inspectors, ecch inspector will be executed in order. pub struct ChainInspector { diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index e248914..40dd5aa 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -1,6 +1,6 @@ use hashbrown::{HashMap, HashSet}; use revm::{ - interpreter::{instructions::i256, opcode, CreateInputs, CreateOutcome, Interpreter, OpCode}, + interpreter::{opcode, CreateInputs, CreateOutcome, Interpreter, OpCode}, primitives::{Address, U256}, Database, EvmContext, Inspector, }; @@ -243,7 +243,8 @@ where } } } - (opcode::EXP, true) => { + (opcode::EXP, _) => { + println!("checking-exp-overflow"); // todo_cl check for overflow if let (Some(a), Some(b), Ok(r)) = ( self.inputs.first(), diff --git a/src/logs.rs b/src/instrument/log_inspector.rs similarity index 95% rename from src/logs.rs rename to src/instrument/log_inspector.rs index 47c8cb7..d9784a8 100644 --- a/src/logs.rs +++ b/src/instrument/log_inspector.rs @@ -1,11 +1,7 @@ use crate::CALL_DEPTH; -use hashbrown::HashMap; use lazy_static::lazy_static; use revm::{ - interpreter::{ - CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, - InstructionResult, - }, + interpreter::{CallInputs, CallOutcome, CallScheme, CallValue, InstructionResult}, primitives::{Address, Bytes, Log as EvmLog, B256, U256}, Database, EvmContext, Inspector, }; diff --git a/src/instrument/mod.rs b/src/instrument/mod.rs index 5adeb1d..7103a22 100644 --- a/src/instrument/mod.rs +++ b/src/instrument/mod.rs @@ -1,3 +1,4 @@ pub mod bug; pub use bug::*; pub mod bug_inspector; +pub mod log_inspector; diff --git a/src/lib.rs b/src/lib.rs index 404c7b1..5eb0d55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use crate::{fork_provider::ForkProvider, logs::LogInspector, response::RevmResult}; +use crate::{fork_provider::ForkProvider, response::RevmResult}; use ::revm::{ db::DbAccount, primitives::{ @@ -39,13 +39,13 @@ pub mod fork_db; /// Cache for the fork requests pub mod fork_provider; pub mod instrument; -/// Logging -mod logs; /// Provide response data structure from EVM pub mod response; pub use common::*; use hex::ToHex; -use instrument::{bug_inspector::BugInspector, BugData, Heuristics, InstrumentConfig}; +use instrument::{ + bug_inspector::BugInspector, log_inspector::LogInspector, BugData, Heuristics, InstrumentConfig, +}; use ruint::aliases::U256; use std::{cell::Cell, str::FromStr}; use tracing::{debug, info, trace}; @@ -334,11 +334,8 @@ impl TinyEVM { transient_logs: logs, ignored_addresses: Default::default(), }; - let mut resp: Response = revm_result.into(); - // if let Some(force_address) = force_address { - // resp.data = force_address.0.to_vec(); - // } - Ok(resp) + + Ok(revm_result.into()) } /// Send a `transact_call` to a `contract` from the `sender` with raw @@ -355,9 +352,6 @@ impl TinyEVM { self.clear_instrumentation(); CALL_DEPTH.get_or_default().set(0); - debug!("db in contract_call: {:?}", self.exe.as_ref().unwrap().db()); - debug!("sender {:?}", sender.encode_hex::(),); - { let tx = self.exe.as_mut().unwrap().tx_mut(); tx.caller = sender; @@ -367,17 +361,6 @@ impl TinyEVM { tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); } - // let mut traces = vec![]; - // let mut logs = vec![]; - // let trace_enabled = matches!(env::var("TINYEVM_CALL_TRACE_ENABLED"), Ok(val) if val == "1"); - - // let inspector = LogsInspector { - // trace_enabled, - // traces: &mut traces, - // logs: &mut logs, - // override_addresses: &mut Default::default(), - // }; - let result = { let exe = self.exe.as_mut().unwrap(); exe.transact_commit() diff --git a/src/response.rs b/src/response.rs index c2c417e..2bd662d 100644 --- a/src/response.rs +++ b/src/response.rs @@ -14,8 +14,10 @@ use std::collections::HashMap as StdHashMap; use std::collections::HashSet as StdHashSet; use crate::{ - instrument::bug::*, - logs::{CallTrace, Log}, + instrument::{ + bug::*, + log_inspector::{CallTrace, Log}, + }, ruint_u256_to_bigint, trim_prefix, }; use primitive_types::H160; diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 17866b5..d6387f2 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -13,7 +13,7 @@ use std::iter::repeat_with; use std::ops::Add; use std::str::FromStr; use std::{collections::HashSet, env}; -use tinyevm::instrument::bug::{Bug, BugType, InstrumentConfig, MissedBranch}; +use tinyevm::instrument::bug::{Bug, BugType, MissedBranch}; use tinyevm::{ enable_tracing, fn_sig_to_prefix, ruint_u256_to_bigint, trim_prefix, TinyEVM, TX_GAS_LIMIT, @@ -820,14 +820,21 @@ fn test_set_get_code() { #[test] fn test_exp_overflow() { + let _ = enable_tracing(); let owner = *OWNER; - let mut vm = TinyEVM::default(); - deploy_hex!("../tests/contracts/exp_overflow.hex", exe, address); + deploy_hex!("../tests/contracts/exp_overflow.hex", vm, address); let fn_sig = "exp(uint256)"; let fn_sig_hex = fn_sig_to_prefix(fn_sig); let bin = format!("{}{:0>64x}", fn_sig_hex, 200); let bin = hex::decode(bin).unwrap(); + + println!("Calling deployed contract: {:?}", address); + println!( + "Current accounts: {:?}", + vm.exe.as_ref().unwrap().db().accounts + ); + let resp = vm.contract_call_helper(Address::new(address.0), owner, bin, UZERO, None); assert!( @@ -840,14 +847,12 @@ fn test_exp_overflow() { let resp = vm.contract_call_helper(Address::new(address.0), owner, bin, UZERO, None); + let bugs = &resp.bug_data; + assert!( - &resp - .bug_data - .clone() - .into_iter() - .any(|b| b.opcode == opcode::EXP), + &bugs.iter().any(|b| b.opcode == opcode::EXP), "Expecting exp overflow in {:?}", - &resp.bug_data + bugs ); } From 526c41330c444d6c3e264adeb60db8b3ffbd78bc Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 12 Jul 2024 15:13:21 +0800 Subject: [PATCH 13/40] fix test_heuristics --- src/instrument/bug_inspector.rs | 6 +++--- tests/revm_test.rs | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 40dd5aa..e8c64bb 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -313,13 +313,13 @@ where self.inputs.get(1), interp.stack().peek(0), ) { - let mut distance = if a >= b { + let mut distance = if a > b { a.overflowing_sub(*b).0 } else { b.overflowing_sub(*a).0 }; - if r == U256::ZERO { - distance = distance.saturating_add(U256::from(1)); + if r != U256::ZERO { + distance = U256::from(1); } self.heuristics.distance = distance; } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index d6387f2..6283ae1 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -107,6 +107,10 @@ fn t_erc20_balance_query(vm: &mut TinyEVM, address: Address, expected_balance: U assert_eq!(expected_balance, balance); } +fn setup() { + let _ = enable_tracing(); +} + /// Convenient function create binary for the solidty function: transfer(address,uint256) fn make_transfer_bin(to: Address, amount: U256) -> Vec { let prefix = fn_sig_to_prefix("transfer(address,uint256)"); @@ -219,7 +223,7 @@ fn single_bugtype_test_helper( #[test] fn test_overflow() { - let _ = enable_tracing(); + setup(); let u256_max_as_hex = format!("{:#x}", U256::MAX); let contract_hex = include_str!("../tests/contracts/IntegerOverflowAdd_deploy.hex"); let num_runs = 1; @@ -397,7 +401,7 @@ fn test_deterministic_deploy() { #[test] fn test_deterministic_deploy_overwrite() -> Result<()> { - let _ = enable_tracing(); + setup(); let contract_deploy_hex = include_str!("../tests/contracts/coverage.hex"); let contract_deploy_bin = hex::decode(contract_deploy_hex).unwrap(); let target_address = Address::from_slice(H160::random().as_bytes()); @@ -518,6 +522,7 @@ fn test_heuristics_inner( #[test] fn test_heuristics() { + setup(); // Test coverage(200) let input = 200; let expected_missed_branches: Vec = vec![ From ab1b6a5cf87c647aee18b5b3518c83aadb130247 Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 12 Jul 2024 16:08:19 +0800 Subject: [PATCH 14/40] fix tx_origin --- src/instrument/bug_inspector.rs | 62 +++++++++++++++++++++------------ tests/revm_test.rs | 15 ++++---- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index e8c64bb..9d452f5 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -139,11 +139,12 @@ where } // it's also possible to handle REVERT, INVALID here - + self.inputs.clear(); match opcode { Some( - OpCode::JUMPI - // possible overflows / underflows + op @ (OpCode::JUMPI + | OpCode::SSTORE + | OpCode::SLOAD | OpCode::ADD | OpCode::SUB | OpCode::MUL @@ -151,33 +152,22 @@ where | OpCode::SDIV | OpCode::SMOD | OpCode::EXP - // heuristic distance | OpCode::LT | OpCode::SLT | OpCode::GT | OpCode::SGT | OpCode::EQ - // possible truncation | OpCode::AND + | OpCode::ADDMOD + | OpCode::MULMOD), ) => { - self.inputs.clear(); - let a = interp.stack().peek(0); - let b = interp.stack().peek(1); - if let (Ok(a), Ok(b)) = (a, b) { - self.inputs.push(a); - self.inputs.push(b); - } - }, - Some( OpCode::ADDMOD | OpCode::MULMOD) => { - self.inputs.clear(); - let a = interp.stack().peek(0); - let b = interp.stack().peek(1); - let n = interp.stack().peek(2); - - if let (Ok(a), Ok(b), Ok(n)) = (a, b, n) { - self.inputs.push(a); - self.inputs.push(b); - self.inputs.push(n); + let num_inputs = op.inputs(); + for i in 0..num_inputs { + if let Ok(v) = interp.stack().peek(i as usize) { + self.inputs.push(v); + } else { + break; + } } } _ => {} @@ -359,6 +349,32 @@ where } } } + (opcode::SSTORE, true) => { + if let (Some(key), Some(value)) = (self.inputs.first(), self.inputs.get(1)) { + let bug = Bug::new( + BugType::Sstore(*key, *value), + self.opcode, + self.pc, + address_index, + ); + self.add_bug(bug); + } + } + (opcode::SLOAD, true) => { + if let Some(key) = self.inputs.first() { + let bug = Bug::new(BugType::Sload(*key), self.opcode, self.pc, address_index); + self.add_bug(bug); + } + } + (opcode::ORIGIN, true) => { + let bug = Bug::new( + BugType::TxOriginDependency, + self.opcode, + self.pc, + address_index, + ); + self.add_bug(bug); + } (opcode::JUMPI, true) => { // Check for missed branches let target_address = self.instrument_config.target_address; diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 6283ae1..dcb2663 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -302,6 +302,7 @@ fn test_timestamp_and_block_number() { #[test] fn test_tx_origin() { + setup(); let contract_hex = include_str!("../tests/contracts/tx_origin.hex"); let fn_args = ""; let fn_sig = "run()"; @@ -313,6 +314,7 @@ fn test_tx_origin() { #[test] fn test_tx_origin_v2() { + setup(); let contract_hex = include_str!("../tests/contracts/test_txorigin.hex"); let fn_args = ""; let fn_sig = "txorigin()"; @@ -958,14 +960,15 @@ fn test_blockhash() { #[test] fn test_tod() { - deploy_hex!("../tests/contracts/test_tod.hex", exe, addr); + setup(); + deploy_hex!("../tests/contracts/test_tod.hex", vm, addr); let owner = *OWNER; - exe.clear_instrumentation(); + vm.clear_instrumentation(); let bin = hex::decode(fn_sig_to_prefix("play_TOD27()")).unwrap(); - let resp = exe.contract_call_helper(Address::new(addr.0), owner, bin, UZERO, None); + let resp = vm.contract_call_helper(Address::new(addr.0), owner, bin, UZERO, None); assert!(resp.success, "Call should succeed"); - let bugs = exe.bug_data().clone(); + let bugs = vm.bug_data().clone(); let expected_sstore_pcs: HashSet = vec![501, 554, 561].into_iter().collect(); @@ -985,9 +988,9 @@ fn test_tod() { let bin = format!("{}{}", fn_sig_to_prefix("write_a(uint256)"), arg_hex); let bin = hex::decode(bin).unwrap(); - let resp = exe.contract_call_helper(Address::new(addr.0), owner, bin, UZERO, None); + let resp = vm.contract_call_helper(Address::new(addr.0), owner, bin, UZERO, None); assert!(resp.success, "Call should succeed"); - let bugs = exe.bug_data().clone(); + let bugs = vm.bug_data().clone(); println!("{:?}", bugs); From f4a2ca9f1f1c8c2ef4d392c1d86a3f52369557d1 Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 12 Jul 2024 16:09:50 +0800 Subject: [PATCH 15/40] fix test_deadloop --- tests/revm_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/revm_test.rs b/tests/revm_test.rs index dcb2663..147fb30 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -885,13 +885,13 @@ fn single_run_test_helper(contract_bin_hex: &str, fn_sig: &str, tests: Vec Date: Fri, 12 Jul 2024 16:19:09 +0800 Subject: [PATCH 16/40] fix test_events --- src/lib.rs | 23 ++++++++++++++++++++--- tests/revm_test.rs | 11 +++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5eb0d55..67d8079 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,7 +97,7 @@ pub struct TinyEVM { static mut TRACE_ENABLED: bool = false; -/// Enable global tracing +/// Enable printing of trace logs for debugging #[pyfunction] pub fn enable_tracing() -> Result<()> { use tracing_subscriber::{fmt, EnvFilter}; @@ -132,6 +132,17 @@ impl TinyEVM { .unwrap() } + fn log_inspector_mut(&mut self) -> &mut LogInspector { + self.exe + .as_mut() + .unwrap() + .context + .external + .log_inspector + .as_mut() + .unwrap() + } + fn bug_inspector(&self) -> &BugInspector { self.exe .as_ref() @@ -633,9 +644,15 @@ impl TinyEVM { } /// Toggle for enable mode, only makes sense when fork_url is set - pub fn toggle_enable_fork(&mut self, enable: bool) { + pub fn toggle_enable_fork(&mut self, enabled: bool) { let db = &mut self.exe.as_mut().unwrap().context.evm.db; - db.fork_enabled = enable; + db.fork_enabled = enabled; + } + + /// Set whether to log the traces of the EVM execution + pub fn set_evm_tracing(&mut self, enabled: bool) { + let log_inspector = self.log_inspector_mut(); + log_inspector.trace_enabled = enabled; } /// Get the current fork toggle status diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 147fb30..2d4cf7d 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1552,10 +1552,9 @@ fn test_sturdy_hack() -> Result<()> { #[test] fn test_events() -> Result<()> { - env::remove_var("TINYEVM_CALL_TRACE_ENABLED"); let bin = include_str!("../tests/contracts/TestEvents.hex"); - let mut evm = TinyEVM::new_offline()?; - let resp = evm.deploy(bin.into(), None)?; + let mut vm = TinyEVM::default(); + let resp = vm.deploy(bin.into(), None)?; assert!(resp.success, "Deploy error {:?}", resp); let contract = format!("0x{:0>40}", hex::encode(&resp.data)); println!("Contract address: {}", contract); @@ -1564,13 +1563,13 @@ fn test_events() -> Result<()> { "1401d2b5", // makeEvent(3232) U256::from(3232) ); - let resp = evm.contract_call(contract.clone(), None, Some(data.clone()), None)?; + let resp = vm.contract_call(contract.clone(), None, Some(data.clone()), None)?; assert!(resp.success, "Call error {:?}", resp); assert!(resp.events.is_empty(), "Expecting no events"); assert!(resp.traces.is_empty(), "Expecting no call traces"); - env::set_var("TINYEVM_CALL_TRACE_ENABLED", "1"); - let resp = evm.contract_call(contract.clone(), None, Some(data), None)?; + vm.set_evm_tracing(true); + let resp = vm.contract_call(contract.clone(), None, Some(data), None)?; assert!(resp.success, "Call error {:?}", resp); assert!(resp.events.len() == 1, "Expecting one event"); From d0ecb17485e244582511240a50c75d714ab7322a Mon Sep 17 00:00:00 2001 From: chen Date: Fri, 12 Jul 2024 17:41:53 +0800 Subject: [PATCH 17/40] fix test_call_trace --- src/instrument/bug.rs | 15 +++++++++----- src/instrument/bug_inspector.rs | 35 ++++++++++++++++++++++++++++++++- tests/revm_test.rs | 11 +++++++---- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/instrument/bug.rs b/src/instrument/bug.rs index 344ae44..f33b8ae 100644 --- a/src/instrument/bug.rs +++ b/src/instrument/bug.rs @@ -8,18 +8,23 @@ use strum_macros::Display; pub enum BugType { IntegerOverflow, IntegerSubUnderflow, - IntegerDivByZero, // op2 for DIV, SDIV is zero - IntegerModByZero, // op2 for MOD, SMOD, ADDMOD, MULMOD, is zero + /// op2 for DIV, SDIV is zero + IntegerDivByZero, + /// op2 for MOD, SMOD, ADDMOD, MULMOD, is zero + IntegerModByZero, PossibleIntegerTruncation, TimestampDependency, BlockNumberDependency, BlockValueDependency, TxOriginDependency, - Call(usize, H160), // Call(input_parameter_size, destination_address) + /// Call(input_parameter_size, destination_address) + Call(usize, H160), RevertOrInvalid, - Jumpi(usize), // Jumpi(dest) + /// Jumpi(dest) + Jumpi(usize), Sload(U256), - Sstore(U256, U256), // index, value + /// storage key, value + Sstore(U256, U256), Unclassified, } diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 9d452f5..991930a 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -1,4 +1,5 @@ use hashbrown::{HashMap, HashSet}; +use primitive_types::H160; use revm::{ interpreter::{opcode, CreateInputs, CreateOutcome, Interpreter, OpCode}, primitives::{Address, U256}, @@ -143,6 +144,10 @@ where match opcode { Some( op @ (OpCode::JUMPI + | OpCode::CALL + | OpCode::CALLCODE + | OpCode::DELEGATECALL + | OpCode::STATICCALL | OpCode::SSTORE | OpCode::SLOAD | OpCode::ADD @@ -187,7 +192,8 @@ where self.record_pc(address, pc); } - let success = interp.instruction_result.is_ok(); + let result = &interp.instruction_result; + let success = result.is_ok(); // Check for overflow and underflow match (opcode, success) { @@ -375,6 +381,33 @@ where ); self.add_bug(bug); } + + (opcode::CALL, _) + | (opcode::CALLCODE, _) + | (opcode::DELEGATECALL, _) + | (opcode::STATICCALL, _) => { + let in_len = { + if self.opcode == opcode::CALL || self.opcode == opcode::CALLCODE { + self.inputs.get(4) + } else { + self.inputs.get(3) + } + }; + let address = self.inputs.get(1); + + if let (Some(in_len), Some(callee)) = (in_len, address) { + let callee_bytes: [u8; 32] = callee.to_be_bytes(); + let callee = H160::from_slice(&callee_bytes[12..]); + let in_len = usize::try_from(in_len).unwrap(); + let bug = Bug::new( + BugType::Call(in_len, callee), + self.opcode, + self.pc, + address_index, + ); + self.add_bug(bug); + } + } (opcode::JUMPI, true) => { // Check for missed branches let target_address = self.instrument_config.target_address; diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 2d4cf7d..ee2499f 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -8,11 +8,11 @@ use primitive_types::{H160, H256}; use revm::interpreter::opcode::{self, CREATE, CREATE2, SELFDESTRUCT}; use revm::primitives::Address; use ruint::aliases::U256; +use std::collections::HashSet; use std::convert::TryInto; use std::iter::repeat_with; use std::ops::Add; use std::str::FromStr; -use std::{collections::HashSet, env}; use tinyevm::instrument::bug::{Bug, BugType, MissedBranch}; use tinyevm::{ @@ -85,6 +85,8 @@ fn check_expected_bugs_are_found(expected: Vec<(BugType, usize)>, found: Vec = expected_bugs.difference(&found_bugs).collect(); assert_eq!(0, diff.len(), "Expected bugs {diff:#?} should be found"); } @@ -333,7 +335,8 @@ fn test_tx_origin_v2() { #[test] fn test_call_trace() { - deploy_hex!("../tests/contracts/calls_trace.hex", exe, address); + setup(); + deploy_hex!("../tests/contracts/calls_trace.hex", vm, address); let tests = vec![ ("always_fail()", vec![(BugType::RevertOrInvalid, 167)], true), @@ -366,9 +369,9 @@ fn test_call_trace() { let fn_hex = fn_sig_to_prefix(fn_sig); let data = hex::decode(fn_hex).unwrap(); let resp = - exe.contract_call_helper(Address::new(address.0), *OWNER, data.clone(), UZERO, None); + vm.contract_call_helper(Address::new(address.0), *OWNER, data.clone(), UZERO, None); assert_eq!(expect_revert, !resp.success); - let bugs = &exe.bug_data(); + let bugs = &vm.bug_data(); let bugs: Vec<_> = bugs.iter().cloned().collect(); check_expected_bugs_are_found(expected_bugs, bugs.to_vec()); } From 02f140f3eb6cf047a95aeb459645d2a09b8ecf1f Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 10:48:02 +0800 Subject: [PATCH 18/40] pass test_mod_zero --- src/instrument/bug_inspector.rs | 120 +++++++++++++++++--------------- tests/revm_test.rs | 1 + 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 991930a..b2d013c 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -1,7 +1,7 @@ use hashbrown::{HashMap, HashSet}; use primitive_types::H160; use revm::{ - interpreter::{opcode, CreateInputs, CreateOutcome, Interpreter, OpCode}, + interpreter::{CreateInputs, CreateOutcome, Interpreter, OpCode}, primitives::{Address, U256}, Database, EvmContext, Inspector, }; @@ -28,7 +28,7 @@ pub struct BugInspector { /// Stack inputs of the current opcodes. Only updated when the opcode is interesting inputs: Vec, /// Current opcode - opcode: u8, + opcode: Option, // Current program counter pc: usize, /// Current index in the execution. For tracking peephole optimized if-statement @@ -127,8 +127,9 @@ where fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; - self.opcode = interp.current_opcode(); - let opcode = OpCode::new(self.opcode); + let opcode = interp.current_opcode(); + let opcode = OpCode::new(opcode); + self.opcode = opcode; self.pc = interp.program_counter(); if let Some(OpCode::EQ) = opcode { @@ -139,7 +140,6 @@ where self.last_index_sub = self.step_index; } - // it's also possible to handle REVERT, INVALID here self.inputs.clear(); match opcode { Some( @@ -155,6 +155,7 @@ where | OpCode::MUL | OpCode::DIV | OpCode::SDIV + | OpCode::MOD | OpCode::SMOD | OpCode::EXP | OpCode::LT @@ -192,54 +193,60 @@ where self.record_pc(address, pc); } - let result = &interp.instruction_result; - let success = result.is_ok(); - - // Check for overflow and underflow - match (opcode, success) { - (opcode::ADD, true) => { + match opcode { + Some(op @ OpCode::ADD) => { if let Ok(r) = interp.stack().peek(0) { if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { if r < *a || r < *b { - let bug = Bug::new(BugType::IntegerOverflow, opcode, pc, address_index); + let bug = + Bug::new(BugType::IntegerOverflow, op.get(), pc, address_index); self.add_bug(bug); } } } } - (opcode::MUL, true) => { + Some(op @ OpCode::MUL) => { if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { if mul_overflow(*a, *b) { - let bug = Bug::new(BugType::IntegerOverflow, opcode, pc, address_index); + let bug = Bug::new(BugType::IntegerOverflow, op.get(), pc, address_index); self.add_bug(bug); } } } - (opcode::SUB, true) => { + Some(op @ OpCode::SUB) => { if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { if a < b { - let bug = Bug::new(BugType::IntegerSubUnderflow, opcode, pc, address_index); + let bug = + Bug::new(BugType::IntegerSubUnderflow, op.get(), pc, address_index); + self.add_bug(bug); + } + } + } + Some(op @ (OpCode::SMOD | OpCode::MOD)) => { + if let Some(b) = self.inputs.get(1) { + if *b == U256::ZERO { + let bug = Bug::new(BugType::IntegerModByZero, op.get(), pc, address_index); self.add_bug(bug); } } } - (opcode::DIV | opcode::SDIV | opcode::SMOD, true) => { + Some(op @ (OpCode::DIV | OpCode::SDIV)) => { if let Some(b) = self.inputs.get(1) { - if b == &U256::ZERO { - let bug = Bug::new(BugType::IntegerDivByZero, opcode, pc, address_index); + if *b == U256::ZERO { + let bug = Bug::new(BugType::IntegerDivByZero, op.get(), pc, address_index); self.add_bug(bug); } } } - (opcode::ADDMOD | opcode::MULMOD, true) => { + Some(op @ (OpCode::ADDMOD | OpCode::MULMOD)) => { if let Some(n) = self.inputs.get(2) { if n == &U256::ZERO { - let bug = Bug::new(BugType::IntegerDivByZero, opcode, pc, address_index); + let bug = Bug::new(BugType::IntegerModByZero, op.get(), pc, address_index); self.add_bug(bug); } } } - (opcode::EXP, _) => { + Some(op @ OpCode::EXP) => { println!("checking-exp-overflow"); // todo_cl check for overflow if let (Some(a), Some(b), Ok(r)) = ( @@ -248,12 +255,12 @@ where interp.stack().peek(0), ) { if exp_overflow(*a, *b, r) { - let bug = Bug::new(BugType::IntegerOverflow, opcode, pc, address_index); + let bug = Bug::new(BugType::IntegerOverflow, op.get(), pc, address_index); self.add_bug(bug); } } } - (opcode::LT, true) => { + Some(OpCode::LT) => { if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { let distance = if a >= b { a.overflowing_sub(*b).0.saturating_add(U256::from(1)) @@ -263,7 +270,7 @@ where self.heuristics.distance = distance; } } - (opcode::GT, true) => { + Some(OpCode::GT) => { if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { let distance = if a >= b { a.overflowing_sub(*b).0 @@ -273,7 +280,7 @@ where self.heuristics.distance = distance; } } - (opcode::SLT, true) => { + Some(OpCode::SLT) => { if let (Some(a), Some(b), Ok(r)) = ( self.inputs.first(), self.inputs.get(1), @@ -290,7 +297,7 @@ where self.heuristics.distance = distance; } } - (opcode::SGT, true) => { + Some(OpCode::SGT) => { if let (Some(a), Some(b), Ok(r)) = ( self.inputs.first(), self.inputs.get(1), @@ -303,7 +310,7 @@ where self.heuristics.distance = distance; } } - (opcode::EQ, true) => { + Some(OpCode::EQ) => { if let (Some(a), Some(b), Ok(r)) = ( self.inputs.first(), self.inputs.get(1), @@ -320,7 +327,7 @@ where self.heuristics.distance = distance; } } - (opcode::AND, true) => { + Some(op @ OpCode::AND) => { if let (Some(a), Some(b)) = (self.inputs.first(), self.inputs.get(1)) { // check if there is an possible truncation @@ -347,7 +354,7 @@ where if possible_overflow { let bug = Bug::new( BugType::PossibleIntegerTruncation, - opcode, + op.get(), pc, address_index, ); @@ -355,39 +362,38 @@ where } } } - (opcode::SSTORE, true) => { + Some(op @ OpCode::SSTORE) => { if let (Some(key), Some(value)) = (self.inputs.first(), self.inputs.get(1)) { let bug = Bug::new( BugType::Sstore(*key, *value), - self.opcode, + op.get(), self.pc, address_index, ); self.add_bug(bug); } } - (opcode::SLOAD, true) => { + Some(op @ OpCode::SLOAD) => { if let Some(key) = self.inputs.first() { - let bug = Bug::new(BugType::Sload(*key), self.opcode, self.pc, address_index); + let bug = Bug::new(BugType::Sload(*key), op.get(), self.pc, address_index); self.add_bug(bug); } } - (opcode::ORIGIN, true) => { + Some(op @ OpCode::ORIGIN) => { let bug = Bug::new( BugType::TxOriginDependency, - self.opcode, + op.get(), self.pc, address_index, ); self.add_bug(bug); } - (opcode::CALL, _) - | (opcode::CALLCODE, _) - | (opcode::DELEGATECALL, _) - | (opcode::STATICCALL, _) => { + Some( + op @ (OpCode::CALL | OpCode::CALLCODE | OpCode::DELEGATECALL | OpCode::STATICCALL), + ) => { let in_len = { - if self.opcode == opcode::CALL || self.opcode == opcode::CALLCODE { + if matches!(op, OpCode::CALL | OpCode::CALLCODE) { self.inputs.get(4) } else { self.inputs.get(3) @@ -401,14 +407,14 @@ where let in_len = usize::try_from(in_len).unwrap(); let bug = Bug::new( BugType::Call(in_len, callee), - self.opcode, + op.get(), self.pc, address_index, ); self.add_bug(bug); } } - (opcode::JUMPI, true) => { + Some(op @ OpCode::JUMPI) => { // Check for missed branches let target_address = self.instrument_config.target_address; macro_rules! update_heuritics { @@ -426,7 +432,7 @@ where ); let target = if $cond { $dest_pc } else { $prev_pc + 1 }; let bug = - Bug::new(BugType::Jumpi(target), opcode, $prev_pc, address_index); + Bug::new(BugType::Jumpi(target), op.get(), $prev_pc, address_index); self.add_bug(bug); } }; @@ -455,32 +461,30 @@ where update_heuritics!(pc, dest, cond); } } - (opcode::BLOBHASH, _) => { - let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); + Some(op @ OpCode::BLOBHASH) => { + let bug = Bug::new(BugType::BlockValueDependency, op.get(), pc, address_index); self.add_bug(bug); } - (opcode::COINBASE, _) => { - let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); + Some(op @ OpCode::COINBASE) => { + let bug = Bug::new(BugType::BlockValueDependency, op.get(), pc, address_index); self.add_bug(bug); } - (opcode::TIMESTAMP, _) => { - let bug = Bug::new(BugType::TimestampDependency, opcode, pc, address_index); + Some(op @ OpCode::TIMESTAMP) => { + let bug = Bug::new(BugType::TimestampDependency, op.get(), pc, address_index); self.add_bug(bug); } - (opcode::NUMBER, _) => { - let bug = Bug::new(BugType::BlockNumberDependency, opcode, pc, address_index); + Some(op @ OpCode::NUMBER) => { + let bug = Bug::new(BugType::BlockNumberDependency, op.get(), pc, address_index); self.add_bug(bug); } - (opcode::DIFFICULTY, _) => { - let bug = Bug::new(BugType::BlockValueDependency, opcode, pc, address_index); + Some(op @ OpCode::DIFFICULTY) => { + let bug = Bug::new(BugType::BlockValueDependency, op.get(), pc, address_index); self.add_bug(bug); } - - (opcode::REVERT | opcode::INVALID, _) => { - let bug = Bug::new(BugType::RevertOrInvalid, opcode, pc, address_index); + Some(op @ (OpCode::REVERT | OpCode::INVALID)) => { + let bug = Bug::new(BugType::RevertOrInvalid, op.get(), pc, address_index); self.add_bug(bug); } - _ => (), } } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index ee2499f..597846c 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -713,6 +713,7 @@ fn test_div_zero() { #[test] fn test_mod_zero() { + setup(); let tests = vec![ (U256::from(1), Some((BugType::IntegerModByZero, 149)), false), (U256::from(2), Some((BugType::IntegerModByZero, 178)), false), From c4792444ca176c81cd77b445522735f3ca7d88b1 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 10:51:49 +0800 Subject: [PATCH 19/40] fix test_selfdestruct_and_create --- src/instrument/bug_inspector.rs | 4 ++++ tests/revm_test.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index b2d013c..5948ad1 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -485,6 +485,10 @@ where let bug = Bug::new(BugType::RevertOrInvalid, op.get(), pc, address_index); self.add_bug(bug); } + Some(op @ (OpCode::SELFDESTRUCT | OpCode::CREATE | OpCode::CREATE2)) => { + let bug = Bug::new(BugType::Unclassified, op.get(), pc, address_index); + self.add_bug(bug); + } _ => (), } } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 597846c..135ca94 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1074,6 +1074,7 @@ fn test_get_set_balance() { #[test] fn test_selfdestruct_and_create() { + setup(); deploy_hex!("../tests/contracts/self_destruct.hex", vm, addr); let bin = hex::decode(fn_sig_to_prefix("kill()")).unwrap(); From 927cbb7ef5d75fda7d5c2604fd95b11a7158879d Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 11:19:58 +0800 Subject: [PATCH 20/40] pass test_sha3_mapping --- src/instrument/bug_inspector.rs | 28 ++++++++++++++++++++++++++-- tests/revm_test.rs | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 5948ad1..84d64fd 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -1,5 +1,5 @@ use hashbrown::{HashMap, HashSet}; -use primitive_types::H160; +use primitive_types::{H160, H256}; use revm::{ interpreter::{CreateInputs, CreateOutcome, Interpreter, OpCode}, primitives::{Address, U256}, @@ -165,7 +165,8 @@ where | OpCode::EQ | OpCode::AND | OpCode::ADDMOD - | OpCode::MULMOD), + | OpCode::MULMOD + | OpCode::KECCAK256), ) => { let num_inputs = op.inputs(); for i in 0..num_inputs { @@ -489,6 +490,29 @@ where let bug = Bug::new(BugType::Unclassified, op.get(), pc, address_index); self.add_bug(bug); } + Some(OpCode::KECCAK256) => { + if self.instrument_config.record_sha3_mapping { + if let (Some(offset), Some(size), Ok(output)) = ( + self.inputs.first(), + self.inputs.get(1), + interp.stack().peek(0), + ) { + let offset = offset.as_limbs()[0] as usize; + let size = size.as_limbs()[0] as usize; + let input = &interp.shared_memory.context_memory()[offset..offset + size]; + // get only last 32 bytes + let last_32 = { + if input.len() > 32 { + &input[input.len() - 32..] + } else { + input + } + }; + let output = H256::from_slice(&output.to_be_bytes::<32>()); + self.heuristics.record_sha3_mapping(last_32, output); + } + } + } _ => (), } } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 135ca94..69b22a0 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1215,6 +1215,7 @@ fn test_reset_storage() { #[test] fn test_sha3_mapping() { + setup(); deploy_hex!("../tests/contracts/sha3_mapping.hex", vm, addr); let addr = Address::new(addr.0); From a1358d7d5bbf6d6452e7c90f905573b051f98de0 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 13:57:43 +0800 Subject: [PATCH 21/40] pass test_seen_addresses --- Cargo.toml | 2 +- src/fork_db.rs | 22 ----------- src/instrument/bug_inspector.rs | 8 ++++ src/lib.rs | 15 +++----- tests/revm_test.rs | 66 ++++++++++++--------------------- 5 files changed, 38 insertions(+), 75 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c16274..2d4c2ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [lib] name="tinyevm" -crate_type = ["cdylib", "rlib"] +crate-type = ["cdylib", "rlib"] path="src/lib.rs" [dependencies] diff --git a/src/fork_db.rs b/src/fork_db.rs index 2cd6ac4..2a332b5 100644 --- a/src/fork_db.rs +++ b/src/fork_db.rs @@ -5,7 +5,6 @@ use std::env; use crate::cache::filesystem_cache::FileSystemProviderCache; use crate::cache::ProviderCache; use crate::fork_provider::ForkProvider; -use crate::instrument::bug::{BugData, Heuristics, InstrumentConfig}; use crate::CALL_DEPTH; use ethers::types::{Block, TxHash}; use eyre::{ContextCompat, Result}; @@ -18,22 +17,6 @@ use revm::primitives::{ use revm::{Database, DatabaseCommit}; use tracing::{debug, info, trace}; -#[derive(Debug, Clone, Default)] -pub struct InstrumentData { - pub bug_data: BugData, - pub heuristics: Heuristics, - // Mapping from contract address to a set of PCs seen in the execution - pub pcs_by_address: HashMap>, - // Holding the addresses created in the current transaction, - // must be cleared by transaction caller before or after each transaction - pub created_addresses: Vec
, - // Managed addresses: contract -> addresses created by any transaction from the contract - pub managed_addresses: HashMap>, - pub opcode_index: usize, - pub last_index_sub: usize, - pub last_index_eq: usize, -} - #[derive(Debug, Default)] pub struct ForkDB { /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks. @@ -57,9 +40,6 @@ pub struct ForkDB { block_cache: HashMap>, /// Max depth to consider when forking address max_fork_depth: usize, - - /// Optional instrument config - pub instrument_config: Option, } impl Clone for ForkDB { @@ -75,7 +55,6 @@ impl Clone for ForkDB { block_cache: self.block_cache.clone(), ignored_addresses: self.ignored_addresses.clone(), max_fork_depth: self.max_fork_depth, - instrument_config: self.instrument_config.clone(), } } } @@ -153,7 +132,6 @@ impl ForkDB { block_cache: HashMap::new(), ignored_addresses: Default::default(), max_fork_depth, - instrument_config: Some(InstrumentConfig::default()), } } diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 84d64fd..19bc00b 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -489,6 +489,13 @@ where Some(op @ (OpCode::SELFDESTRUCT | OpCode::CREATE | OpCode::CREATE2)) => { let bug = Bug::new(BugType::Unclassified, op.get(), pc, address_index); self.add_bug(bug); + if matches!(op, OpCode::CREATE | OpCode::CREATE2) { + if let Ok(created_address) = interp.stack.peek(0) { + let bytes: [u8; 32] = created_address.to_be_bytes(); + let created_address = Address::from_slice(&bytes[12..]); + self.record_seen_address(created_address); + } + } } Some(OpCode::KECCAK256) => { if self.instrument_config.record_sha3_mapping { @@ -540,6 +547,7 @@ where address ); } + return CreateOutcome::new(result.to_owned(), Some(*override_address)); } } diff --git a/src/lib.rs b/src/lib.rs index 67d8079..15bb05f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,6 +121,9 @@ pub fn enable_tracing() -> Result<()> { // Implementations for use in Rust impl TinyEVM { + pub fn instrument_config_mut(&mut self) -> &mut InstrumentConfig { + &mut self.bug_inspector_mut().instrument_config + } fn log_inspector(&self) -> &LogInspector { self.exe .as_ref() @@ -238,7 +241,6 @@ impl TinyEVM { owner: Address, contract_bytecode: Vec, value: U256, - _overwrite: bool, // not supported yet tx_gas_limit: Option, force_address: Option
, // not supported yet ) -> Result { @@ -679,7 +681,6 @@ impl TinyEVM { owner, hex::decode(contract_deploy_code)?, U256::default(), - true, None, None, ) @@ -739,7 +740,6 @@ impl TinyEVM { owner, contract_bytecode, bigint_to_ruint_u256(&value)?, - true, None, Some(Address::from_str(&deploy_to_address)?), )?; @@ -924,18 +924,13 @@ impl TinyEVM { /// - `config`: A json string serialized for [`InstrumentConfig`](https://github.com/sbip-sg/revm/blob/6f7ac687a22f67462999ca132ede8d116bd7feb9/crates/revm/src/bug.rs#L153) pub fn configure(&mut self, config: &REVMConfig) -> Result<()> { let config = config.to_iconfig()?; - let db = &mut self.exe.as_mut().unwrap().context.evm.db; - db.instrument_config = Some(config); + self.bug_inspector_mut().instrument_config = config; Ok(()) } /// Get current runtime instrumentation configuration pub fn get_instrument_config(&self) -> Result { - let db = &self.exe.as_ref().unwrap().context.evm.db; - let r = &db - .instrument_config - .as_ref() - .ok_or_else(|| eyre!("Instrumentation config not set"))?; + let r = &self.bug_inspector().instrument_config; Ok(REVMConfig::from(r)) } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 69b22a0..4ef8424 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -48,14 +48,7 @@ macro_rules! deploy_hex { let bytecode_hex = include_str!($hex_path); let bytecode = hex::decode(bytecode_hex).unwrap(); - let resp = $vm.deploy_helper( - *OWNER, - bytecode, - UZERO, - false, - None, - Some(*CONTRACT_ADDRESS), - ); + let resp = $vm.deploy_helper(*OWNER, bytecode, UZERO, None, Some(*CONTRACT_ADDRESS)); assert!( resp.is_ok(), @@ -189,7 +182,7 @@ fn single_bugtype_test_helper( let bytecode = hex::decode(contract_deploy_hex).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, UZERO, false, None, None) + .deploy_helper(owner, bytecode, UZERO, None, None) .unwrap(); let address = Address::from_slice(&resp.data); @@ -383,20 +376,13 @@ fn test_deterministic_deploy() { let contract_deploy_bin = hex::decode(contract_deploy_hex).unwrap(); let mut vm = TinyEVM::default(); let c1 = vm - .deploy_helper( - *OWNER, - contract_deploy_bin.clone(), - UZERO, - false, - None, - None, - ) + .deploy_helper(*OWNER, contract_deploy_bin.clone(), UZERO, None, None) .unwrap(); assert!(c1.success, "Deploy use salt 1 should succeed: {:?}", &c1); let c2 = vm - .deploy_helper(*OWNER, contract_deploy_bin, UZERO, false, None, None) + .deploy_helper(*OWNER, contract_deploy_bin, UZERO, None, None) .unwrap(); assert!(c2.success, "Deploy use salt 2 should succeed: {:?}", &c2); @@ -417,7 +403,6 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { *OWNER, contract_deploy_bin.clone(), UZERO, - false, None, force_address, ) @@ -445,14 +430,7 @@ fn test_deterministic_deploy_overwrite() -> Result<()> { }; let c2 = vm - .deploy_helper( - *OWNER, - contract_deploy_bin, - UZERO, - true, - None, - force_address, - ) + .deploy_helper(*OWNER, contract_deploy_bin, UZERO, None, force_address) .unwrap(); assert!( @@ -627,7 +605,7 @@ fn test_bug_data_in_deploy() { let bytecode = hex::decode(format!("{}{}", contract_hex, constructor_args_hex)).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, UZERO, false, None, None) + .deploy_helper(owner, bytecode, UZERO, None, None) .unwrap(); assert!(resp.success, "Contract deploy should succeed."); @@ -666,7 +644,7 @@ fn test_deploy_with_args_and_value() { let bytecode = hex::decode(format!("{}{}", contract_hex, constructor_args_hex)).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, value, false, None, None) + .deploy_helper(owner, bytecode, value, None, None) .unwrap(); println!("resp: {resp:?}"); @@ -736,7 +714,7 @@ fn test_gas_usage() { let bytecode_hex = include_str!("../tests/contracts/gasusage.hex"); let bytecode = hex::decode(bytecode_hex).unwrap(); - let resp = vm.deploy_helper(*OWNER, bytecode, UZERO, false, None, None); + let resp = vm.deploy_helper(*OWNER, bytecode, UZERO, None, None); assert!(resp.is_ok(), "Contract deploy should succeed."); @@ -930,7 +908,7 @@ fn test_blockhash() { block_env.number = block; let resp = vm - .deploy_helper(owner, bytecode.clone(), UZERO, true, None, None) + .deploy_helper(owner, bytecode.clone(), UZERO, None, None) .unwrap(); let addr = Address::from_slice(&resp.data); @@ -1040,7 +1018,7 @@ fn test_get_set_balance() { let bytecode = include_str!("../tests/contracts/balance.hex"); let bytecode = hex::decode(bytecode).unwrap(); let resp = vm - .deploy_helper(owner, bytecode, UZERO, false, None, None) + .deploy_helper(owner, bytecode, UZERO, None, None) .unwrap(); assert!(resp.success, "Deployment should succeed"); let addr = Address::from_slice(&resp.data); @@ -1252,31 +1230,33 @@ fn test_sha3_mapping() { #[test] fn test_seen_addresses() { + setup(); let mut vm = TinyEVM::default(); - - { - // vm.env.block.number = U256::from(100u64); - } + let r = vm.set_env_field_value("block_number".into(), format!("{:0>64x}", U256::from(1u64))); + assert!(r.is_ok(), "Set block number should succeed"); let bytecode = include_str!("./contracts/contract_addresses_A.hex"); let bytecode = hex::decode(bytecode).unwrap(); let resp = vm - .deploy_helper(*OWNER, bytecode, UZERO, false, None, None) + .deploy_helper(*OWNER, bytecode, UZERO, None, None) .unwrap(); assert!(resp.success, "Deploy contract A should succeed"); let addr_a = Address::from_slice(&resp.data); - // if let Some(ref mut config) = vm.db.instrument_config { - // config.record_branch_for_target_only = true; - // config.target_address = addr_a; - // } + println!("address A: {:?}", addr_a); + + { + let config = vm.instrument_config_mut(); + config.record_branch_for_target_only = true; + config.target_address = addr_a; + } let bytecode = include_str!("./contracts/contract_addresses_B.hex"); let bytecode = format!("{}{:0>64}", bytecode, addr_a.encode_hex::()); println!("Deploying contract B with bytecode: {}", bytecode); let bytecode = hex::decode(bytecode).unwrap(); let resp = vm - .deploy_helper(*OWNER, bytecode, UZERO, false, None, None) + .deploy_helper(*OWNER, bytecode, UZERO, None, None) .unwrap(); assert!( resp.success, @@ -1286,6 +1266,8 @@ fn test_seen_addresses() { let addr = Address::from_slice(&resp.data); + println!("address B: {:?}", addr); + let prefix = fn_sig_to_prefix("getBlockNumber()"); let args = ""; From 2094151ce53dfed66913e3a23e573c549e9ac2ef Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 14:01:59 +0800 Subject: [PATCH 22/40] pass test_runtime_configuration --- tests/revm_test.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 4ef8424..748ecb9 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1115,9 +1115,12 @@ fn test_seen_pcs() { #[test] fn test_runtime_configuration() { + setup(); deploy_hex!("../tests/contracts/contract_creation_B.hex", vm, address); let address = Address::new(address.0); + vm.instrument_config_mut().pcs_by_address = false; + vm.set_account_balance( *OWNER, U256::from_str_radix("9999999999999999999999", 16).unwrap(), From e9cb1aff40250993983ee60296b8deb60ef09be8 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 15:07:01 +0800 Subject: [PATCH 23/40] pass test_peephole_optimized_if_equal --- src/instrument/bug_inspector.rs | 16 ++++++++++------ tests/revm_test.rs | 4 +++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 19bc00b..f95630e 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -440,25 +440,29 @@ where } // NOTE: invalid jumps are ignored - if let (Some(dest), Some(value)) = (self.inputs.first(), self.inputs.get(1)) { + if let (Some(counter), Some(cond)) = (self.inputs.first(), self.inputs.get(1)) { // Check for distance in peephole optimized if-statement if self.possibly_if_equal() { + debug!( + "Possible peephole optimized if-statement found, inputs: {:?} pc {}", + self.inputs, self.pc + ); let max = U256::MAX; let mut half = U256::MAX; half.set_bit(31, false); let h = &mut self.heuristics; h.distance = { // smallest distance from the `value` to U256::MAX and 0 - if *value > half { - max - value + U256::from(1) + if *cond > half { + max - cond + U256::from(1) } else { - *value + *cond } }; } - let dest = usize::try_from(dest).unwrap(); - let cond = *value != U256::ZERO; + let dest = usize::try_from(counter).unwrap(); + let cond = *cond != U256::ZERO; update_heuritics!(pc, dest, cond); } } diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 748ecb9..a693706 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1350,6 +1350,7 @@ fn test_distance_signed() { #[test] fn test_peephole_optimized_if_equal() { + setup(); deploy_hex!( "../tests/contracts/test_peephole_optimized.hex", vm, @@ -1362,7 +1363,8 @@ fn test_peephole_optimized_if_equal() { let input = U256::from(1); let fn_args_hex = format!("{:0>64x}", input); - let expected_missed_branches: (usize, usize, U256) = (317, 167, U256::from(0x2007)); + let expected_missed_branches: (usize, usize, U256) = (166, 181, U256::from(0x2007)); + // [MissedBranch { prev_pc: 11, cond: true, dest_pc: 16, distance: 115792089237316195423570985008687907853269984665640564039457584007913129639935, address_index: 0 }, MissedBranch { prev_pc: 25, cond: false, dest_pc: 65, distance: 33, address_index: 0 }, MissedBranch { prev_pc: 42, cond: true, dest_pc: 70, distance: 1, address_index: 0 }, MissedBranch { prev_pc: 354, cond: true, dest_pc: 363, distance: 1, address_index: 0 }, MissedBranch { prev_pc: 312, cond: true, dest_pc: 317, distance: 1, address_index: 0 }, MissedBranch { prev_pc: 166, cond: true, dest_pc: 181, distance: 8199, address_index: 0 }] let fn_hex = format!("{}{}", fn_sig_hex, fn_args_hex); From 1ce7de08873408bd8d1138b3a22852406138c37b Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 15:31:55 +0800 Subject: [PATCH 24/40] fix redis version --- README.md | 219 ++++++++++++++++++++++++++++++++++++++++++++- src/cache/mod.rs | 5 ++ src/fork_db.rs | 12 ++- src/lib.rs | 7 +- tests/revm_test.rs | 2 +- 5 files changed, 230 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 89b53e5..2cf39f1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,218 @@ -# tevm +# tinyevm -An EVM with instrumentation for debugging and analysis using REVM. +Dynamic library providing API to EVM executor + + +## Start development + +* Clone this repository + +``` bash +git clone git@github.com:sbip-sg/tevm.git +``` + + +* Run test in tinyevm + +- For unit tests, run + +``` bash +make test +``` + +- For Rust APIs benchmark test, run + +``` bash +make bench +``` + + +## How to test the underline REVM + + +If you wish to verify that your EVM implementation adheres to the EVM specification, you'll need to conduct this test. + +To do so, you should first clone the sbip/revm repository. After cloning, switch to the instrument branch. Once you're on the correct branch, follow the instructions provided in the [documentaion](https://github.com/sbip-sg/revm#running-eth-tests) to execute the test. + + +## How to contribute + +* Clone this repository + +``` bash +git clone git@github.com:sbip-sg/tinyevm.git +``` + +* Create a fork for the task you are working on + + +``` bash +cd tinyevm +git checkout -b [name-of-your-fork] +``` + +Continue working on this fork, commit as often as you like. When you +feel like it's ready to merge, create a PR on github to request +merging your fork to `develop`. + +After PR is appproved, please merge your changes to `develop`: + * If there is no conflict, you can merge the changes directly on github + * In case there are any conflicts preventing the merge, follow these steps + * `git fetch --all` + * `git merge origin/develop` + * Resolve all the conflicts, after testing, make another commit and you can continue merging the changes. + * After squashing and mering the PR, delete your branch. + + +## Sample usage + +You can find example usage in Python at `example/example.py` and `example/example-bug-detected.py` + +Example files: + +``` text +example/ +├── C.hex # Bytecode for contract C as hex +├── example-bug-detected.py # Sample bug-detection in Python +├── example.py # Sample code in Python +├── int_cast_0.5.0.hex # Bytecode for contract IntCast as hex compiled with solc version 0.5.0 +├── int_cast_0.8.0.hex # Bytecode for contract IntCast as hex compiled with solc version 0.8.0 +├── int_cast.sol # Source code for the IntCast contract +├── tx_origin.hex # Bytecode for contract TxOrigin as hex compiled with solc version 0.8.12 +├── tx_origin.sol # Source code for the TxOrigin contract +├── calls_trace.hex # Bytecode for contract `sample` as hex compiled with solc version 0.7.0 +├── call_trace.sol # Source code for the `sample` contract for call tracing test +├── block_hash.sol # Source code for the `BHash` contract for blockhash test +├── block_hash.hex # Bytecode for contract `BHash` as hex compiled with solc version 0.7.0 +├── test_tod.sol # Source code for testing SSLOAD/SSTORE instrumentation +├── test_tod.hex # Bytecode for the tet contract in test_tod.sol +├── balance.sol # Source code for the `Balance` contract for balance get/set test +├── balance.hex # Bytecode for contract `Balance` as hex compiled with solc version 0.7.0 +├── self_destruct.sol # Source code for the `Des` contract for SELFDESTRUCT detection +├── self_destruct.hex # Bytecode for contract `Des` as hex compiled with solc 0.8.10 +├── contract_creation.sol # Source code for testing code coverage calculated with program counter +├── contract_creation_B.hex # Bytecode for contract `B` in contract_creation.sol +├── deploy_with_args_and_value.hex # Bytecode for contract DeployWithArgsAndValue as hex compiled with solc version 0.7.0 +├── deploy_with_args_and_value.sol # Source code for the `DeployWithArgsAndValue` contract for testing contract with constructor arguments +├── ... +``` + +The contract `C` used in this example is compiled from [data_structures.sol](https://github.com/cassc/evm-play/tree/main/contracts). + + +## Python Module + +### Local development + +* Clean up the previous build + + ``` bash + make clean + ``` + +* Install development dependencies + + ``` bash + pip install -r requirements-dev.txt + ``` + +* Build and install the Python library + + ``` bash + maturing develop + ``` +* Run test, this will run the Rust test first, install the development version of TinyEVM and run the Python tests + + ``` bash + make test + ``` + +### Build and release Python library + +* The following command will build a `whl` file inside `target/wheels` folder + ``` bash + maturin build --release + # or to compile for python 3.9 + maturin build --release -i 3.9 + ``` +* You can install this file with `pip` command + ``` bash + pip install target/wheels/*.whl --force-reinstall + ``` + +Here's the corrected version: + +### Cache Web3 Requests to Redis + +By default, requests to the Web3 endpoints are cached in the file system. If you wish to use Redis as the cache, compile with the `provider_cache_redis` flag: + +```bash +maturin build --release -i 3.9 --cargo-extra-args="--features provider_cache_redis" +``` + +Additionally, you must set the environment variable `TINYEVM_REDIS_NODE` to a valid Redis endpoint. + +# Benchmarks + +## Global snapshot benchmarks + +* Using the naive implementation which clones the whole data structure + +``` bash +============================================================================================================= test session starts ============================================================================================================== +platform linux -- Python 3.9.7, pytest-7.2.2, pluggy-1.0.0 +benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) +rootdir: /home/garfield/projects/sbip-sg/tinyevm +plugins: cov-4.1.0, web3-5.31.2, hypothesis-6.82.0, anyio-3.6.2, benchmark-4.0.0, xdist-3.3.1 +collected 7 items + +tests/test_global_snapshot.py ......Filename: /home/garfield/projects/sbip-sg/tinyevm/tests/test_global_snapshot.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 153 97.3 MiB 97.3 MiB 1 @profile + 154 def test_compare_memory(): + 155 97.3 MiB 0.0 MiB 1002 x_fns_fastseq = [('fast_seq()', 1 + 5 * i) for i in range(1, 1000)] + 156 97.3 MiB 0.0 MiB 102 x_fns_slowseq = [('slow_seq()', 1 + 5 * 50 * i) for i in range(1, 100)] + 157 global tevm + 158 + 159 # Running fastseq taking 100 snapshots + 160 97.3 MiB 0.0 MiB 1 tevm = tinyevm.TinyEVM() + 161 97.3 MiB 0.0 MiB 1 gc.collect() + 162 363.6 MiB 266.2 MiB 1 run_global_snapshot(True, x_fns_fastseq, take_snapshot_after_each_tx=True) + 163 + 164 # Running slowseq taking 100 snapshots + 165 217.3 MiB -146.3 MiB 1 tevm = tinyevm.TinyEVM() + 166 217.3 MiB 0.0 MiB 1 gc.collect() + 167 229.4 MiB 12.1 MiB 1 run_global_snapshot(True, x_fns_slowseq, take_snapshot_after_each_tx=True) + 168 + 169 # Running 1000 fastseq without taking snapshots + 170 150.0 MiB -79.4 MiB 1 tevm = tinyevm.TinyEVM() + 171 150.0 MiB 0.0 MiB 1 gc.collect() + 172 150.0 MiB 0.0 MiB 1 run_global_snapshot(True, x_fns_fastseq, disable_snapshot=True) + 173 + 174 # Running 100 slowseq without taking snapshots + 175 150.0 MiB 0.0 MiB 1 tevm = tinyevm.TinyEVM() + 176 150.0 MiB 0.0 MiB 1 gc.collect() + 177 150.0 MiB 0.0 MiB 1 run_global_snapshot(True, x_fns_slowseq, disable_snapshot=True) + + +. + + +------------------------------------------------------------------------------------------------------------------------------------ benchmark: 6 tests ----------------------------------------------------------------------------------------------------------------------------------- +Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +test_global_snapshot[False-x_fns4-True-fastseq don't take snapshot] 380.7210 (1.0) 864.9290 (1.0) 414.0279 (1.0) 47.1819 (1.0) 399.9720 (1.0) 14.6963 (1.0) 126;194 2,415.2963 (1.0) 1917 1 +test_global_snapshot[False-x_fns1-False-fastseq take and restore snapshot, don't keep after restore] 395.2560 (1.04) 1,188.2490 (1.37) 432.9322 (1.05) 71.7241 (1.52) 408.4960 (1.02) 25.4053 (1.73) 165;235 2,309.8306 (0.96) 1899 1 +test_global_snapshot[True-x_fns0-False-fastseq take and restore snapshot, keep after restore] 405.6830 (1.07) 1,157.2750 (1.34) 491.9984 (1.19) 129.4663 (2.74) 421.2585 (1.05) 100.5715 (6.84) 99;86 2,032.5268 (0.84) 448 1 +test_global_snapshot[False-x_fns5-True-slowseq don't take snapshot] 4,965.2520 (13.04) 7,654.7660 (8.85) 5,348.2192 (12.92) 419.9397 (8.90) 5,206.4350 (13.02) 247.7357 (16.86) 12;12 186.9781 (0.08) 173 1 +test_global_snapshot[False-x_fns3-False-slowseq take and restore snapshot, don't keep after restore] 4,989.4110 (13.11) 9,267.5720 (10.71) 5,320.8998 (12.85) 383.5033 (8.13) 5,216.9890 (13.04) 197.7330 (13.45) 7;11 187.9381 (0.08) 171 1 +test_global_snapshot[True-x_fns2-False-slowseq take and restore snapshot, keep after restore] 5,056.2300 (13.28) 7,849.9280 (9.08) 5,392.2435 (13.02) 327.4688 (6.94) 5,311.0770 (13.28) 268.5015 (18.27) 15;6 185.4516 (0.08) 176 1 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Legend: + Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile. + OPS: Operations Per Second, computed as 1 / Mean +============================================================================================================== 7 passed in 8.21s =============================================================================================================== +``` diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 2c34de9..053d1f7 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -6,6 +6,11 @@ pub mod filesystem_cache; #[cfg(feature = "redis")] pub mod redis_cache; +#[cfg(not(feature = "redis"))] +pub use filesystem_cache::FileSystemProviderCache as DefaultProviderCache; +#[cfg(feature = "redis")] +pub use redis_cache::RedisProviderCache as DefaultProviderCache; + pub trait ProviderCache: Clone + Default { fn store( &self, diff --git a/src/fork_db.rs b/src/fork_db.rs index 2a332b5..0c7bf66 100644 --- a/src/fork_db.rs +++ b/src/fork_db.rs @@ -1,13 +1,10 @@ -use hashbrown::hash_map::Entry; -use hashbrown::{HashMap, HashSet}; -use std::env; - -use crate::cache::filesystem_cache::FileSystemProviderCache; -use crate::cache::ProviderCache; +use crate::cache::{DefaultProviderCache, ProviderCache}; use crate::fork_provider::ForkProvider; use crate::CALL_DEPTH; use ethers::types::{Block, TxHash}; use eyre::{ContextCompat, Result}; +use hashbrown::hash_map::Entry; +use hashbrown::{HashMap, HashSet}; use primitive_types::H256; use revm::db::{AccountState, DbAccount}; use revm::primitives::{ @@ -15,6 +12,7 @@ use revm::primitives::{ U256, }; use revm::{Database, DatabaseCommit}; +use std::env; use tracing::{debug, info, trace}; #[derive(Debug, Default)] @@ -42,7 +40,7 @@ pub struct ForkDB { max_fork_depth: usize, } -impl Clone for ForkDB { +impl Clone for ForkDB { fn clone(&self) -> Self { Self { accounts: self.accounts.clone(), diff --git a/src/lib.rs b/src/lib.rs index 15bb05f..1093c89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,15 +7,12 @@ use ::revm::{ }, Evm, }; -#[cfg(feature = "redis")] -use cache::redis_cache::RedisProviderCache as DefaultProviderCache; +use cache::DefaultProviderCache; + use chain_inspector::ChainInspector; use hashbrown::{HashMap, HashSet}; use revm::inspector_handle_register; -#[cfg(not(feature = "redis"))] -use cache::filesystem_cache::FileSystemProviderCache as DefaultProviderCache; - use dotenv::dotenv; use ethers_providers::{Http, Provider}; use eyre::{eyre, ContextCompat, Result}; diff --git a/tests/revm_test.rs b/tests/revm_test.rs index a693706..4200695 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1419,7 +1419,7 @@ fn test_fork() -> Result<()> { #[test] fn test_call_forked_contract_from_local_contract() -> Result<()> { - let _ = enable_tracing(); + setup(); let bin = include_str!("../tests/contracts/test_fork.hex"); let fork_url = Some("https://bscrpc.com".into()); let block_id = Some(0x1e08bd6); From 07ad1099bc9d576364a4709919ed04de41daf629 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 15:39:01 +0800 Subject: [PATCH 25/40] clean ci file --- .github/workflows/develop.yml | 9 +++------ Makefile | 15 +++++++++++++++ README.md | 6 +----- requirements-dev.txt | 8 ++++++++ requirements.txt | 0 5 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 Makefile create mode 100644 requirements-dev.txt create mode 100644 requirements.txt diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index f8d94ed..0a5d54a 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -23,9 +23,10 @@ jobs: with: submodules: 'recursive' - - uses: actions-rs/toolchain@v1 # Rust toolchain + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy + components: rustfmt, clippy, llvm-tools-preview - name: Get OS infomration id: os @@ -71,10 +72,6 @@ jobs: - name: Run Rust tests run: cargo nextest run - - uses: dtolnay/rust-toolchain@stable - with: - components: llvm-tools-preview - - name: Install as local Python package uses: PyO3/maturin-action@v1 with: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..306bbc2 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +prepare: + cargo install cargo-criterion + pip install -r requirements-dev.txt +linting: + cargo clippy --tests --benches --features linting +build: + maturin build --release -i 3.9 +test: + cargo nextest run --features linting + maturin develop --release + pytest -s --show-capture all +bench: + cargo bench +clean: + cargo clean diff --git a/README.md b/README.md index 2cf39f1..820a16d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Dynamic library providing API to EVM executor git clone git@github.com:sbip-sg/tevm.git ``` - * Run test in tinyevm - For unit tests, run @@ -29,10 +28,7 @@ make bench ## How to test the underline REVM - -If you wish to verify that your EVM implementation adheres to the EVM specification, you'll need to conduct this test. - -To do so, you should first clone the sbip/revm repository. After cloning, switch to the instrument branch. Once you're on the correct branch, follow the instructions provided in the [documentaion](https://github.com/sbip-sg/revm#running-eth-tests) to execute the test. + ## How to contribute diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..77928c2 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,8 @@ +pytest>=3.5.0 +pip>=21.3 +maturin>=0.12,<0.13 +pycryptodome>=3.10.1 +pytest-benchmark +eth_abi +maturin +memory_profiler diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 From ffab4344a1a7abcca360dc770a7f85cd35231a41 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 15:40:21 +0800 Subject: [PATCH 26/40] remove revm deps --- .github/workflows/develop.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 0a5d54a..940c652 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -54,12 +54,6 @@ jobs: - name: Install Python dependencies run: pip install -r requirements-dev.txt - - uses: webfactory/ssh-agent@v0.5.4 - with: - ssh-private-key: | - ${{ secrets.SSH_PRIVATE_KEY_RUST_EVM }} - ${{ secrets.SSH_PRIVATE_KEY_REVM }} - - name: Check code formatting run: cargo +stable fmt From 4cbcf3f274b7955f2ce3f2210fd4cd3e621ec8ee Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 15:43:03 +0800 Subject: [PATCH 27/40] fix lingting and ci --- .github/workflows/develop.yml | 2 +- src/instrument/bug_inspector.rs | 68 ++++++++++++++++----------------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 940c652..4e9d5f4 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -58,7 +58,7 @@ jobs: run: cargo +stable fmt - name: Check code linting - run: cargo clippy --tests --benches --features linting + run: cargo clippy --all-targets --all-features -- -D warnings - name: Check documentation linting run: cargo doc --no-deps --features linting diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index f95630e..8caf3fa 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -141,43 +141,41 @@ where } self.inputs.clear(); - match opcode { - Some( - op @ (OpCode::JUMPI - | OpCode::CALL - | OpCode::CALLCODE - | OpCode::DELEGATECALL - | OpCode::STATICCALL - | OpCode::SSTORE - | OpCode::SLOAD - | OpCode::ADD - | OpCode::SUB - | OpCode::MUL - | OpCode::DIV - | OpCode::SDIV - | OpCode::MOD - | OpCode::SMOD - | OpCode::EXP - | OpCode::LT - | OpCode::SLT - | OpCode::GT - | OpCode::SGT - | OpCode::EQ - | OpCode::AND - | OpCode::ADDMOD - | OpCode::MULMOD - | OpCode::KECCAK256), - ) => { - let num_inputs = op.inputs(); - for i in 0..num_inputs { - if let Ok(v) = interp.stack().peek(i as usize) { - self.inputs.push(v); - } else { - break; - } + if let Some( + op @ (OpCode::JUMPI + | OpCode::CALL + | OpCode::CALLCODE + | OpCode::DELEGATECALL + | OpCode::STATICCALL + | OpCode::SSTORE + | OpCode::SLOAD + | OpCode::ADD + | OpCode::SUB + | OpCode::MUL + | OpCode::DIV + | OpCode::SDIV + | OpCode::MOD + | OpCode::SMOD + | OpCode::EXP + | OpCode::LT + | OpCode::SLT + | OpCode::GT + | OpCode::SGT + | OpCode::EQ + | OpCode::AND + | OpCode::ADDMOD + | OpCode::MULMOD + | OpCode::KECCAK256), + ) = opcode + { + let num_inputs = op.inputs(); + for i in 0..num_inputs { + if let Ok(v) = interp.stack().peek(i as usize) { + self.inputs.push(v); + } else { + break; } } - _ => {} } self.inc_step_index(); From 13a7f1561cb912f52514018b5d7f49c6b6c8098e Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 15:51:20 +0800 Subject: [PATCH 28/40] fix doc linting --- .github/workflows/develop.yml | 2 +- src/fork_db.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 4e9d5f4..ffea136 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -61,7 +61,7 @@ jobs: run: cargo clippy --all-targets --all-features -- -D warnings - name: Check documentation linting - run: cargo doc --no-deps --features linting + run: cargo doc --no-deps --all-features - name: Run Rust tests run: cargo nextest run diff --git a/src/fork_db.rs b/src/fork_db.rs index 0c7bf66..9c0b572 100644 --- a/src/fork_db.rs +++ b/src/fork_db.rs @@ -22,7 +22,7 @@ pub struct ForkDB { pub accounts: HashMap, /// Tracks all contracts by their code hash. pub contracts: HashMap, - /// All cached block hashes from the [DatabaseRef]. + /// All cached block hashes pub block_hashes: HashMap, pub fork_enabled: bool, From ba39298c25073e570d5da35c2e98b929e1dd1d4a Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 16:25:03 +0800 Subject: [PATCH 29/40] skip fork test on ci --- .github/workflows/develop.yml | 1 + Cargo.lock | 12 +- Cargo.toml | 1 + src/lib.rs | 51 ++++++- tests/contracts/snapshots.compiled.json | 1 + tests/contracts/snapshots.sol | 30 ++++ tests/revm_test.rs | 24 +++- tests/test_account_snapshot.py | 3 +- tests/test_global_snapshot.py | 184 ++++++++++++++++++++++++ 9 files changed, 294 insertions(+), 13 deletions(-) create mode 100644 tests/contracts/snapshots.compiled.json create mode 100644 tests/contracts/snapshots.sol create mode 100644 tests/test_global_snapshot.py diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index ffea136..266c8a2 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -12,6 +12,7 @@ on: env: CARGO_TERM_COLOR: always SSH_AUTH_SOCK: /tmp/ssh_agent.sock + TINYEVM_CI_TESTS: true jobs: Test: diff --git a/Cargo.lock b/Cargo.lock index 66a660d..91ff13d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1514,7 +1514,7 @@ dependencies = [ "sha2", "sha3", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -4285,6 +4285,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "uuid 1.10.0", ] [[package]] @@ -4818,6 +4819,15 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2d4c2ba..8c34f4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ strum_macros = "0.26.4" hashbrown = "*" redis = { version= "0.25.4", optional = true} alloy = { version = "0.1.4", features = ["full"] } +uuid = { version = "1.9.1", features = ["v4"] } [dev-dependencies] criterion = {version="0.3.6", features=["html_reports"] } diff --git a/src/lib.rs b/src/lib.rs index 1093c89..0c05254 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,21 +8,21 @@ use ::revm::{ Evm, }; use cache::DefaultProviderCache; - use chain_inspector::ChainInspector; -use hashbrown::{HashMap, HashSet}; -use revm::inspector_handle_register; - use dotenv::dotenv; use ethers_providers::{Http, Provider}; use eyre::{eyre, ContextCompat, Result}; use fork_db::ForkDB; +use hashbrown::{HashMap, HashSet}; use lazy_static::lazy_static; use num_bigint::BigInt; use pyo3::prelude::*; use response::{Response, SeenPcsMap, WrappedBug, WrappedHeuristics, WrappedMissedBranch}; +use revm::inspector_handle_register; use thread_local::ThreadLocal; use tokio::runtime::Runtime; +use uuid::Uuid; + /// Caching for Web3 provider mod cache; mod chain_inspector; @@ -44,7 +44,7 @@ use instrument::{ bug_inspector::BugInspector, log_inspector::LogInspector, BugData, Heuristics, InstrumentConfig, }; use ruint::aliases::U256; -use std::{cell::Cell, str::FromStr}; +use std::{cell::Cell, mem::replace, str::FromStr}; use tracing::{debug, info, trace}; lazy_static! { @@ -90,6 +90,8 @@ pub struct TinyEVM { pub snapshots: HashMap, /// Optional fork url pub fork_url: Option, + /// Snapshot of global states + global_snapshot: HashMap>, } static mut TRACE_ENABLED: bool = false; @@ -118,6 +120,14 @@ pub fn enable_tracing() -> Result<()> { // Implementations for use in Rust impl TinyEVM { + fn db(&self) -> &ForkDB { + &self.exe.as_ref().unwrap().context.evm.db + } + + fn db_mut(&mut self) -> &mut ForkDB { + &mut self.exe.as_mut().unwrap().context.evm.db + } + pub fn instrument_config_mut(&mut self) -> &mut InstrumentConfig { &mut self.bug_inspector_mut().instrument_config } @@ -603,6 +613,7 @@ impl TinyEVM { owner, fork_url, snapshots: HashMap::with_capacity(32), + global_snapshot: Default::default(), }; Ok(tinyevm) @@ -1098,6 +1109,36 @@ impl TinyEVM { db.accounts.insert(addr, account.clone()); Ok(()) } + + /// Take global snapshot of all accounts + pub fn take_global_snapshot(&mut self) -> Result { + let db = self.db(); + let snapshot = db.clone(); + let id = Uuid::new_v4(); + self.global_snapshot.insert(id, snapshot); + Ok(id.to_string()) + } + + pub fn restore_global_snapshot( + &mut self, + snapshot_id: String, + keep_snapshot: bool, + ) -> Result<()> { + let id = Uuid::parse_str(&snapshot_id)?; + + if keep_snapshot { + let snapshot = self.global_snapshot.get(&id).context("No snapshot found")?; + *self.db_mut() = snapshot.clone(); + } else { + let snapshot = self + .global_snapshot + .remove(&id) + .context("No snapshot found")?; + let _ = replace(self.db_mut(), snapshot); + } + + Ok(()) + } } /// Configuration class for instrumentation, this is a wrapper for diff --git a/tests/contracts/snapshots.compiled.json b/tests/contracts/snapshots.compiled.json new file mode 100644 index 0000000..fed1013 --- /dev/null +++ b/tests/contracts/snapshots.compiled.json @@ -0,0 +1 @@ +{"contracts":{"tests/contracts/snapshots.sol:Test":{"abi":[{"inputs":[],"name":"counter","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"counter1","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"counter2","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"f_fast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"f_slow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fast_seq","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slow_seq","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"}],"bin":"6080604052600160025534801561001557600080fd5b50610423806100256000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80635a6ce6441161005b5780635a6ce644146101005780635bd11ee01461010a57806361bc221a1461012857806391575026146101465761007d565b80631ac7c6c5146100825780632b208f68146100a05780632d7ee760146100d0575b600080fd5b61008a610150565b60405161009791906102b9565b60405180910390f35b6100ba60048036038101906100b59190610305565b610185565b6040516100c791906102b9565b60405180910390f35b6100ea60048036038101906100e59190610305565b61019d565b6040516100f791906102b9565b60405180910390f35b6101086101b5565b005b61011261021d565b60405161011f91906102b9565b60405180910390f35b610130610252565b60405161013d91906102b9565b60405180910390f35b61014e610258565b005b600080600090505b600581121561017c576101696101b5565b808061017490610361565b915050610158565b50600254905090565b60016020528060005260406000206000915090505481565b60006020528060005260406000206000915090505481565b60005b603281121561021a576001600260008282546101d491906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461020091906103a9565b92505081905550808061021290610361565b9150506101b8565b50565b600080600090505b600581121561024957610236610258565b808061024190610361565b915050610225565b50600254905090565b60025481565b60016002600082825461026b91906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461029791906103a9565b92505081905550565b6000819050919050565b6102b3816102a0565b82525050565b60006020820190506102ce60008301846102aa565b92915050565b600080fd5b6102e2816102a0565b81146102ed57600080fd5b50565b6000813590506102ff816102d9565b92915050565b60006020828403121561031b5761031a6102d4565b5b6000610329848285016102f0565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061036c826102a0565b91507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361039e5761039d610332565b5b600182019050919050565b60006103b4826102a0565b91506103bf836102a0565b9250828201905082811215600083121683821260008412151617156103e7576103e6610332565b5b9291505056fea26469706673582212206e191165fdf82df5652c2babfba503ef181f18d0c5d6245eec3918dc3c73997164736f6c63430008130033","bin-runtime":"608060405234801561001057600080fd5b506004361061007d5760003560e01c80635a6ce6441161005b5780635a6ce644146101005780635bd11ee01461010a57806361bc221a1461012857806391575026146101465761007d565b80631ac7c6c5146100825780632b208f68146100a05780632d7ee760146100d0575b600080fd5b61008a610150565b60405161009791906102b9565b60405180910390f35b6100ba60048036038101906100b59190610305565b610185565b6040516100c791906102b9565b60405180910390f35b6100ea60048036038101906100e59190610305565b61019d565b6040516100f791906102b9565b60405180910390f35b6101086101b5565b005b61011261021d565b60405161011f91906102b9565b60405180910390f35b610130610252565b60405161013d91906102b9565b60405180910390f35b61014e610258565b005b600080600090505b600581121561017c576101696101b5565b808061017490610361565b915050610158565b50600254905090565b60016020528060005260406000206000915090505481565b60006020528060005260406000206000915090505481565b60005b603281121561021a576001600260008282546101d491906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461020091906103a9565b92505081905550808061021290610361565b9150506101b8565b50565b600080600090505b600581121561024957610236610258565b808061024190610361565b915050610225565b50600254905090565b60025481565b60016002600082825461026b91906103a9565b9250508190555060016000806002548152602001908152602001600020600082825461029791906103a9565b92505081905550565b6000819050919050565b6102b3816102a0565b82525050565b60006020820190506102ce60008301846102aa565b92915050565b600080fd5b6102e2816102a0565b81146102ed57600080fd5b50565b6000813590506102ff816102d9565b92915050565b60006020828403121561031b5761031a6102d4565b5b6000610329848285016102f0565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061036c826102a0565b91507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361039e5761039d610332565b5b600182019050919050565b60006103b4826102a0565b91506103bf836102a0565b9250828201905082811215600083121683821260008412151617156103e7576103e6610332565b5b9291505056fea26469706673582212206e191165fdf82df5652c2babfba503ef181f18d0c5d6245eec3918dc3c73997164736f6c63430008130033","srcmap":"56:682:0:-:0;;;191:1;167:25;;56:682;;;;;;;;;;;;;;;;","srcmap-runtime":"56:682:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;590:146;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;121:40;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;75;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;289:144;;;:::i;:::-;;439:146;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;167:25;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;198:86;;;:::i;:::-;;590:146;626:6;648:8;659:1;648:12;;643:63;666:1;662;:5;643:63;;;687:8;:6;:8::i;:::-;669:3;;;;;:::i;:::-;;;;643:63;;;;722:7;;715:14;;590:146;:::o;121:40::-;;;;;;;;;;;;;;;;;:::o;75:::-;;;;;;;;;;;;;;;;;:::o;289:144::-;328:8;323:104;346:2;342:1;:6;323:104;;;379:1;368:7;;:12;;;;;;;:::i;:::-;;;;;;;;415:1;394:8;:17;403:7;;394:17;;;;;;;;;;;;:22;;;;;;;:::i;:::-;;;;;;;;350:3;;;;;:::i;:::-;;;;323:104;;;;289:144::o;439:146::-;475:6;497:8;508:1;497:12;;492:63;515:1;511;:5;492:63;;;536:8;:6;:8::i;:::-;518:3;;;;;:::i;:::-;;;;492:63;;;;571:7;;564:14;;439:146;:::o;167:25::-;;;;:::o;198:86::-;244:1;233:7;;:12;;;;;;;:::i;:::-;;;;;;;;276:1;255:8;:17;264:7;;255:17;;;;;;;;;;;;:22;;;;;;;:::i;:::-;;;;;;;;198:86::o;7:76:1:-;43:7;72:5;61:16;;7:76;;;:::o;89:115::-;174:23;191:5;174:23;:::i;:::-;169:3;162:36;89:115;;:::o;210:218::-;301:4;339:2;328:9;324:18;316:26;;352:69;418:1;407:9;403:17;394:6;352:69;:::i;:::-;210:218;;;;:::o;515:117::-;624:1;621;614:12;761:120;833:23;850:5;833:23;:::i;:::-;826:5;823:34;813:62;;871:1;868;861:12;813:62;761:120;:::o;887:137::-;932:5;970:6;957:20;948:29;;986:32;1012:5;986:32;:::i;:::-;887:137;;;;:::o;1030:327::-;1088:6;1137:2;1125:9;1116:7;1112:23;1108:32;1105:119;;;1143:79;;:::i;:::-;1105:119;1263:1;1288:52;1332:7;1323:6;1312:9;1308:22;1288:52;:::i;:::-;1278:62;;1234:116;1030:327;;;;:::o;1363:180::-;1411:77;1408:1;1401:88;1508:4;1505:1;1498:15;1532:4;1529:1;1522:15;1549:231;1587:3;1610:23;1627:5;1610:23;:::i;:::-;1601:32;;1655:66;1648:5;1645:77;1642:103;;1725:18;;:::i;:::-;1642:103;1772:1;1765:5;1761:13;1754:20;;1549:231;;;:::o;1786:375::-;1825:3;1844:19;1861:1;1844:19;:::i;:::-;1839:24;;1877:19;1894:1;1877:19;:::i;:::-;1872:24;;1919:1;1916;1912:9;1905:16;;2117:1;2112:3;2108:11;2101:19;2097:1;2094;2090:9;2086:35;2069:1;2064:3;2060:11;2055:1;2052;2048:9;2041:17;2037:35;2021:110;2018:136;;;2134:18;;:::i;:::-;2018:136;1786:375;;;;:::o"}},"sourceList":["tests/contracts/snapshots.sol"],"version":"0.8.19+commit.7dd6d404.Linux.g++"} diff --git a/tests/contracts/snapshots.sol b/tests/contracts/snapshots.sol new file mode 100644 index 0000000..17e983c --- /dev/null +++ b/tests/contracts/snapshots.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; +contract Test{ + mapping (int256=>int256) public counter1; + mapping (int256=>int256) public counter2; + int256 public counter = 1; + function f_fast() public { + counter += 1; + counter1[counter] += 1; + } + function f_slow() public{ + for (int256 i = 0; i < 50; i++){ + counter += 1; + counter1[counter] += 1; + } + } + + function fast_seq() public returns (int256){ + for (int256 i = 0; i < 5; i++){ + f_fast(); + } + return counter; + } + function slow_seq() public returns (int256){ + for (int256 i = 0; i < 5; i++){ + f_slow(); + } + return counter; + } +} diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 4200695..1befa29 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -10,10 +10,12 @@ use revm::primitives::Address; use ruint::aliases::U256; use std::collections::HashSet; use std::convert::TryInto; +use std::env; use std::iter::repeat_with; use std::ops::Add; use std::str::FromStr; use tinyevm::instrument::bug::{Bug, BugType, MissedBranch}; +use tracing::warn; use tinyevm::{ enable_tracing, fn_sig_to_prefix, ruint_u256_to_bigint, trim_prefix, TinyEVM, TX_GAS_LIMIT, @@ -706,7 +708,7 @@ fn test_mod_zero() { #[test] fn test_gas_usage() { - // let _ = enable_tracing(); + setup(); let owner = *OWNER; // deploy_hex!("../tests/contracts/gasusage.hex", exe, address); @@ -784,7 +786,7 @@ fn test_set_get_storage() { #[test] fn test_set_get_code() { - // let _ = enable_tracing(); + setup(); let owner = Address::new(H160::random().0); let mut vm = TinyEVM::default(); @@ -809,7 +811,7 @@ fn test_set_get_code() { #[test] fn test_exp_overflow() { - let _ = enable_tracing(); + setup(); let owner = *OWNER; deploy_hex!("../tests/contracts/exp_overflow.hex", vm, address); @@ -1392,6 +1394,11 @@ fn test_peephole_optimized_if_equal() { #[test] fn test_fork() -> Result<()> { + setup(); + if env::var("TINYEVM_CI_TESTS").is_ok() { + warn!("Skipping tests on CI"); + } + let fork_url = Some("https://eth.llamarpc.com".into()); let block_id = Some(17869485); @@ -1420,12 +1427,15 @@ fn test_fork() -> Result<()> { #[test] fn test_call_forked_contract_from_local_contract() -> Result<()> { setup(); + if env::var("TINYEVM_CI_TESTS").is_ok() { + warn!("Skipping tests on CI"); + } + let bin = include_str!("../tests/contracts/test_fork.hex"); let fork_url = Some("https://bscrpc.com".into()); let block_id = Some(0x1e08bd6); let mut evm = TinyEVM::new(fork_url, block_id)?; - enable_tracing()?; let resp = evm.deploy(bin.into(), None)?; @@ -1492,12 +1502,16 @@ fn test_call_forked_contract_from_local_contract() -> Result<()> { #[test] fn test_sturdy_hack() -> Result<()> { + setup(); + if env::var("TINYEVM_CI_TESTS").is_ok() { + warn!("Skipping tests on CI"); + } + let bin = include_str!("../tests/contracts/SturdyFinance_ReadonlyRE.hex"); let fork_url = Some("https://eth.llamarpc.com".into()); let block_id = Some(17_460_609); let mut evm = TinyEVM::new(fork_url, block_id)?; - enable_tracing()?; let resp = evm.deploy(bin.into(), None)?; diff --git a/tests/test_account_snapshot.py b/tests/test_account_snapshot.py index 27ec533..a8ac7e6 100644 --- a/tests/test_account_snapshot.py +++ b/tests/test_account_snapshot.py @@ -6,7 +6,6 @@ import json from eth_abi import encode - def handle_exception(exc_type, exc_value, exc_traceback): if issubclass(exc_type, KeyboardInterrupt): sys.__excepthook__(exc_type, exc_value, exc_traceback) @@ -140,7 +139,7 @@ def reset_contract_call(contract, owner): -def test_snapshot(benchmark): +def test_account_snapshot(benchmark): owner = '0x9C33eaCc2F50E39940D3AfaF2c7B8246B681A374' contract = deploy_contract(salt='0x1fff0000000000000000000000000000000000000000000000000000000000ff', owner=owner) def reset_contract(): diff --git a/tests/test_global_snapshot.py b/tests/test_global_snapshot.py new file mode 100644 index 0000000..00f19ca --- /dev/null +++ b/tests/test_global_snapshot.py @@ -0,0 +1,184 @@ +import tinyevm +from Crypto.Hash import keccak +import threading +import sys +from datetime import datetime +import json +from eth_abi import encode +from memory_profiler import memory_usage, profile +import gc +import pytest + +# @pytest.fixture +# def benchmark_memory(): +# def benchmark_memory_fn(func, *args, **kwargs): +# mem_usage = memory_usage((func, args, kwargs), interval=0.1, timeout=1) +# return max(mem_usage) - min(mem_usage) +# return benchmark_memory_fn + +def handle_exception(exc_type, exc_value, exc_traceback): + if issubclass(exc_type, KeyboardInterrupt): + sys.__excepthook__(exc_type, exc_value, exc_traceback) + return + + print("Uncaught exception", exc_type, exc_value) + sys.exit(1) + +sys.excepthook = handle_exception + + +def fn_sig(sig): + k = keccak.new(digest_bits=256) + k.update(sig.encode()) + return k.hexdigest()[:8] + +def tprint(*args): + # print(datetime.now(), threading.current_thread().name, *args) + pass + +def encode_balance_of(address): + # Compute the method id for the balanceOf function + method_id = "0x70a08231" # function signature for balanceOf(address) + + # ABI encode the address + address_param = encode('address', address).hex() + + # Concatenate the method id and the address parameter + data = method_id + address_param + + return data + +tevm = tinyevm.TinyEVM() + +def transfer_1000(contract, sender): + fn = fn_sig('transfer(address,uint256)') + data = encode(['address', 'uint256'], ['0x44Eadb1b1288F4883F2166846800335bfFa290be', 1000]).hex() + resp = tevm.contract_call(contract, sender, fn + data, None) + assert resp.success + + +def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297'): + """ + Deploy and check the balance of the contract + """ + # generated with + # solc --combined-json abi,bin,bin-runtime,srcmap,srcmap-runtime tests/contracts/snapshots.sol > tests/contracts/snapshots.compiled.json + with open('tests/contracts/snapshots.compiled.json') as f: + out = json.load(f) + + binary = out['contracts']['tests/contracts/snapshots.sol:Test']['bin'] + + tevm.set_balance(owner, 0xffff0000000000000000000000000000000000000000000000000000000000ff) + + data = '' + value = None + init_value = None + resp = tevm.deterministic_deploy(binary, salt, owner, data, value, init_value) + tprint('Deployment resp: {}'.format(resp)) + + assert resp.success + + contract = bytes(resp.data).hex() + tprint('Contract deployed to: {}'.format(contract or 'EMPTY')) + + return contract + + +def run_global_snapshot(keep_snapshot: bool, x_fns: list, disable_snapshot=False, take_snapshot_after_each_tx=False): + owner = '0x1e209e340405D4211a3185f97628179917883505' + init_count_value = 1 + + # deploy some contracts to populate a few more acounts + deploy_contract(salt='0x1fff00000000000000000000000000000000000000000000000000000000eeff') + deploy_contract(salt='0x1fff000000000000000000000000000000000000000000000000000000cceeff') + deploy_contract(salt='0x1fff000000000000000000000000000000000000000000000000000000aaeeff') + + # deploy and take a global snapshot + contract = deploy_contract(owner=owner) + snapshot_id = None + if not disable_snapshot: + snapshot_id = tevm.take_global_snapshot() + + # make sure we've deployed successfully by checking the initial states + call_data = fn_sig('counter()') + resp = tevm.contract_call(contract, None, call_data, None) + tprint(f"counter() resp: {resp.data}") + assert resp.success + count = int.from_bytes(bytes(resp.data), 'big') + assert count == init_count_value + + # make some transactions + for (fn, expected_return_value) in x_fns: + call_data = fn_sig(fn) + resp = tevm.contract_call(contract, owner, call_data, None) + assert resp.success + count = int.from_bytes(bytes(resp.data), 'big') + if expected_return_value is not None: + assert count == expected_return_value + if take_snapshot_after_each_tx and not disable_snapshot: + tevm.take_global_snapshot() + + # restore the snapshot + if not disable_snapshot: + for _ in range(1000): + tevm.restore_global_snapshot(snapshot_id, keep_snapshot) + + # check the global state reverted to previous state + call_data = fn_sig('counter()') + resp = tevm.contract_call(contract, owner, call_data, None) + assert resp.success + count = int.from_bytes(bytes(resp.data), 'big') + if not disable_snapshot: + assert count == init_count_value + + + +x_fns_fastseq = [('fast_seq()', None),] * 1000 + +x_fns_slowseq = [('slow_seq()', None), ] * 100 + +@pytest.mark.parametrize("x_fns, disable_snapshot, name", [ + (x_fns_fastseq, False, "fastseq take and restore snapshot"), + (x_fns_slowseq, False, "slowseq take and restore snapshot"), + (x_fns_fastseq, True, "fastseq no snapshot"), + (x_fns_slowseq, True, "slowseq no snapshot"), +]) +def test_global_snapshot(benchmark, x_fns, disable_snapshot, name): + global tevm + tevm = tinyevm.TinyEVM() + def run(): + run_global_snapshot(True, x_fns, disable_snapshot=disable_snapshot) + benchmark(run) + + + +@profile +def test_compare_memory(): + x_fns_fastseq = [('fast_seq()', 1 + 5 * i) for i in range(1, 1000)] + x_fns_slowseq = [('slow_seq()', 1 + 5 * 50 * i) for i in range(1, 100)] + global tevm + + + # Running 100 slowseq without taking snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_slowseq, disable_snapshot=True) + gc.collect() + + # Running 1000 fastseq without taking snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_fastseq, disable_snapshot=True) + gc.collect() + + # Running slowseq taking 100 snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_slowseq, take_snapshot_after_each_tx=True) + gc.collect() + + # Running fastseq taking 1000 snapshots + tevm = tinyevm.TinyEVM() + gc.collect() + run_global_snapshot(True, x_fns_fastseq, take_snapshot_after_each_tx=True) + gc.collect() From 6d798beecf8b62517799a5fe89312c12c51c364f Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 16:33:30 +0800 Subject: [PATCH 30/40] update package name --- Cargo.lock | 72 +++++++++++++++++++++++----------------------- Cargo.toml | 4 +-- Makefile | 4 +-- tests/revm_test.rs | 3 ++ 4 files changed, 43 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91ff13d..4ef0954 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4252,42 +4252,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tevm" -version = "0.1.0" -dependencies = [ - "alloy", - "criterion", - "dotenv", - "ethers", - "ethers-contract", - "ethers-core", - "ethers-middleware", - "ethers-providers", - "eyre", - "hashbrown", - "hex", - "lazy_static", - "libc", - "maplit", - "num-bigint", - "primitive-types", - "pyo3", - "redis", - "revm", - "ruint", - "serde", - "serde_json", - "sha3", - "simple_logger", - "strum_macros", - "thread_local", - "tokio", - "tracing", - "tracing-subscriber", - "uuid 1.10.0", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -4378,6 +4342,42 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinyevm" +version = "1.0.0" +dependencies = [ + "alloy", + "criterion", + "dotenv", + "ethers", + "ethers-contract", + "ethers-core", + "ethers-middleware", + "ethers-providers", + "eyre", + "hashbrown", + "hex", + "lazy_static", + "libc", + "maplit", + "num-bigint", + "primitive-types", + "pyo3", + "redis", + "revm", + "ruint", + "serde", + "serde_json", + "sha3", + "simple_logger", + "strum_macros", + "thread_local", + "tokio", + "tracing", + "tracing-subscriber", + "uuid 1.10.0", +] + [[package]] name = "tinytemplate" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 8c34f4e..2feda2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "tevm" -version = "0.1.0" +name = "tinyevm" +version = "1.0.0" edition = "2021" [lib] diff --git a/Makefile b/Makefile index 306bbc2..038c47c 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,11 @@ prepare: cargo install cargo-criterion pip install -r requirements-dev.txt linting: - cargo clippy --tests --benches --features linting + cargo clippy --tests --benches --all-features -- -D warnings build: maturin build --release -i 3.9 test: - cargo nextest run --features linting + cargo nextest run --no-fail-fast --success-output=never maturin develop --release pytest -s --show-capture all bench: diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 1befa29..ebb4f41 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1397,6 +1397,7 @@ fn test_fork() -> Result<()> { setup(); if env::var("TINYEVM_CI_TESTS").is_ok() { warn!("Skipping tests on CI"); + return Ok(()); } let fork_url = Some("https://eth.llamarpc.com".into()); @@ -1429,6 +1430,7 @@ fn test_call_forked_contract_from_local_contract() -> Result<()> { setup(); if env::var("TINYEVM_CI_TESTS").is_ok() { warn!("Skipping tests on CI"); + return Ok(()); } let bin = include_str!("../tests/contracts/test_fork.hex"); @@ -1505,6 +1507,7 @@ fn test_sturdy_hack() -> Result<()> { setup(); if env::var("TINYEVM_CI_TESTS").is_ok() { warn!("Skipping tests on CI"); + return Ok(()); } let bin = include_str!("../tests/contracts/SturdyFinance_ReadonlyRE.hex"); From f64dcae04e646e3756bd0ce135cf39b9db7362d9 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 17:38:50 +0800 Subject: [PATCH 31/40] more updates --- README.md | 13 ++++++++++++- src/instrument/bug_inspector.rs | 5 ----- src/lib.rs | 24 +++++++++++++++--------- tests/revm_test.rs | 2 +- tests/test_account_snapshot.py | 2 +- tests/test_global_snapshot.py | 2 +- tests/test_multithreading.py | 4 ++-- tests/test_tinyevm.py | 5 ++--- 8 files changed, 34 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 820a16d..f74901d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,11 @@ After PR is appproved, please merge your changes to `develop`: ## Sample usage -You can find example usage in Python at `example/example.py` and `example/example-bug-detected.py` +``` python +# todo add a python example +``` + +You can find example usage in Python in the `example/` folder. Example files: @@ -212,3 +216,10 @@ Legend: OPS: Operations Per Second, computed as 1 / Mean ============================================================================================================== 7 passed in 8.21s =============================================================================================================== ``` + +## Changes + +### 1.0.0 + +Breaking changes: +- `deterministic_deploy` signature is now changed to `deterministic_deploy(contract_deploy_code, owner=None, data=None, value=None, init_value=None, deploy_to_address=None)`, i.e., `salt` is removed. [Related to [REVM-1182](https://github.com/bluealloy/revm/issues/1182)]. If you want to similate the previous behaviour, it's suggested to use the `deploy_to_address` parameter to specify the address where the contract will be deployed. diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index 8caf3fa..d5704b5 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -246,7 +246,6 @@ where } } Some(op @ OpCode::EXP) => { - println!("checking-exp-overflow"); // todo_cl check for overflow if let (Some(a), Some(b), Ok(r)) = ( self.inputs.first(), @@ -441,10 +440,6 @@ where if let (Some(counter), Some(cond)) = (self.inputs.first(), self.inputs.get(1)) { // Check for distance in peephole optimized if-statement if self.possibly_if_equal() { - debug!( - "Possible peephole optimized if-statement found, inputs: {:?} pc {}", - self.inputs, self.pc - ); let max = U256::MAX; let mut half = U256::MAX; half.set_bit(31, false); diff --git a/src/lib.rs b/src/lib.rs index 0c05254..5a64272 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,9 @@ pub struct TinyEVM { /// REVM instance pub exe: Option>, pub owner: Address, + /// Default gas limit for each transaction + #[pyo3(get, set)] + tx_gas_limit: u64, /// Snapshots of account state pub snapshots: HashMap, /// Optional fork url @@ -271,7 +274,7 @@ impl TinyEVM { tx.transact_to = TransactTo::Create; tx.data = contract_bytecode.clone().into(); tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); + tx.gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); } // todo_cl this is read from global state, might be wrong @@ -612,6 +615,7 @@ impl TinyEVM { exe: Some(exe), owner, fork_url, + tx_gas_limit: TX_GAS_LIMIT, snapshots: HashMap::with_capacity(32), global_snapshot: Default::default(), }; @@ -700,27 +704,26 @@ impl TinyEVM { /// /// For optional arguments, you can use the empty string as inputs to use the default values. /// - /// [Source: ] - /// /// - `contract_deploy_code`: contract deploy binary array encoded as hex string - /// - `deploy_to_address`: Deploy the contract to the address /// - `owner`: Owner address as a 20-byte array encoded as hex string /// - `data`: (Optional, default empty) Constructor arguments encoded as hex string. /// - `value`: (Optional, default 0) a U256. Set the value to be included in the contract creation transaction. + /// - `deploy_to_address`: when provided, change the address of the deployed contract to this address + /// - This requires the constructor to be payable. /// - The transaction sender (owner) must have enough balance /// - `init_value`: (Optional) BigInt. Override the initial balance of the contract to this value. /// /// Returns a list consisting of 4 items `[reason, address-as-byte-array, bug_data, heuristics]` - #[pyo3(signature = (contract_deploy_code, deploy_to_address, owner=None, data=None, value=None, init_value=None))] + #[pyo3(signature = (contract_deploy_code, owner=None, data=None, value=None, init_value=None, deploy_to_address=None))] pub fn deterministic_deploy( &mut self, contract_deploy_code: String, // variable length - deploy_to_address: String, - owner: Option, // h160 as hex string - data: Option, // variable length + owner: Option, // h160 as hex string + data: Option, // variable length value: Option, init_value: Option, + deploy_to_address: Option, ) -> Result { let owner = { if let Some(owner) = owner { @@ -742,6 +745,9 @@ impl TinyEVM { let value = value.unwrap_or_default(); let mut contract_bytecode = contract_deploy_code.to_vec(); contract_bytecode.extend(data); + let force_address: Option
= deploy_to_address + .map(|s| Address::from_str(&s)) + .transpose()?; let resp = { let resp = self.deploy_helper( @@ -749,7 +755,7 @@ impl TinyEVM { contract_bytecode, bigint_to_ruint_u256(&value)?, None, - Some(Address::from_str(&deploy_to_address)?), + force_address, )?; if let Some(balance) = init_value { diff --git a/tests/revm_test.rs b/tests/revm_test.rs index ebb4f41..00f4e90 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -1298,7 +1298,7 @@ fn test_seen_addresses() { #[test] fn test_distance_signed() { - let _ = enable_tracing(); + setup(); deploy_hex!("../tests/contracts/test_distance_signed.hex", vm, address); let address = Address::new(address.0); let fn_sig = "sign_distance(int256)"; diff --git a/tests/test_account_snapshot.py b/tests/test_account_snapshot.py index a8ac7e6..6083e92 100644 --- a/tests/test_account_snapshot.py +++ b/tests/test_account_snapshot.py @@ -70,7 +70,7 @@ def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297 tevm.set_balance(owner, 0xffff0000000000000000000000000000000000000000000000000000000000ff) - resp = tevm.deterministic_deploy(binary, salt, owner, data, value, init_value) + resp = tevm.deterministic_deploy(binary, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success diff --git a/tests/test_global_snapshot.py b/tests/test_global_snapshot.py index 00f19ca..7f6b73d 100644 --- a/tests/test_global_snapshot.py +++ b/tests/test_global_snapshot.py @@ -73,7 +73,7 @@ def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297 data = '' value = None init_value = None - resp = tevm.deterministic_deploy(binary, salt, owner, data, value, init_value) + resp = tevm.deterministic_deploy(binary, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success diff --git a/tests/test_multithreading.py b/tests/test_multithreading.py index 5d17b2a..5886ce7 100644 --- a/tests/test_multithreading.py +++ b/tests/test_multithreading.py @@ -44,7 +44,7 @@ def run_test(): tprint('balance before deployment: {}'.format(balance)) # todo update response object - resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) + resp = tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success @@ -86,7 +86,7 @@ def run_infinite_loop(): tprint('balance before deployment: {}'.format(balance)) # todo update response object - resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) + resp = tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success diff --git a/tests/test_tinyevm.py b/tests/test_tinyevm.py index 38ae392..ed0aede 100644 --- a/tests/test_tinyevm.py +++ b/tests/test_tinyevm.py @@ -50,14 +50,13 @@ def test_get_change_tx_gas_limit(self): assert tevm.tx_gas_limit == 100, 'default_tx_gas_limit should be changed to 100' contract_bytecode = open('tests/contracts/C.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None init_value = 0x223312323 try: - tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) + tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) except RuntimeError as e: tprint('Expected error: {}'.format(e)) assert 'OutOfGas' in str(e), 'should raise out of gas error' @@ -91,7 +90,7 @@ def test_deployment(self): tprint('balance before deployment: {}'.format(balance)) # todo update response object - resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) + resp = tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) bugs = list(resp.bug_data) From e5cc53a49bf6fcfa9cc6c0e4452690dc40c29706 Mon Sep 17 00:00:00 2001 From: chen Date: Tue, 16 Jul 2024 17:51:49 +0800 Subject: [PATCH 32/40] . --- src/lib.rs | 4 ++-- tests/test_tinyevm.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5a64272..4049a99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -381,7 +381,7 @@ impl TinyEVM { tx.transact_to = TransactTo::Call(contract); tx.data = data.into(); tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(TX_GAS_LIMIT); + tx.gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); } let result = { @@ -615,7 +615,7 @@ impl TinyEVM { exe: Some(exe), owner, fork_url, - tx_gas_limit: TX_GAS_LIMIT, + tx_gas_limit: u64::MAX, snapshots: HashMap::with_capacity(32), global_snapshot: Default::default(), }; diff --git a/tests/test_tinyevm.py b/tests/test_tinyevm.py index ed0aede..6609172 100644 --- a/tests/test_tinyevm.py +++ b/tests/test_tinyevm.py @@ -35,9 +35,9 @@ def test_get_set_env_field(self): tevm = tinyevm.TinyEVM() # block_number - assert tevm.get_env_value_by_field('block_number') == '0x0000000000000000000000000000000000000000000000000000000000000000' + assert int(tevm.get_env_value_by_field('block_number'), 16) == int('0x0000000000000000000000000000000000000000000000000000000000000000', 16) tevm.set_env_field_value('block_number', '0x00000000000000000000000000000000000000000000000000000000000000ff') - assert tevm.get_env_value_by_field('block_number') == '0x00000000000000000000000000000000000000000000000000000000000000ff' + assert int(tevm.get_env_value_by_field('block_number'), 16) == int('0x00000000000000000000000000000000000000000000000000000000000000ff', 16) # origin assert tevm.get_env_value_by_field('origin') == '0x0000000000000000000000000000000000000000' @@ -46,9 +46,9 @@ def test_get_set_env_field(self): def test_get_change_tx_gas_limit(self): tevm = tinyevm.TinyEVM() - tevm.tx_gas_limit = 100 + tevm.tx_gas_limit = 0xffffffffffff - assert tevm.tx_gas_limit == 100, 'default_tx_gas_limit should be changed to 100' + assert tevm.tx_gas_limit == 0xffffffffffff, 'default_tx_gas_limit should be changed to 100' contract_bytecode = open('tests/contracts/C.hex').read() owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None From 09cc550dd9fc8a11f37cb96f18d09fab359a6e60 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 11:10:51 +0800 Subject: [PATCH 33/40] limit tx gas --- README.md | 1 + src/lib.rs | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f74901d..9445fff 100644 --- a/README.md +++ b/README.md @@ -223,3 +223,4 @@ Legend: Breaking changes: - `deterministic_deploy` signature is now changed to `deterministic_deploy(contract_deploy_code, owner=None, data=None, value=None, init_value=None, deploy_to_address=None)`, i.e., `salt` is removed. [Related to [REVM-1182](https://github.com/bluealloy/revm/issues/1182)]. If you want to similate the previous behaviour, it's suggested to use the `deploy_to_address` parameter to specify the address where the contract will be deployed. +- `enabled` in `REVMConfig` is removed. To disable all instrumentation, use diff --git a/src/lib.rs b/src/lib.rs index 4049a99..730d23e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -277,7 +277,7 @@ impl TinyEVM { tx.gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); } - // todo_cl this is read from global state, might be wrong + // todo this is read from global state, might be wrong let nonce = self .exe .as_ref() @@ -546,7 +546,6 @@ impl TinyEVM { let fork_enabled = fork_url.is_some(); - // let mut db = InMemoryDB::default(); let mut db = match fork_url { Some(ref url) => { info!("Starting EVM from fork {} and block: {:?}", url, block_id); @@ -603,8 +602,6 @@ impl TinyEVM { bug_inspector: Some(bug_inspector), }; - // builder = builder.with_external_context(inspector); - let exe = Evm::builder() .modify_env(|e| *e = Box::new(env.clone())) .with_db(db.clone()) @@ -615,7 +612,7 @@ impl TinyEVM { exe: Some(exe), owner, fork_url, - tx_gas_limit: u64::MAX, + tx_gas_limit: TX_GAS_LIMIT, snapshots: HashMap::with_capacity(32), global_snapshot: Default::default(), }; From fef2d760b5a31c4b9408de6d2fd61ed044655065 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 11:23:11 +0800 Subject: [PATCH 34/40] add enabled flag --- src/instrument/bug.rs | 3 +++ src/instrument/bug_inspector.rs | 15 +++++++++++++++ src/lib.rs | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/src/instrument/bug.rs b/src/instrument/bug.rs index f33b8ae..ca44ceb 100644 --- a/src/instrument/bug.rs +++ b/src/instrument/bug.rs @@ -196,6 +196,8 @@ impl Heuristics { #[derive(Clone, Debug)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] pub struct InstrumentConfig { + /// Set true to enable bug_inspector + pub enabled: bool, /// Enable recording seen PCs by current contract address pub pcs_by_address: bool, /// Enable heuristics which will record list of jumpi destinations @@ -215,6 +217,7 @@ pub struct InstrumentConfig { impl Default for InstrumentConfig { fn default() -> InstrumentConfig { InstrumentConfig { + enabled: true, pcs_by_address: true, heuristics: true, record_branch_for_target_only: false, diff --git a/src/instrument/bug_inspector.rs b/src/instrument/bug_inspector.rs index d5704b5..1994387 100644 --- a/src/instrument/bug_inspector.rs +++ b/src/instrument/bug_inspector.rs @@ -38,6 +38,10 @@ pub struct BugInspector { } impl BugInspector { + pub fn enabled(&self) -> bool { + self.instrument_config.enabled + } + pub fn inc_step_index(&mut self) { self.step_index += 1; } @@ -125,6 +129,10 @@ where { #[inline] fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + if !self.enabled() { + return; + } + let _ = interp; let _ = context; let opcode = interp.current_opcode(); @@ -183,6 +191,9 @@ where #[inline] fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { + if !self.enabled() { + return; + } let address = interp.contract().target_address; let address_index = self.record_seen_address(address); let opcode = self.opcode; @@ -528,6 +539,10 @@ where _inputs: &CreateInputs, outcome: CreateOutcome, ) -> CreateOutcome { + if !self.enabled() { + return outcome; + } + let CreateOutcome { result, address } = &outcome; if let Some(address) = address { if let Some(override_address) = self.create_address_overrides.get(address) { diff --git a/src/lib.rs b/src/lib.rs index 730d23e..8645334 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1148,6 +1148,8 @@ impl TinyEVM { /// REVM::InstrumentConfig #[pyclass(set_all, get_all)] pub struct REVMConfig { + /// Enable the bug detector instrumentation + pub enabled: bool, /// Enable recording seen PCs by current contract address pub pcs_by_address: bool, /// Enable heuristics which will record list of jumpi destinations @@ -1191,6 +1193,7 @@ impl REVMConfig { }; Ok(InstrumentConfig { + enabled: self.enabled, target_address, pcs_by_address: self.pcs_by_address, heuristics: self.heuristics, @@ -1202,6 +1205,7 @@ impl REVMConfig { /// Convert to `REVMConfig` from internal Rust struct fn from(config: &InstrumentConfig) -> Self { Self { + enabled: config.enabled, pcs_by_address: config.pcs_by_address, heuristics: config.heuristics, record_branch_for_target_only: config.record_branch_for_target_only, From 3c7a806c60fa150c1b4b68531065c758646dad3e Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 11:24:26 +0800 Subject: [PATCH 35/40] fix double quoted hex int --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8645334..d35c08a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -890,7 +890,7 @@ impl TinyEVM { let exe = &self.exe.as_ref().unwrap(); macro_rules! hex2str { ($val:expr) => { - serde_json::to_string(&$val).unwrap() + format!("{:#066x}", $val) }; } From 3677a54f5b26c6d507debc145089de1779067cd1 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 12:02:59 +0800 Subject: [PATCH 36/40] add benchmark in readme --- README.md | 88 ++++++++++++++--------------------- tests/test_fork.py | 3 +- tests/test_global_snapshot.py | 8 ++-- 3 files changed, 42 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 9445fff..5be3e40 100644 --- a/README.md +++ b/README.md @@ -156,67 +156,51 @@ Additionally, you must set the environment variable `TINYEVM_REDIS_NODE` to a va ## Global snapshot benchmarks -* Using the naive implementation which clones the whole data structure +To run the test: ``` bash +maturing develop --release +pytest -s --show-capture all tests/test_global_snapshot.py +``` + + +* Results from legacy Tinyevm (using instrumented REVM) commit `839b0b8822702b49096bc2bf3f092c7a1aab13a3`: + +``` text ============================================================================================================= test session starts ============================================================================================================== platform linux -- Python 3.9.7, pytest-7.2.2, pluggy-1.0.0 benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000) rootdir: /home/garfield/projects/sbip-sg/tinyevm plugins: cov-4.1.0, web3-5.31.2, hypothesis-6.82.0, anyio-3.6.2, benchmark-4.0.0, xdist-3.3.1 -collected 7 items - -tests/test_global_snapshot.py ......Filename: /home/garfield/projects/sbip-sg/tinyevm/tests/test_global_snapshot.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 153 97.3 MiB 97.3 MiB 1 @profile - 154 def test_compare_memory(): - 155 97.3 MiB 0.0 MiB 1002 x_fns_fastseq = [('fast_seq()', 1 + 5 * i) for i in range(1, 1000)] - 156 97.3 MiB 0.0 MiB 102 x_fns_slowseq = [('slow_seq()', 1 + 5 * 50 * i) for i in range(1, 100)] - 157 global tevm - 158 - 159 # Running fastseq taking 100 snapshots - 160 97.3 MiB 0.0 MiB 1 tevm = tinyevm.TinyEVM() - 161 97.3 MiB 0.0 MiB 1 gc.collect() - 162 363.6 MiB 266.2 MiB 1 run_global_snapshot(True, x_fns_fastseq, take_snapshot_after_each_tx=True) - 163 - 164 # Running slowseq taking 100 snapshots - 165 217.3 MiB -146.3 MiB 1 tevm = tinyevm.TinyEVM() - 166 217.3 MiB 0.0 MiB 1 gc.collect() - 167 229.4 MiB 12.1 MiB 1 run_global_snapshot(True, x_fns_slowseq, take_snapshot_after_each_tx=True) - 168 - 169 # Running 1000 fastseq without taking snapshots - 170 150.0 MiB -79.4 MiB 1 tevm = tinyevm.TinyEVM() - 171 150.0 MiB 0.0 MiB 1 gc.collect() - 172 150.0 MiB 0.0 MiB 1 run_global_snapshot(True, x_fns_fastseq, disable_snapshot=True) - 173 - 174 # Running 100 slowseq without taking snapshots - 175 150.0 MiB 0.0 MiB 1 tevm = tinyevm.TinyEVM() - 176 150.0 MiB 0.0 MiB 1 gc.collect() - 177 150.0 MiB 0.0 MiB 1 run_global_snapshot(True, x_fns_slowseq, disable_snapshot=True) - - -. - - ------------------------------------------------------------------------------------------------------------------------------------- benchmark: 6 tests ----------------------------------------------------------------------------------------------------------------------------------- -Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -test_global_snapshot[False-x_fns4-True-fastseq don't take snapshot] 380.7210 (1.0) 864.9290 (1.0) 414.0279 (1.0) 47.1819 (1.0) 399.9720 (1.0) 14.6963 (1.0) 126;194 2,415.2963 (1.0) 1917 1 -test_global_snapshot[False-x_fns1-False-fastseq take and restore snapshot, don't keep after restore] 395.2560 (1.04) 1,188.2490 (1.37) 432.9322 (1.05) 71.7241 (1.52) 408.4960 (1.02) 25.4053 (1.73) 165;235 2,309.8306 (0.96) 1899 1 -test_global_snapshot[True-x_fns0-False-fastseq take and restore snapshot, keep after restore] 405.6830 (1.07) 1,157.2750 (1.34) 491.9984 (1.19) 129.4663 (2.74) 421.2585 (1.05) 100.5715 (6.84) 99;86 2,032.5268 (0.84) 448 1 -test_global_snapshot[False-x_fns5-True-slowseq don't take snapshot] 4,965.2520 (13.04) 7,654.7660 (8.85) 5,348.2192 (12.92) 419.9397 (8.90) 5,206.4350 (13.02) 247.7357 (16.86) 12;12 186.9781 (0.08) 173 1 -test_global_snapshot[False-x_fns3-False-slowseq take and restore snapshot, don't keep after restore] 4,989.4110 (13.11) 9,267.5720 (10.71) 5,320.8998 (12.85) 383.5033 (8.13) 5,216.9890 (13.04) 197.7330 (13.45) 7;11 187.9381 (0.08) 171 1 -test_global_snapshot[True-x_fns2-False-slowseq take and restore snapshot, keep after restore] 5,056.2300 (13.28) 7,849.9280 (9.08) 5,392.2435 (13.02) 327.4688 (6.94) 5,311.0770 (13.28) 268.5015 (18.27) 15;6 185.4516 (0.08) 176 1 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -Legend: - Outliers: 1 Standard Deviation from Mean; 1.5 IQR (InterQuartile Range) from 1st Quartile and 3rd Quartile. - OPS: Operations Per Second, computed as 1 / Mean -============================================================================================================== 7 passed in 8.21s =============================================================================================================== +collected 5 items + +... +... + +------------------------------------------------------------------------------------------------------------- benchmark: 4 tests ------------------------------------------------------------------------------------------------------------ +Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +test_global_snapshot[x_fns2-True-fastseq no snapshot] 118.6619 (1.0) 124.2569 (1.0) 122.1113 (1.0) 1.7266 (1.0) 122.0048 (1.0) 1.8421 (1.0) 2;1 8.1892 (1.0) 8 1 +test_global_snapshot[x_fns0-False-fastseq take and restore snapshot] 123.2069 (1.04) 131.6260 (1.06) 126.0777 (1.03) 3.4155 (1.98) 124.7658 (1.02) 5.4766 (2.97) 2;0 7.9316 (0.97) 8 1 +test_global_snapshot[x_fns1-False-slowseq take and restore snapshot] 448.7344 (3.78) 490.6087 (3.95) 466.8714 (3.82) 18.1452 (10.51) 456.7645 (3.74) 29.2119 (15.86) 1;0 2.1419 (0.26) 5 1 +test_global_snapshot[x_fns3-True-slowseq no snapshot] 453.6979 (3.82) 516.7671 (4.16) 473.7815 (3.88) 25.0584 (14.51) 463.8226 (3.80) 25.1137 (13.63) 1;0 2.1107 (0.26) 5 1 +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` +* Results from the current branch: + +``` text +------------------------------------------------------------------------------------------------------------ benchmark: 4 tests ------------------------------------------------------------------------------------------------------------ +Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +test_global_snapshot[x_fns2-True-fastseq no snapshot] 95.1790 (1.0) 101.4989 (1.0) 97.1787 (1.0) 2.3811 (1.0) 96.0393 (1.0) 2.9408 (1.0) 2;0 10.2903 (1.0) 10 1 +test_global_snapshot[x_fns0-False-fastseq take and restore snapshot] 101.7576 (1.07) 111.5388 (1.10) 106.1005 (1.09) 2.9359 (1.23) 106.2893 (1.11) 4.1448 (1.41) 3;0 9.4250 (0.92) 10 1 +test_global_snapshot[x_fns1-False-slowseq take and restore snapshot] 315.4810 (3.31) 337.3184 (3.32) 327.1796 (3.37) 8.0853 (3.40) 329.3743 (3.43) 9.8087 (3.34) 2;0 3.0564 (0.30) 5 1 +test_global_snapshot[x_fns3-True-slowseq no snapshot] 315.5748 (3.32) 328.8318 (3.24) 321.7635 (3.31) 5.0368 (2.12) 321.1631 (3.34) 7.1114 (2.42) 2;0 3.1079 (0.30) 5 1 +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +``` + + ## Changes ### 1.0.0 diff --git a/tests/test_fork.py b/tests/test_fork.py index f3f9e78..8de27ed 100644 --- a/tests/test_fork.py +++ b/tests/test_fork.py @@ -14,7 +14,8 @@ def tprint(*args): class TestTinyEVM(unittest.TestCase): @classmethod def setUpClass(cls): - tinyevm.enable_tracing() + # tinyevm.enable_tracing() + pass def test_get_balance_from_fork(self): fork_url = "https://eth.llamarpc.com" diff --git a/tests/test_global_snapshot.py b/tests/test_global_snapshot.py index 7f6b73d..8ce3b4d 100644 --- a/tests/test_global_snapshot.py +++ b/tests/test_global_snapshot.py @@ -138,10 +138,10 @@ def run_global_snapshot(keep_snapshot: bool, x_fns: list, disable_snapshot=False x_fns_slowseq = [('slow_seq()', None), ] * 100 @pytest.mark.parametrize("x_fns, disable_snapshot, name", [ - (x_fns_fastseq, False, "fastseq take and restore snapshot"), - (x_fns_slowseq, False, "slowseq take and restore snapshot"), - (x_fns_fastseq, True, "fastseq no snapshot"), - (x_fns_slowseq, True, "slowseq no snapshot"), + (x_fns_fastseq, False, "fastseq, plus take and restore snapshot"), + (x_fns_slowseq, False, "slowseq, plus take and restore snapshot"), + (x_fns_fastseq, True, "fastseq"), + (x_fns_slowseq, True, "slowseq"), ]) def test_global_snapshot(benchmark, x_fns, disable_snapshot, name): global tevm From 0ce9b806638e521ae2dc4a5a4d01d1750c7d0a88 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 13:49:08 +0800 Subject: [PATCH 37/40] add benchmark tests --- Cargo.toml | 19 ++++++++++ Makefile | 2 +- benches/complex.rs | 44 +++++++++++++++++++++ benches/deploy.rs | 62 ++++++++++++++++++++++++++++++ benches/function.rs | 82 ++++++++++++++++++++++++++++++++++++++++ benches/general.rs | 69 +++++++++++++++++++++++++++++++++ benches/infinite_loop.rs | 67 ++++++++++++++++++++++++++++++++ tests/revm_test.rs | 12 +++++- 8 files changed, 354 insertions(+), 3 deletions(-) create mode 100644 benches/complex.rs create mode 100644 benches/deploy.rs create mode 100644 benches/function.rs create mode 100644 benches/general.rs create mode 100644 benches/infinite_loop.rs diff --git a/Cargo.toml b/Cargo.toml index 2feda2a..9077022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,22 @@ criterion = {version="0.3.6", features=["html_reports"] } [features] default = [] redis=["dep:redis"] + +[[bench]] +name = "general" +harness = false + +[[bench]] +name = "deploy" +harness = false + +[[bench]] +name = "infinite_loop" +harness = false + +[[bench]] +name = "function" +harness = false + +[profile.bench] +debug = true \ No newline at end of file diff --git a/Makefile b/Makefile index 038c47c..06e62e7 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ build: test: cargo nextest run --no-fail-fast --success-output=never maturin develop --release - pytest -s --show-capture all + pytest -s bench: cargo bench clean: diff --git a/benches/complex.rs b/benches/complex.rs new file mode 100644 index 0000000..d5b10ad --- /dev/null +++ b/benches/complex.rs @@ -0,0 +1,44 @@ +use std::time::Duration; + +use criterion::{criterion_group, criterion_main, Criterion}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_complex_function(c: &mut Criterion) { + c.bench_function("call_complex_function", |b| { + let source = include_str!("../tests/contracts/complex_contract.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = tinyevm::TinyEVM::default(); + let owner = OWNER; + let deploy_to_address = Some(DEPLOY_TO_ADDRESS); + + let resp = { + exe.deploy_helper(owner, bytecode, UZERO, None, deploy_to_address) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "complexFunction()"; + b.iter(|| { + let data = hex::decode(fn_sig_to_prefix(fn_sig)).unwrap(); + + let r = exe.contract_call_helper(address, owner, data, UZERO, None); + // assert!(r.success); // this function can revert sometimes + assert!(r.gas_usage > 0); + }) + }); +} + +criterion_group!( + name = complex; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = bench_call_complex_function, +); + +criterion_main!(complex); diff --git a/benches/deploy.rs b/benches/deploy.rs new file mode 100644 index 0000000..45a6177 --- /dev/null +++ b/benches/deploy.rs @@ -0,0 +1,62 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::H256; +use revm::primitives::Address; +use ruint::aliases::U256; +use tinyevm::{TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +// Reusing can be slower because there are more data inside the instrumentation log +fn bench_contract_deterministic_deploy(c: &mut Criterion) { + c.bench_function("deploy_contract_deterministic", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let source = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + + b.iter(|| { + { + exe.deploy_helper(OWNER, source.clone(), UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap(); + }; + }) + }); +} + +fn bench_contract_deploy_on_different_executors(c: &mut Criterion) { + c.bench_function("deploy_contract_deploy_on_different_executors", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let source = hex::decode(source).unwrap(); + + b.iter(|| { + let mut exe = TinyEVM::default(); + { + exe.deploy_helper( + OWNER, + source.clone(), + U256::from(0), + None, + Some(DEPLOY_TO_ADDRESS), + ) + .unwrap(); + }; + }) + }); +} + +#[allow(unused)] +fn bench_random_h256(c: &mut Criterion) { + c.bench_function("call H256 random", |b| { + b.iter(|| { + let _ = H256::random(); + }) + }); +} + +criterion_group!( + name = deploy; + config = Criterion::default(); + targets = bench_random_h256, bench_contract_deterministic_deploy, bench_contract_deploy_on_different_executors +); + +criterion_main!(deploy); diff --git a/benches/function.rs b/benches/function.rs new file mode 100644 index 0000000..8f10a24 --- /dev/null +++ b/benches/function.rs @@ -0,0 +1,82 @@ +use std::{iter::repeat_with, time::Duration}; + +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::H256; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_function_returning_large_string(c: &mut Criterion) { + c.bench_function("call_function_returning_large_string", |b| { + let source = include_str!("../tests/contracts/VeLogo.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "tokenURI(uint256,uint256,uint256,uint256)"; + b.iter(|| { + let fn_args_hex: String = repeat_with(H256::random).take(4).map(hex::encode).collect(); + + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + + let r = exe.contract_call_helper(address, OWNER, data, UZERO, None); + assert!(r.success); + }) + }); +} + +#[allow(unused)] +// TODO this repeats most part of the previous test function, refactor +fn bench_call_function_returning_large_string_no_instrumentation(c: &mut Criterion) { + c.bench_function( + "call_function_returning_large_string_no_instrumetation", + |b| { + let source = include_str!("../tests/contracts/VeLogo.hex"); + let bytecode = hex::decode(source).unwrap(); + let mut exe = TinyEVM::default(); + exe.instrument_config_mut().enabled = false; + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + let fn_sig = "tokenURI(uint256,uint256,uint256,uint256)"; + b.iter(|| { + let fn_args_hex: String = + repeat_with(H256::random).take(4).map(hex::encode).collect(); + + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + + let r = exe.contract_call_helper(address, OWNER, data, UZERO, None); + assert!(r.success); + }) + }, + ); +} + +criterion_group!( + name = evm_benches; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = bench_call_function_returning_large_string, + bench_call_function_returning_large_string_no_instrumentation, +); + +criterion_main!(evm_benches); diff --git a/benches/general.rs b/benches/general.rs new file mode 100644 index 0000000..34c15c4 --- /dev/null +++ b/benches/general.rs @@ -0,0 +1,69 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +#[allow(unused)] +fn bench_call_tracing_with_shared_executor(c: &mut Criterion) { + c.bench_function("call_tracing_with_shared_executor", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test_call_success_success_failed()"; + let fn_args_hex = ""; + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +#[allow(unused)] +fn bench_call_tracing_with_different_executor(c: &mut Criterion) { + c.bench_function("call_tracing_with_different_executor", |b| { + let source = include_str!("../tests/contracts/calls_trace.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test_call_success_success_failed()"; + let fn_args_hex = ""; + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + b.iter(|| { + let mut exe = TinyEVM::default(); + let resp = exe + .deploy_helper( + OWNER, + bytecode.clone(), + UZERO, + None, + Some(DEPLOY_TO_ADDRESS), + ) + .unwrap(); + + let address = Address::from_slice(&resp.data); + + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +criterion_group!( + name = evm_benches; + config = Criterion::default(); + targets = bench_call_tracing_with_shared_executor, bench_call_tracing_with_different_executor +); + +criterion_main!(evm_benches); diff --git a/benches/infinite_loop.rs b/benches/infinite_loop.rs new file mode 100644 index 0000000..4538866 --- /dev/null +++ b/benches/infinite_loop.rs @@ -0,0 +1,67 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use primitive_types::{H160, U256}; +use revm::primitives::Address; +use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; + +const OWNER: Address = Address::repeat_byte(0x01); +const DEPLOY_TO_ADDRESS: Address = Address::repeat_byte(0x02); + +// ~100ms per loop +fn bench_infinite_loop_math(c: &mut Criterion) { + c.bench_function("infinite_loop_with_simple_math", |b| { + let source = include_str!("../tests/contracts/infinite_loop_Test2.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test1(int256)"; + let fn_args_hex = format!("{:0>64x}", U256::from(0)); + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +// <300ms per loop +fn bench_infinite_loop_adderss_call(c: &mut Criterion) { + c.bench_function("infinite_loop_with_address_call", |b| { + let source = include_str!("../tests/contracts/infinite_loop_Test.hex"); + let bytecode = hex::decode(source).unwrap(); + let fn_sig = "test1(int256)"; + let fn_args_hex = format!("{:0>64x}", U256::from(0)); + let add_hex = format!("{}{}", fn_sig_to_prefix(fn_sig), fn_args_hex); + + let data = hex::decode(add_hex).unwrap(); + let mut exe = TinyEVM::default(); + + let resp = { + exe.deploy_helper(OWNER, bytecode, UZERO, None, Some(DEPLOY_TO_ADDRESS)) + .unwrap() + }; + + assert!(resp.success, "Contract deploy should succeed."); + let address = Address::from_slice(&resp.data); + + b.iter(|| { + let _ = exe.contract_call_helper(address, OWNER, data.clone(), UZERO, None); + }) + }); +} + +criterion_group!( + name = infinite_loop; + config = Criterion::default(); + targets = bench_infinite_loop_math, bench_infinite_loop_adderss_call +); + +criterion_main!(infinite_loop); diff --git a/tests/revm_test.rs b/tests/revm_test.rs index 00f4e90..7e6579a 100644 --- a/tests/revm_test.rs +++ b/tests/revm_test.rs @@ -381,13 +381,21 @@ fn test_deterministic_deploy() { .deploy_helper(*OWNER, contract_deploy_bin.clone(), UZERO, None, None) .unwrap(); - assert!(c1.success, "Deploy use salt 1 should succeed: {:?}", &c1); + assert!( + c1.success, + "Deploy by initial nonce should succeed: {:?}", + &c1 + ); let c2 = vm .deploy_helper(*OWNER, contract_deploy_bin, UZERO, None, None) .unwrap(); - assert!(c2.success, "Deploy use salt 2 should succeed: {:?}", &c2); + assert!( + c2.success, + "Deploy by auto updated nonce should succeed: {:?}", + &c2 + ); assert_ne!(c1.data, c2.data, "Address of c1 and c2 should not equal"); } From 59dd3836e5bf1da1f81bc3f349fd556b5bf342af Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 14:54:45 +0800 Subject: [PATCH 38/40] restore salt and enabled parameters --- README.md | 9 --------- benches/infinite_loop.rs | 2 +- src/lib.rs | 33 ++++++++++++++++++++++++--------- tests/test_account_snapshot.py | 3 ++- tests/test_global_snapshot.py | 3 ++- tests/test_multithreading.py | 9 ++++----- tests/test_tinyevm.py | 7 ++++--- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 5be3e40..877baf3 100644 --- a/README.md +++ b/README.md @@ -199,12 +199,3 @@ test_global_snapshot[x_fns1-False-slowseq take and restore snapshot] 315.481 test_global_snapshot[x_fns3-True-slowseq no snapshot] 315.5748 (3.32) 328.8318 (3.24) 321.7635 (3.31) 5.0368 (2.12) 321.1631 (3.34) 7.1114 (2.42) 2;0 3.1079 (0.30) 5 1 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` - - -## Changes - -### 1.0.0 - -Breaking changes: -- `deterministic_deploy` signature is now changed to `deterministic_deploy(contract_deploy_code, owner=None, data=None, value=None, init_value=None, deploy_to_address=None)`, i.e., `salt` is removed. [Related to [REVM-1182](https://github.com/bluealloy/revm/issues/1182)]. If you want to similate the previous behaviour, it's suggested to use the `deploy_to_address` parameter to specify the address where the contract will be deployed. -- `enabled` in `REVMConfig` is removed. To disable all instrumentation, use diff --git a/benches/infinite_loop.rs b/benches/infinite_loop.rs index 4538866..68d4f37 100644 --- a/benches/infinite_loop.rs +++ b/benches/infinite_loop.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use primitive_types::{H160, U256}; +use primitive_types::U256; use revm::primitives::Address; use tinyevm::{fn_sig_to_prefix, TinyEVM, UZERO}; diff --git a/src/lib.rs b/src/lib.rs index d35c08a..9b9135f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ use lazy_static::lazy_static; use num_bigint::BigInt; use pyo3::prelude::*; use response::{Response, SeenPcsMap, WrappedBug, WrappedHeuristics, WrappedMissedBranch}; -use revm::inspector_handle_register; +use revm::{inspector_handle_register, primitives::B256}; use thread_local::ThreadLocal; use tokio::runtime::Runtime; use uuid::Uuid; @@ -39,7 +39,7 @@ pub mod instrument; /// Provide response data structure from EVM pub mod response; pub use common::*; -use hex::ToHex; +use hex::{FromHex, ToHex}; use instrument::{ bug_inspector::BugInspector, log_inspector::LogInspector, BugData, Heuristics, InstrumentConfig, }; @@ -705,19 +705,20 @@ impl TinyEVM { /// - `owner`: Owner address as a 20-byte array encoded as hex string /// - `data`: (Optional, default empty) Constructor arguments encoded as hex string. /// - `value`: (Optional, default 0) a U256. Set the value to be included in the contract creation transaction. - /// - `deploy_to_address`: when provided, change the address of the deployed contract to this address + /// - `deploy_to_address`: when provided, change the address of the deployed contract to this address, otherwise deploy to a an address created using `owner.CREATE2(a_fixed_salt, codehash)`. /// - This requires the constructor to be payable. /// - The transaction sender (owner) must have enough balance /// - `init_value`: (Optional) BigInt. Override the initial balance of the contract to this value. /// /// Returns a list consisting of 4 items `[reason, address-as-byte-array, bug_data, heuristics]` - #[pyo3(signature = (contract_deploy_code, owner=None, data=None, value=None, init_value=None, deploy_to_address=None))] + #[pyo3(signature = (contract_deploy_code, salt=None, owner=None, data=None, value=None, init_value=None, deploy_to_address=None))] pub fn deterministic_deploy( &mut self, contract_deploy_code: String, // variable length - owner: Option, // h160 as hex string - data: Option, // variable length + salt: Option, // h256 as hex string, has no effect if deploy_to_address is provided + owner: Option, // h160 as hex string + data: Option, // variable length value: Option, init_value: Option, deploy_to_address: Option, @@ -742,9 +743,23 @@ impl TinyEVM { let value = value.unwrap_or_default(); let mut contract_bytecode = contract_deploy_code.to_vec(); contract_bytecode.extend(data); - let force_address: Option
= deploy_to_address + + let salt = { + if let Some(salt) = salt { + let salt = &salt; + B256::from_hex(salt)? + } else { + B256::ZERO + } + }; + + let force_address: Address = deploy_to_address .map(|s| Address::from_str(&s)) - .transpose()?; + .transpose()? + .unwrap_or_else(|| { + let codehash = keccak256(&contract_bytecode); + owner.create2(salt, codehash) + }); let resp = { let resp = self.deploy_helper( @@ -752,7 +767,7 @@ impl TinyEVM { contract_bytecode, bigint_to_ruint_u256(&value)?, None, - force_address, + Some(force_address), )?; if let Some(balance) = init_value { diff --git a/tests/test_account_snapshot.py b/tests/test_account_snapshot.py index 6083e92..5e9b303 100644 --- a/tests/test_account_snapshot.py +++ b/tests/test_account_snapshot.py @@ -65,12 +65,13 @@ def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297 data = encode(['uint256', 'string', 'string', 'uint256'], [_initialSupply, _name, _symbol, _decimals]).hex() + salt = None value = None init_value = 0x223312323 tevm.set_balance(owner, 0xffff0000000000000000000000000000000000000000000000000000000000ff) - resp = tevm.deterministic_deploy(binary, owner, data, value, init_value) + resp = tevm.deterministic_deploy(binary, salt, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success diff --git a/tests/test_global_snapshot.py b/tests/test_global_snapshot.py index 8ce3b4d..07ecb0d 100644 --- a/tests/test_global_snapshot.py +++ b/tests/test_global_snapshot.py @@ -70,10 +70,11 @@ def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297 tevm.set_balance(owner, 0xffff0000000000000000000000000000000000000000000000000000000000ff) + salt = None data = '' value = None init_value = None - resp = tevm.deterministic_deploy(binary, owner, data, value, init_value) + resp = tevm.deterministic_deploy(binary, salt, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success diff --git a/tests/test_multithreading.py b/tests/test_multithreading.py index 5886ce7..33817c4 100644 --- a/tests/test_multithreading.py +++ b/tests/test_multithreading.py @@ -7,6 +7,8 @@ from concurrent.futures import ProcessPoolExecutor +salt = None + def handle_exception(exc_type, exc_value, exc_traceback): if issubclass(exc_type, KeyboardInterrupt): sys.__excepthook__(exc_type, exc_value, exc_traceback) @@ -33,7 +35,6 @@ def run_test(): tevm = tinyevm.TinyEVM() contract_bytecode = open('tests/contracts/C.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None @@ -43,8 +44,7 @@ def run_test(): balance = tevm.get_balance(owner) tprint('balance before deployment: {}'.format(balance)) - # todo update response object - resp = tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) + resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success @@ -75,7 +75,6 @@ def run_infinite_loop(): tevm = tinyevm.TinyEVM() contract_bytecode = open('tests/contracts/infinite_loop_Test.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None @@ -86,7 +85,7 @@ def run_infinite_loop(): tprint('balance before deployment: {}'.format(balance)) # todo update response object - resp = tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) + resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) assert resp.success diff --git a/tests/test_tinyevm.py b/tests/test_tinyevm.py index 6609172..268824f 100644 --- a/tests/test_tinyevm.py +++ b/tests/test_tinyevm.py @@ -2,6 +2,8 @@ import unittest from Crypto.Hash import keccak +salt = None + def fn_sig(sig): k = keccak.new(digest_bits=256) k.update(sig.encode()) @@ -56,7 +58,7 @@ def test_get_change_tx_gas_limit(self): init_value = 0x223312323 try: - tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) + tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) except RuntimeError as e: tprint('Expected error: {}'.format(e)) assert 'OutOfGas' in str(e), 'should raise out of gas error' @@ -79,7 +81,6 @@ def test_deployment(self): tevm = tinyevm.TinyEVM() contract_bytecode = open('tests/contracts/C.hex').read() - salt = None owner = '0x388C818CA8B9251b393131C08a736A67ccB19297' data = None value = None @@ -90,7 +91,7 @@ def test_deployment(self): tprint('balance before deployment: {}'.format(balance)) # todo update response object - resp = tevm.deterministic_deploy(contract_bytecode, owner, data, value, init_value) + resp = tevm.deterministic_deploy(contract_bytecode, salt, owner, data, value, init_value) tprint('Deployment resp: {}'.format(resp)) bugs = list(resp.bug_data) From 70721487a51078ee3d31dace6145248547a0af64 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 15:42:25 +0800 Subject: [PATCH 39/40] allow too many args for use in python --- src/lib.rs | 55 +++++++++++++++++++--------------- tests/test_account_snapshot.py | 1 - 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9b9135f..75fa42c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,10 @@ use lazy_static::lazy_static; use num_bigint::BigInt; use pyo3::prelude::*; use response::{Response, SeenPcsMap, WrappedBug, WrappedHeuristics, WrappedMissedBranch}; -use revm::{inspector_handle_register, primitives::B256}; +use revm::{ + inspector_handle_register, + primitives::{TxEnv, B256}, +}; use thread_local::ThreadLocal; use tokio::runtime::Runtime; use uuid::Uuid; @@ -123,6 +126,14 @@ pub fn enable_tracing() -> Result<()> { // Implementations for use in Rust impl TinyEVM { + pub fn exe_mut(&mut self) -> &mut Evm<'static, ChainInspector, TinyEvmDb> { + self.exe.as_mut().unwrap() + } + + pub fn tx_mut(&mut self) -> &mut TxEnv { + self.exe_mut().tx_mut() + } + fn db(&self) -> &ForkDB { &self.exe.as_ref().unwrap().context.evm.db } @@ -299,13 +310,6 @@ impl TinyEVM { } let result = self.exe.as_mut().unwrap().transact_commit(); - // debug!("db {:?}", self.exe.as_ref().unwrap().db()); - // debug!("sender {:?}", owner.encode_hex::(),); - - // // todo_cl temp check - // self.db = self.exe.as_ref().unwrap().db().clone(); - // self.env = self.exe.as_ref().unwrap().context.evm.env.as_ref().clone(); - trace!("deploy result: {:?}", result); let collision = { @@ -376,18 +380,16 @@ impl TinyEVM { CALL_DEPTH.get_or_default().set(0); { - let tx = self.exe.as_mut().unwrap().tx_mut(); + let tx_gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); + let tx = self.tx_mut(); tx.caller = sender; tx.transact_to = TransactTo::Call(contract); tx.data = data.into(); tx.value = value; - tx.gas_limit = tx_gas_limit.unwrap_or(self.tx_gas_limit); + tx.gas_limit = tx_gas_limit; } - let result = { - let exe = self.exe.as_mut().unwrap(); - exe.transact_commit() - }; + let result = self.exe_mut().transact_commit(); let addresses = self.created_addresses().clone(); info!( @@ -407,7 +409,7 @@ impl TinyEVM { let heuristics = self.heuristics().clone(); let seen_pcs = self.pcs_by_address().clone(); - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let ignored_addresses = db.ignored_addresses.clone(); let ignored_addresses = ignored_addresses.into_iter().map(Into::into).collect(); @@ -429,7 +431,7 @@ impl TinyEVM { /// Set code of an account pub fn set_code_by_address(&mut self, addr: Address, code: Vec) -> Result<()> { - let db = &mut self.exe.as_mut().unwrap().context.evm.db; + let db = &mut self.db_mut(); let code = Bytecode::new_raw(code.into()); let accounts = &db.accounts; @@ -465,7 +467,7 @@ impl TinyEVM { /// Get code from an address pub fn get_code_by_address(&self, addr: Address) -> Result> { - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let accounts = &db.accounts; let account = accounts.get(&addr); if let Some(account) = account { @@ -480,7 +482,7 @@ impl TinyEVM { /// Get Eth balance for an account pub fn get_eth_balance(&self, addr: Address) -> Result { - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let accounts = &db.accounts; if let Some(account) = accounts.get(&addr) { Ok(account.info.balance) @@ -491,7 +493,7 @@ impl TinyEVM { /// Get storage by address and index pub fn get_storage_by_address(&self, addr: Address, index: U256) -> Result { - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = &self.db(); let accounts = &db.accounts; let account = accounts .get(&addr) @@ -509,7 +511,7 @@ impl TinyEVM { index: U256, value: U256, ) -> Result<()> { - let db = &mut self.exe.as_mut().unwrap().context.evm.db; + let db = self.db_mut(); db.insert_account_storage(addr, index, value)?; Ok(()) } @@ -713,6 +715,7 @@ impl TinyEVM { /// /// Returns a list consisting of 4 items `[reason, address-as-byte-array, bug_data, heuristics]` #[pyo3(signature = (contract_deploy_code, salt=None, owner=None, data=None, value=None, init_value=None, deploy_to_address=None))] + #[allow(clippy::too_many_arguments)] pub fn deterministic_deploy( &mut self, contract_deploy_code: String, // variable length @@ -1084,7 +1087,7 @@ impl TinyEVM { /// Take a snapshot of an account, raise error if account does not exist in db pub fn take_snapshot(&mut self, address: String) -> Result<()> { let addr = Address::from_str(&address)?; - let db = &self.exe.as_ref().unwrap().context.evm.db; + let db = self.db(); if let Some(account) = db.accounts.get(&addr) { self.snapshots.insert(addr, account.clone()); Ok(()) @@ -1121,10 +1124,14 @@ impl TinyEVM { /// Restore a snapshot for an account, raise error if there is no snapshot for the account pub fn restore_snapshot(&mut self, address: String) -> Result<()> { let addr = Address::from_str(&address)?; - let db = &mut self.exe.as_mut().unwrap().context.evm.db; - let account = self.snapshots.get(&addr).context("No snapshot found")?; - db.accounts.insert(addr, account.clone()); + let account = { + self.snapshots + .get(&addr) + .context("No snapshot found")? + .clone() + }; + self.db_mut().accounts.insert(addr, account); Ok(()) } diff --git a/tests/test_account_snapshot.py b/tests/test_account_snapshot.py index 5e9b303..a8ac7e6 100644 --- a/tests/test_account_snapshot.py +++ b/tests/test_account_snapshot.py @@ -65,7 +65,6 @@ def deploy_contract(salt=None, owner='0x388C818CA8B9251b393131C08a736A67ccB19297 data = encode(['uint256', 'string', 'string', 'uint256'], [_initialSupply, _name, _symbol, _decimals]).hex() - salt = None value = None init_value = 0x223312323 From daff0026a8363a44e8e3e02500a1b22c7b4a00b3 Mon Sep 17 00:00:00 2001 From: chen Date: Wed, 17 Jul 2024 17:23:16 +0800 Subject: [PATCH 40/40] clean and avoid a bit copying --- src/chain_inspector.rs | 20 -------------------- src/common.rs | 6 +----- src/lib.rs | 4 +++- tests/test_account_snapshot.py | 1 - 4 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/chain_inspector.rs b/src/chain_inspector.rs index 1e99929..5627a76 100644 --- a/src/chain_inspector.rs +++ b/src/chain_inspector.rs @@ -110,24 +110,4 @@ impl Inspector for ChainInspector { } outcome } - - // fn eofcreate( - // &mut self, - // context: &mut EvmContext, - // inputs: &mut EOFCreateInputs, - // ) -> Option { - // None - // } - - // fn eofcreate_end( - // &mut self, - // context: &mut EvmContext, - // inputs: &EOFCreateInputs, - // outcome: CreateOutcome, - // ) -> CreateOutcome { - // outcome - // } - - // #[inline] - // fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {} } diff --git a/src/common.rs b/src/common.rs index 1dd1b98..0a6bf4b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -60,16 +60,12 @@ pub fn bigint_to_ruint_u256(b: &BigInt) -> Result { return Err(eyre::eyre!("BigInt is negative")); } - let mut padded_bytes = [0u8; 32]; let bytes_len = bytes.len(); if bytes_len > 32 { return Err(eyre::eyre!("BigInt is too large")); } - // Copy bytes into the end of the padded array - padded_bytes[(32 - bytes_len)..].copy_from_slice(&bytes); - - Ok(U256::from_be_slice(&padded_bytes)) + Ok(U256::from_be_slice(&bytes)) } /// Returns the distance between two U256 numbers diff --git a/src/lib.rs b/src/lib.rs index 75fa42c..46e0a3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -426,7 +426,7 @@ impl TinyEVM { transient_logs: logs, ignored_addresses, }; - revm_result.into() + Response::from(revm_result) } /// Set code of an account @@ -1119,6 +1119,8 @@ impl TinyEVM { bug_inspector.bug_data.clear(); bug_inspector.created_addresses.clear(); bug_inspector.heuristics = Default::default(); + self.log_inspector_mut().traces.clear(); + self.log_inspector_mut().logs.clear(); } /// Restore a snapshot for an account, raise error if there is no snapshot for the account diff --git a/tests/test_account_snapshot.py b/tests/test_account_snapshot.py index a8ac7e6..0d6533d 100644 --- a/tests/test_account_snapshot.py +++ b/tests/test_account_snapshot.py @@ -123,7 +123,6 @@ def reset_contract_call(contract, owner): resp = tevm.contract_call(contract, owner, data_balance_check, None) assert _initialSupply - 1000 == int.from_bytes(bytes(resp.data), 'big') - random_address = '0x253397db4016dE1983D29f7DEc2901c54dB81A22' tevm.copy_snapshot(contract, random_address)