From e4a3b10f48f102bf5c1e71c65e08c139e50c76ea Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sat, 11 Sep 2021 20:53:48 +0200 Subject: [PATCH] Add arithmetic opcodes implementation --- src/lib.rs | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 20de783..bb59a6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -// use num::pow::pow; use num_bigint::BigUint; use std::collections::HashMap; @@ -60,12 +59,173 @@ impl Stack { fn push(&mut self, b: [u8; 32]) { self.stack.push(b); } + // push_arbitrary performs a push, but first converting the arbitrary-length input into a 32 + // byte array + fn push_arbitrary(&mut self, b: &[u8]) { + // TODO if b.len()>32 return error + let mut d: [u8; 32] = [0; 32]; + d[32 - b.len()..].copy_from_slice(&b[..]); + self.stack.push(d); + } fn pop(&mut self) -> [u8; 32] { match self.stack.pop() { Some(x) => return x, None => panic!("err"), } } + fn execute(&mut self, code: &[u8], calldata: &[u8], debug: bool) -> Vec { + self.pc = 0; + self.calldata_i = 0; + let l = code.len(); + + while self.pc < l { + let opcode = code[self.pc]; + if !self.opcodes.contains_key(&opcode) { + panic!("invalid opcode {:x}", opcode); + } + + match opcode & 0xf0 { + 0x00 => { + // arithmetic + match opcode { + 0x00 => { + return Vec::new(); + } + 0x01 => self.add(), + 0x02 => self.mul(), + 0x03 => self.sub(), + 0x04 => self.div(), + 0x05 => self.sdiv(), + 0x06 => self.modulus(), + 0x07 => self.smod(), + 0x08 => self.add_mod(), + 0x09 => self.mul_mod(), + 0x0a => self.exp(), + // 0x0b => self.sign_extend(), + _ => panic!("unimplemented {:x}", opcode), + } + self.pc += 1; + } + 0x50 => { + self.pc += 1; + match opcode { + 0x52 => self.mstore(), + _ => panic!("unimplemented {:x}", opcode), + } + } + 0x60 | 0x70 => { + // push + let n = (opcode - 0x5f) as usize; + self.push_arbitrary(&code[self.pc + 1..self.pc + 1 + n]); + self.pc += 1 + n; + } + 0xf0 => { + if opcode == 0xf3 { + let pos_to_return = u256_to_u64(self.pop()) as usize; + let len_to_return = u256_to_u64(self.pop()) as usize; + return self.mem[pos_to_return..pos_to_return + len_to_return].to_vec(); + } + } + _ => { + panic!("unimplemented {:x}", opcode); + } + } + self.gas -= self.opcodes.get(&opcode).unwrap().gas; + } + return Vec::new(); + } + + // arithmetic + // TODO instead of [u8;32] converted to BigUint, use custom type uint256 that implements all + // the arithmetic + fn add(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + self.push_arbitrary(&(b0 + b1).to_bytes_be()); + } + fn mul(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + self.push_arbitrary(&(b0 * b1).to_bytes_be()); + } + fn sub(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + if b0 >= b1 { + self.push_arbitrary(&(b0 - b1).to_bytes_be()); + } else { + // 2**256 + let max = + "115792089237316195423570985008687907853269984665640564039457584007913129639936" + .parse::() + .unwrap(); + self.push_arbitrary(&(max + b0 - b1).to_bytes_be()); + } + } + fn div(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + self.push_arbitrary(&(b0 / b1).to_bytes_be()); + } + fn sdiv(&mut self) { + panic!("unimplemented"); + } + fn modulus(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + self.push_arbitrary(&(b0 % b1).to_bytes_be()); + } + fn smod(&mut self) { + panic!("unimplemented"); + } + fn add_mod(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + let b2 = BigUint::from_bytes_be(&self.pop()[..]); + self.push_arbitrary(&(b0 + b1 % b2).to_bytes_be()); + } + fn mul_mod(&mut self) { + let b0 = BigUint::from_bytes_be(&self.pop()[..]); + let b1 = BigUint::from_bytes_be(&self.pop()[..]); + let b2 = BigUint::from_bytes_be(&self.pop()[..]); + self.push_arbitrary(&(b0 * b1 % b2).to_bytes_be()); + } + fn exp(&mut self) { + panic!("unimplemented"); + // let b0 = BigUint::from_bytes_be(&self.pop()[..]); + // let b1 = BigUint::from_bytes_be(&self.pop()[..]); + // self.push_arbitrary(&(pow(b0, b1)).to_bytes_be()); + } + + // boolean + // crypto + + // contract context + fn calldata_load(&mut self, calldata: &[u8]) {} + + // blockchain context + + // storage and execution + fn extend_mem(&mut self, start: usize, size: usize) { + if size <= self.mem.len() || start + size <= self.mem.len() { + return; + } + let old_size = self.mem.len() / 32; + let new_size = (start + size) / 32; + let old_total_fee = old_size * GMEMORY + old_size.pow(2) / GQUADRATICMEMDENOM; + let new_total_fee = new_size * GMEMORY + new_size.pow(2) / GQUADRATICMEMDENOM; + let mem_fee = new_total_fee - old_total_fee; + self.gas -= mem_fee as u64; + let mut new_bytes: Vec = vec![0; size]; + self.mem.append(&mut new_bytes); + } + fn mstore(&mut self) { + let pos = u256_to_u64(self.pop()); + let val = self.pop(); + self.extend_mem(pos as usize, 32); + + self.mem[pos as usize..].copy_from_slice(&val); + } } fn u256_to_u64(a: [u8; 32]) -> u64 { @@ -214,4 +374,45 @@ mod tests { // assert_eq!(s.pop(), str_to_u256("1")); // assert_eq!(s.pop(), error); // TODO expect error as stack is empty } + + // arithmetic + #[test] + fn execute_opcodes_0() { + let code = hex::decode("6005600c01").unwrap(); // 5+12 + let calldata = vec![]; + + let mut s = Stack::new(); + s.execute(&code, &calldata, false); + assert_eq!(s.pop(), str_to_u256("17")); + assert_eq!(s.gas, 9999999991); + assert_eq!(s.pc, 5); + } + + #[test] + fn execute_opcodes_1() { + let code = hex::decode("60056004016000526001601ff3").unwrap(); + let calldata = vec![]; + + let mut s = Stack::new(); + let out = s.execute(&code, &calldata, false); + + assert_eq!(out[0], 0x09); + assert_eq!(s.gas, 9999999976); + assert_eq!(s.pc, 12); + // assert_eq!(s.pop(), err); // TODO expect error as stack is empty + } + + #[test] + fn execute_opcodes_2() { + let code = hex::decode("61010161010201").unwrap(); + let calldata = vec![]; + + let mut s = Stack::new(); + s.execute(&code, &calldata, false); + + // assert_eq!(out[0], 0x09); + assert_eq!(s.gas, 9999999991); + assert_eq!(s.pc, 7); + assert_eq!(s.pop(), str_to_u256("515")); + } }