From dac8a29e7e9dab93c8b071d95c0721863e52d89a Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sat, 25 Sep 2021 18:42:44 +0200 Subject: [PATCH] Add boolean opcodes & other - Add boolean opcodes - Add jump_dest validity check - Add tests for opcode exceptions - Update calldataload opcode logic - Add stack.peek() --- Cargo.toml | 3 +- src/lib.rs | 27 ++++++++++++++++-- src/opcodes.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++++-- tests/execute.rs | 22 ++++++++++++++- 4 files changed, 119 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e90c8f..4b50760 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ license = "GPL-3.0" repository = "https://github.com/arnaucube/evm-rs" [dependencies] -num-bigint = "0.4" num = "0.4.0" +num-traits = "0.2.14" +num-bigint = "0.4" hex = "0.4.3" diff --git a/src/lib.rs b/src/lib.rs index bff3e0d..7d6a79f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use num_bigint::BigUint; use std::collections::HashMap; pub mod opcodes; pub mod u256; @@ -82,6 +81,12 @@ impl Stack { None => Err("pop err".to_string()), // WIP } } + pub fn peek(&mut self) -> Result<[u8; 32], String> { + if self.stack.is_empty() { + return Err("peek err".to_string()); + } + Ok(self.stack[self.stack.len() - 1]) + } pub fn substract_gas(&mut self, val: u64) -> Result<(), String> { if self.gas < val { return Err("out of gas".to_string()); @@ -142,9 +147,27 @@ impl Stack { } self.pc += 1; } + 0x10 => { + // arithmetic + match opcode { + 0x10 => self.lt()?, + 0x11 => self.gt()?, + // 0x12 => self.slt()?, + // 0x13 => self.sgt()?, + 0x14 => self.eq()?, + 0x15 => self.is_zero()?, + 0x16 => self.and()?, + 0x17 => self.or()?, + 0x18 => self.xor()?, + 0x19 => self.not()?, + // 0x1a => self.byte()?, + _ => return Err(format!("unimplemented {:x}", opcode)), + } + self.pc += 1; + } 0x30 => { match opcode { - 0x35 => self.calldata_load(&calldata), + 0x35 => self.calldata_load(&calldata)?, 0x36 => self.calldata_size(&calldata), 0x39 => self.code_copy(&code)?, _ => return Err(format!("unimplemented {:x}", opcode)), diff --git a/src/opcodes.rs b/src/opcodes.rs index 7c9ce94..a2764e1 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -1,4 +1,6 @@ use super::*; +use num_bigint::BigUint; +use num_traits::identities::Zero; // Non-opcode gas prices const GDEFAULT: usize = 1; @@ -8,6 +10,7 @@ const GSTORAGEREFUND: usize = 15000; const GSTORAGEKILL: usize = 5000; const GSTORAGEMOD: usize = 5000; const GSTORAGEADD: usize = 20000; + const GEXPONENTBYTE: usize = 10; // cost of EXP exponent per byte const EXP_SUPPLEMENTAL_GAS: usize = 40; const GCOPY: usize = 3; // cost to copy one 32 byte word @@ -244,12 +247,77 @@ impl Stack { } // boolean + pub fn lt(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + let b = BigUint::from_bytes_be(&self.pop()?[..]); + self.push(u256::usize_to_u256((a < b) as usize)); + Ok(()) + } + pub fn gt(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + let b = BigUint::from_bytes_be(&self.pop()?[..]); + self.push(u256::usize_to_u256((a > b) as usize)); + Ok(()) + } + pub fn eq(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + let b = BigUint::from_bytes_be(&self.pop()?[..]); + self.push(u256::usize_to_u256((a == b) as usize)); + Ok(()) + } + pub fn is_zero(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + self.push(u256::usize_to_u256(a.is_zero() as usize)); + Ok(()) + } + pub fn and(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + let b = BigUint::from_bytes_be(&self.pop()?[..]); + self.push_arbitrary(&(a & b).to_bytes_be()); + Ok(()) + } + pub fn or(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + let b = BigUint::from_bytes_be(&self.pop()?[..]); + self.push_arbitrary(&(a | b).to_bytes_be()); + Ok(()) + } + pub fn xor(&mut self) -> Result<(), String> { + let a = BigUint::from_bytes_be(&self.pop()?[..]); + let b = BigUint::from_bytes_be(&self.pop()?[..]); + self.push_arbitrary(&(a ^ b).to_bytes_be()); + Ok(()) + } + pub fn not(&mut self) -> Result<(), String> { + // 2**256-1 TODO this will not be here hardcoded, there will be a custom type uint256 + let f = "115792089237316195423570985008687907853269984665640564039457584007913129639935" + .parse::() + .unwrap(); + + let a = BigUint::from_bytes_be(&self.pop()?[..]); + self.push_arbitrary(&(f - a).to_bytes_be()); + Ok(()) + } + // crypto // contract context - pub fn calldata_load(&mut self, calldata: &[u8]) { - self.put_arbitrary(&calldata[self.calldata_i..self.calldata_i + self.calldata_size]); + pub fn calldata_load(&mut self, calldata: &[u8]) -> Result<(), String> { + let mut start = self.calldata_i; + if !self.stack.is_empty() { + start = u256::u256_to_u64(self.peek()?) as usize; + } + let l = calldata.len(); + if start > l { + start = l; + } + let mut end = start + self.calldata_size; + if end > l { + end = l; + } + self.put_arbitrary(&calldata[start..end]); self.calldata_i += self.calldata_size; + Ok(()) } pub fn calldata_size(&mut self, calldata: &[u8]) { self.calldata_size = calldata.len(); @@ -310,6 +378,7 @@ impl Stack { Ok(()) } pub fn sstore(&mut self) -> Result<(), String> { + // TODO WIP let key = self.pop()?; let value = self.pop()?; if self.storage.contains_key(&key) { diff --git a/tests/execute.rs b/tests/execute.rs index f690dc4..668ade4 100644 --- a/tests/execute.rs +++ b/tests/execute.rs @@ -215,9 +215,29 @@ fn execute_opcodes_9() { let mut s = Stack::new(); s.execute(&code, &calldata, false).unwrap(); - assert_eq!(s.gas, 9999974988); // assert_eq!(s.gas, 9999977788); // TODO WIP geth reported gas + assert_eq!(s.gas, 9999974988); assert_eq!(s.pc, 10); assert_eq!(s.stack.len(), 0); assert_eq!(s.storage.len(), 1); } + +#[test] +fn execute_opcodes_10() { + let code = hex::decode( + "606060405260e060020a6000350463a5f3c23b8114601a575b005b60243560043501600055601856", + ) + .unwrap(); + let calldata = hex::decode("a5f3c23b00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000004").unwrap(); + + println!("LEN {:?}", calldata.len()); + + let mut s = Stack::new(); + s.execute(&code, &calldata, true).unwrap(); + + // assert_eq!(s.gas, 9999977752); // WIP correct sstore gas computation + assert_eq!(s.gas, 9999979852); + assert_eq!(s.pc, 25); + assert_eq!(s.stack.len(), 1); + assert_eq!(s.storage.len(), 1); +}