Browse Source

Add arithmetic opcodes implementation

master
arnaucube 3 years ago
parent
commit
e4a3b10f48
1 changed files with 202 additions and 1 deletions
  1. +202
    -1
      src/lib.rs

+ 202
- 1
src/lib.rs

@ -1,6 +1,5 @@
#![allow(dead_code)] #![allow(dead_code)]
// use num::pow::pow;
use num_bigint::BigUint; use num_bigint::BigUint;
use std::collections::HashMap; use std::collections::HashMap;
@ -60,12 +59,173 @@ impl Stack {
fn push(&mut self, b: [u8; 32]) { fn push(&mut self, b: [u8; 32]) {
self.stack.push(b); 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] { fn pop(&mut self) -> [u8; 32] {
match self.stack.pop() { match self.stack.pop() {
Some(x) => return x, Some(x) => return x,
None => panic!("err"), None => panic!("err"),
} }
} }
fn execute(&mut self, code: &[u8], calldata: &[u8], debug: bool) -> Vec<u8> {
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::<BigUint>()
.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<u8> = 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 { 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(), str_to_u256("1"));
// assert_eq!(s.pop(), error); // TODO expect error as stack is empty // 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"));
}
} }

Loading…
Cancel
Save