Browse Source

Implement 0x36, 0x39, 0x55, 0x0a opcodes

Implement:
- 0x0a Exp opcode
- 0x36 calldata_size
- 0x39 code_copy
- 0x55 sstore
master
arnaucube 3 years ago
parent
commit
974406fc8e
5 changed files with 283 additions and 107 deletions
  1. +4
    -3
      Cargo.toml
  2. +2
    -0
      README.md
  3. +42
    -21
      src/lib.rs
  4. +137
    -61
      src/opcodes.rs
  5. +98
    -22
      tests/execute.rs

+ 4
- 3
Cargo.toml

@ -1,9 +1,10 @@
[package] [package]
name = "evm" name = "evm"
version = "0.1.0"
version = "0.0.1"
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
authors = ["arnaucube <root@arnaucube.com>"]
license = "GPL-3.0"
repository = "https://github.com/arnaucube/evm-rs"
[dependencies] [dependencies]
num-bigint = "0.4" num-bigint = "0.4"

+ 2
- 0
README.md

@ -2,3 +2,5 @@
EVM ([Ethereum Virtual Machine](https://ethereum.org/en/developers/docs/evm/)) implementation from scratch in Rust. EVM ([Ethereum Virtual Machine](https://ethereum.org/en/developers/docs/evm/)) implementation from scratch in Rust.
*This is a repo done to get familiar with the EVM, do not use.* *This is a repo done to get familiar with the EVM, do not use.*
Opcodes implemented: 83/130.

+ 42
- 21
src/lib.rs

@ -3,12 +3,15 @@
use num_bigint::BigUint; use num_bigint::BigUint;
use std::collections::HashMap; use std::collections::HashMap;
pub mod opcodes; pub mod opcodes;
pub mod u256;
#[derive(Default)] #[derive(Default)]
pub struct Stack { pub struct Stack {
pub pc: usize, pub pc: usize,
pub calldata_i: usize, pub calldata_i: usize,
pub calldata_size: usize,
pub stack: Vec<[u8; 32]>, pub stack: Vec<[u8; 32]>,
pub storage: HashMap<[u8; 32], Vec<u8>>,
pub mem: Vec<u8>, pub mem: Vec<u8>,
pub gas: u64, pub gas: u64,
pub opcodes: HashMap<u8, opcodes::Opcode>, pub opcodes: HashMap<u8, opcodes::Opcode>,
@ -19,7 +22,9 @@ impl Stack {
let mut s = Stack { let mut s = Stack {
pc: 0, pc: 0,
calldata_i: 0, calldata_i: 0,
calldata_size: 32,
stack: Vec::new(), stack: Vec::new(),
storage: HashMap::new(),
mem: Vec::new(), mem: Vec::new(),
gas: 10000000000, gas: 10000000000,
opcodes: HashMap::new(), opcodes: HashMap::new(),
@ -28,8 +33,28 @@ impl Stack {
s s
} }
pub fn print_stack(&self) { pub fn print_stack(&self) {
println!("stack ({}):", self.stack.len());
for i in (0..self.stack.len()).rev() { for i in (0..self.stack.len()).rev() {
println!("{:x?}", &self.stack[i][28..]);
// println!("{:x}", &self.stack[i][28..]);
println!("{:?}", vec_u8_to_hex(self.stack[i].to_vec()));
}
}
pub fn print_memory(&self) {
if self.mem.len() > 0 {
println!("memory ({}):", self.mem.len());
println!("{:?}", vec_u8_to_hex(self.mem.to_vec()));
}
}
pub fn print_storage(&self) {
if self.storage.len() > 0 {
println!("storage ({}):", self.storage.len());
for (key, value) in self.storage.iter() {
println!(
"{:?}: {:?}",
vec_u8_to_hex(key.to_vec()),
vec_u8_to_hex(value.to_vec())
);
}
} }
} }
pub fn push(&mut self, b: [u8; 32]) { pub fn push(&mut self, b: [u8; 32]) {
@ -47,14 +72,14 @@ impl Stack {
pub fn put_arbitrary(&mut self, b: &[u8]) { pub fn put_arbitrary(&mut self, b: &[u8]) {
// TODO if b.len()>32 return error // TODO if b.len()>32 return error
let mut d: [u8; 32] = [0; 32]; let mut d: [u8; 32] = [0; 32];
d[32 - b.len()..].copy_from_slice(b);
d[0..b.len()].copy_from_slice(b); // put without left padding
let l = self.stack.len(); let l = self.stack.len();
self.stack[l - 1] = d; self.stack[l - 1] = d;
} }
pub fn pop(&mut self) -> [u8; 32] { pub fn pop(&mut self) -> [u8; 32] {
match self.stack.pop() { match self.stack.pop() {
Some(x) => x, Some(x) => x,
None => panic!("err"),
None => panic!("pop err"),
} }
} }
@ -71,13 +96,15 @@ impl Stack {
if debug { if debug {
println!( println!(
"{:?} (0x{:x}): pc={:?} gas={:?}\nstack:",
"{} (0x{:x}): pc={:?} gas={:?}",
self.opcodes.get(&opcode).unwrap().name, self.opcodes.get(&opcode).unwrap().name,
opcode, opcode,
self.pc, self.pc,
self.gas, self.gas,
); );
self.print_stack(); self.print_stack();
self.print_memory();
self.print_storage();
println!(); println!();
} }
match opcode & 0xf0 { match opcode & 0xf0 {
@ -85,6 +112,7 @@ impl Stack {
// arithmetic // arithmetic
match opcode { match opcode {
0x00 => { 0x00 => {
println!("0x00: STOP");
return Vec::new(); return Vec::new();
} }
0x01 => self.add(), 0x01 => self.add(),
@ -104,9 +132,9 @@ impl Stack {
} }
0x30 => { 0x30 => {
match opcode { match opcode {
0x35 => {
self.calldata_load(&calldata);
}
0x35 => self.calldata_load(&calldata),
0x36 => self.calldata_size(&calldata),
0x39 => self.code_copy(&code),
_ => panic!("unimplemented {:x}", opcode), _ => panic!("unimplemented {:x}", opcode),
} }
self.pc += 1; self.pc += 1;
@ -116,6 +144,7 @@ impl Stack {
match opcode { match opcode {
0x51 => self.mload(), 0x51 => self.mload(),
0x52 => self.mstore(), 0x52 => self.mstore(),
0x55 => self.sstore(),
0x56 => self.jump(), 0x56 => self.jump(),
0x57 => self.jump_i(), 0x57 => self.jump_i(),
0x5b => self.jump_dest(), 0x5b => self.jump_dest(),
@ -152,13 +181,13 @@ impl Stack {
} }
0xf0 => { 0xf0 => {
if opcode == 0xf3 { 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;
let pos_to_return = u256::u256_to_u64(self.pop()) as usize;
let len_to_return = u256::u256_to_u64(self.pop()) as usize;
return self.mem[pos_to_return..pos_to_return + len_to_return].to_vec(); return self.mem[pos_to_return..pos_to_return + len_to_return].to_vec();
} }
} }
_ => { _ => {
panic!("unimplemented {:x}", opcode);
return panic!("unimplemented {:x}", opcode);
} }
} }
self.gas -= self.opcodes.get(&opcode).unwrap().gas; self.gas -= self.opcodes.get(&opcode).unwrap().gas;
@ -166,15 +195,7 @@ impl Stack {
Vec::new() Vec::new()
} }
} }
pub fn u256_to_u64(a: [u8; 32]) -> u64 {
let mut b8: [u8; 8] = [0; 8];
b8.copy_from_slice(&a[32 - 8..32]);
u64::from_be_bytes(b8)
}
pub fn str_to_u256(s: &str) -> [u8; 32] {
let bi = s.parse::<BigUint>().unwrap().to_bytes_be();
let mut r: [u8; 32] = [0; 32];
r[32 - bi.len()..].copy_from_slice(&bi[..]);
r
pub fn vec_u8_to_hex(bytes: Vec<u8>) -> String {
let strs: Vec<String> = bytes.iter().map(|b| format!("{:02X}", b)).collect();
strs.join("")
} }

+ 137
- 61
src/opcodes.rs

@ -9,6 +9,7 @@ const GSTORAGEKILL: usize = 5000;
const GSTORAGEMOD: usize = 5000; const GSTORAGEMOD: usize = 5000;
const GSTORAGEADD: usize = 20000; const GSTORAGEADD: usize = 20000;
const GEXPONENTBYTE: usize = 10; // cost of EXP exponent per byte 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 const GCOPY: usize = 3; // cost to copy one 32 byte word
const GCONTRACTBYTE: usize = 200; // one byte of code in contract creation const GCONTRACTBYTE: usize = 200; // one byte of code in contract creation
const GCALLVALUETRANSFER: usize = 9000; // non-zero-valued call const GCALLVALUETRANSFER: usize = 9000; // non-zero-valued call
@ -57,81 +58,81 @@ pub fn new_opcodes() -> HashMap {
opcodes.insert(0x02, new_opcode("MUL", 2, 1, 5)); opcodes.insert(0x02, new_opcode("MUL", 2, 1, 5));
opcodes.insert(0x03, new_opcode("SUB", 2, 1, 3)); opcodes.insert(0x03, new_opcode("SUB", 2, 1, 3));
opcodes.insert(0x04, new_opcode("DIV", 2, 1, 5)); opcodes.insert(0x04, new_opcode("DIV", 2, 1, 5));
opcodes.insert(0x05, new_opcode("SDIV", 2, 1, 5));
// opcodes.insert(0x05, new_opcode("SDIV", 2, 1, 5));
opcodes.insert(0x06, new_opcode("MOD", 2, 1, 5)); opcodes.insert(0x06, new_opcode("MOD", 2, 1, 5));
opcodes.insert(0x07, new_opcode("SMOD", 2, 1, 5));
// opcodes.insert(0x07, new_opcode("SMOD", 2, 1, 5));
opcodes.insert(0x08, new_opcode("ADDMOD", 3, 1, 8)); opcodes.insert(0x08, new_opcode("ADDMOD", 3, 1, 8));
opcodes.insert(0x09, new_opcode("MULMOD", 3, 1, 8)); opcodes.insert(0x09, new_opcode("MULMOD", 3, 1, 8));
opcodes.insert(0x0a, new_opcode("EXP", 2, 1, 10)); opcodes.insert(0x0a, new_opcode("EXP", 2, 1, 10));
opcodes.insert(0x0b, new_opcode("SIGNEXTEND", 2, 1, 5));
// opcodes.insert(0x0b, new_opcode("SIGNEXTEND", 2, 1, 5));
// boolean // boolean
opcodes.insert(0x10, new_opcode("LT", 2, 1, 3));
opcodes.insert(0x11, new_opcode("GT", 2, 1, 3));
opcodes.insert(0x12, new_opcode("SLT", 2, 1, 3));
opcodes.insert(0x13, new_opcode("SGT", 2, 1, 3));
opcodes.insert(0x14, new_opcode("EQ", 2, 1, 3));
opcodes.insert(0x15, new_opcode("ISZERO", 1, 1, 3));
opcodes.insert(0x16, new_opcode("AND", 2, 1, 3));
opcodes.insert(0x17, new_opcode("OR", 2, 1, 3));
opcodes.insert(0x18, new_opcode("XOR", 2, 1, 3));
opcodes.insert(0x19, new_opcode("NOT", 1, 1, 3));
opcodes.insert(0x1a, new_opcode("BYTE", 2, 1, 3));
// opcodes.insert(0x10, new_opcode("LT", 2, 1, 3));
// opcodes.insert(0x11, new_opcode("GT", 2, 1, 3));
// opcodes.insert(0x12, new_opcode("SLT", 2, 1, 3));
// opcodes.insert(0x13, new_opcode("SGT", 2, 1, 3));
// opcodes.insert(0x14, new_opcode("EQ", 2, 1, 3));
// opcodes.insert(0x15, new_opcode("ISZERO", 1, 1, 3));
// opcodes.insert(0x16, new_opcode("AND", 2, 1, 3));
// opcodes.insert(0x17, new_opcode("OR", 2, 1, 3));
// opcodes.insert(0x18, new_opcode("XOR", 2, 1, 3));
// opcodes.insert(0x19, new_opcode("NOT", 1, 1, 3));
// opcodes.insert(0x1a, new_opcode("BYTE", 2, 1, 3));
// crypto // crypto
opcodes.insert(0x20, new_opcode("SHA3", 2, 1, 30));
// opcodes.insert(0x20, new_opcode("SHA3", 2, 1, 30));
// contract context // contract context
opcodes.insert(0x30, new_opcode("ADDRESS", 0, 1, 2));
opcodes.insert(0x31, new_opcode("BALANCE", 1, 1, 20));
opcodes.insert(0x32, new_opcode("ORIGIN", 0, 1, 2));
opcodes.insert(0x33, new_opcode("CALLER", 0, 1, 2));
opcodes.insert(0x34, new_opcode("CALLVALUE", 0, 1, 2));
// opcodes.insert(0x30, new_opcode("ADDRESS", 0, 1, 2));
// opcodes.insert(0x31, new_opcode("BALANCE", 1, 1, 20));
// opcodes.insert(0x32, new_opcode("ORIGIN", 0, 1, 2));
// opcodes.insert(0x33, new_opcode("CALLER", 0, 1, 2));
// opcodes.insert(0x34, new_opcode("CALLVALUE", 0, 1, 2));
opcodes.insert(0x35, new_opcode("CALLDATALOAD", 1, 1, 3)); opcodes.insert(0x35, new_opcode("CALLDATALOAD", 1, 1, 3));
opcodes.insert(0x36, new_opcode("CALLDATASIZE", 0, 1, 2)); opcodes.insert(0x36, new_opcode("CALLDATASIZE", 0, 1, 2));
opcodes.insert(0x37, new_opcode("CALLDATACOPY", 3, 0, 3));
opcodes.insert(0x38, new_opcode("CODESIZE", 0, 1, 2));
// opcodes.insert(0x37, new_opcode("CALLDATACOPY", 3, 0, 3));
// opcodes.insert(0x38, new_opcode("CODESIZE", 0, 1, 2));
opcodes.insert(0x39, new_opcode("CODECOPY", 3, 0, 3)); opcodes.insert(0x39, new_opcode("CODECOPY", 3, 0, 3));
opcodes.insert(0x3a, new_opcode("GASPRICE", 0, 1, 2));
opcodes.insert(0x3b, new_opcode("EXTCODESIZE", 1, 1, 20));
opcodes.insert(0x3c, new_opcode("EXTCODECOPY", 4, 0, 20));
// opcodes.insert(0x3a, new_opcode("GASPRICE", 0, 1, 2));
// opcodes.insert(0x3b, new_opcode("EXTCODESIZE", 1, 1, 20));
// opcodes.insert(0x3c, new_opcode("EXTCODECOPY", 4, 0, 20));
// blockchain context // blockchain context
opcodes.insert(0x40, new_opcode("BLOCKHASH", 1, 1, 20));
opcodes.insert(0x41, new_opcode("COINBASE", 0, 1, 2));
opcodes.insert(0x42, new_opcode("TIMESTAMP", 0, 1, 2));
opcodes.insert(0x43, new_opcode("NUMBER", 0, 1, 2));
opcodes.insert(0x44, new_opcode("DIFFICULTY", 0, 1, 2));
opcodes.insert(0x45, new_opcode("GASLIMIT", 0, 1, 2));
// opcodes.insert(0x40, new_opcode("BLOCKHASH", 1, 1, 20));
// opcodes.insert(0x41, new_opcode("COINBASE", 0, 1, 2));
// opcodes.insert(0x42, new_opcode("TIMESTAMP", 0, 1, 2));
// opcodes.insert(0x43, new_opcode("NUMBER", 0, 1, 2));
// opcodes.insert(0x44, new_opcode("DIFFICULTY", 0, 1, 2));
// opcodes.insert(0x45, new_opcode("GASLIMIT", 0, 1, 2));
// storage and execution // storage and execution
opcodes.insert(0x50, new_opcode("POP", 1, 0, 2));
// opcodes.insert(0x50, new_opcode("POP", 1, 0, 2));
opcodes.insert(0x51, new_opcode("MLOAD", 1, 1, 3)); opcodes.insert(0x51, new_opcode("MLOAD", 1, 1, 3));
opcodes.insert(0x52, new_opcode("MSTORE", 2, 0, 3)); opcodes.insert(0x52, new_opcode("MSTORE", 2, 0, 3));
opcodes.insert(0x53, new_opcode("MSTORE8", 2, 0, 3));
opcodes.insert(0x54, new_opcode("SLOAD", 1, 1, 50));
// opcodes.insert(0x53, new_opcode("MSTORE8", 2, 0, 3));
// opcodes.insert(0x54, new_opcode("SLOAD", 1, 1, 50));
opcodes.insert(0x55, new_opcode("SSTORE", 2, 0, 0)); opcodes.insert(0x55, new_opcode("SSTORE", 2, 0, 0));
opcodes.insert(0x56, new_opcode("JUMP", 1, 0, 8)); opcodes.insert(0x56, new_opcode("JUMP", 1, 0, 8));
opcodes.insert(0x57, new_opcode("JUMPI", 2, 0, 10)); opcodes.insert(0x57, new_opcode("JUMPI", 2, 0, 10));
opcodes.insert(0x58, new_opcode("PC", 0, 1, 2));
opcodes.insert(0x59, new_opcode("MSIZE", 0, 1, 2));
opcodes.insert(0x5a, new_opcode("GAS", 0, 1, 2));
// opcodes.insert(0x58, new_opcode("PC", 0, 1, 2));
// opcodes.insert(0x59, new_opcode("MSIZE", 0, 1, 2));
// opcodes.insert(0x5a, new_opcode("GAS", 0, 1, 2));
opcodes.insert(0x5b, new_opcode("JUMPDEST", 0, 0, 1)); opcodes.insert(0x5b, new_opcode("JUMPDEST", 0, 0, 1));
// logging // logging
opcodes.insert(0xa0, new_opcode("LOG0", 2, 0, 375));
opcodes.insert(0xa1, new_opcode("LOG1", 3, 0, 750));
opcodes.insert(0xa2, new_opcode("LOG2", 4, 0, 1125));
opcodes.insert(0xa3, new_opcode("LOG3", 5, 0, 1500));
opcodes.insert(0xa4, new_opcode("LOG4", 6, 0, 1875));
// opcodes.insert(0xa0, new_opcode("LOG0", 2, 0, 375));
// opcodes.insert(0xa1, new_opcode("LOG1", 3, 0, 750));
// opcodes.insert(0xa2, new_opcode("LOG2", 4, 0, 1125));
// opcodes.insert(0xa3, new_opcode("LOG3", 5, 0, 1500));
// opcodes.insert(0xa4, new_opcode("LOG4", 6, 0, 1875));
// closures // closures
opcodes.insert(0xf0, new_opcode("CREATE", 3, 1, 32000));
opcodes.insert(0xf1, new_opcode("CALL", 7, 1, 40));
opcodes.insert(0xf2, new_opcode("CALLCODE", 7, 1, 40));
// opcodes.insert(0xf0, new_opcode("CREATE", 3, 1, 32000));
// opcodes.insert(0xf1, new_opcode("CALL", 7, 1, 40));
// opcodes.insert(0xf2, new_opcode("CALLCODE", 7, 1, 40));
opcodes.insert(0xf3, new_opcode("RETURN", 2, 0, 0)); opcodes.insert(0xf3, new_opcode("RETURN", 2, 0, 0));
opcodes.insert(0xf4, new_opcode("DELEGATECALL", 6, 0, 40));
opcodes.insert(0xff, new_opcode("SUICIDE", 1, 0, 0));
// opcodes.insert(0xf4, new_opcode("DELEGATECALL", 6, 0, 40));
// opcodes.insert(0xff, new_opcode("SUICIDE", 1, 0, 0));
for i in 1..33 { for i in 1..33 {
let name = format!("PUSH{}", i); let name = format!("PUSH{}", i);
@ -169,7 +170,7 @@ impl Stack {
if b0 >= b1 { if b0 >= b1 {
self.push_arbitrary(&(b0 - b1).to_bytes_be()); self.push_arbitrary(&(b0 - b1).to_bytes_be());
} else { } else {
// 2**256
// 2**256 TODO this will not be here hardcoded, there will be a custom type uint256
let max = let max =
"115792089237316195423570985008687907853269984665640564039457584007913129639936" "115792089237316195423570985008687907853269984665640564039457584007913129639936"
.parse::<BigUint>() .parse::<BigUint>()
@ -206,10 +207,32 @@ impl Stack {
self.push_arbitrary(&(b0 * b1 % b2).to_bytes_be()); self.push_arbitrary(&(b0 * b1 % b2).to_bytes_be());
} }
pub fn exp(&mut self) { pub 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());
let b = BigUint::from_bytes_be(&self.pop()[..]);
let e = BigUint::from_bytes_be(&self.pop()[..]);
let mut r = "1".parse::<BigUint>().unwrap();
let zero = "0".parse::<BigUint>().unwrap();
let mut rem = e.clone();
let mut exp = b;
// 2**256 TODO this will not be here hardcoded, there will be a custom type uint256
let field =
"115792089237316195423570985008687907853269984665640564039457584007913129639936"
.parse::<BigUint>()
.unwrap();
while rem != zero {
if rem.bit(0) {
// is odd
r = r * exp.clone() % field.clone();
}
exp = exp.clone() * exp.clone();
rem >>= 1;
}
self.push_arbitrary(&r.to_bytes_be());
let n_bytes = &e.to_bytes_be().len();
let mut exp_fee = n_bytes * GEXPONENTBYTE;
exp_fee += EXP_SUPPLEMENTAL_GAS * n_bytes;
self.gas -= exp_fee as u64;
} }
// boolean // boolean
@ -217,8 +240,33 @@ impl Stack {
// contract context // contract context
pub fn calldata_load(&mut self, calldata: &[u8]) { pub fn calldata_load(&mut self, calldata: &[u8]) {
self.put_arbitrary(&calldata[self.calldata_i..self.calldata_i + 32]);
self.calldata_i += 32;
self.put_arbitrary(&calldata[self.calldata_i..self.calldata_i + self.calldata_size]);
self.calldata_i += self.calldata_size;
}
pub fn calldata_size(&mut self, calldata: &[u8]) {
self.calldata_size = calldata.len();
self.push(u256::usize_to_u256(self.calldata_size));
}
fn spend_gas_data_copy(&mut self, length: usize) {
let length32 = upper_multiple_of_32(length);
self.gas -= ((GCOPY * length32) / 32) as u64;
}
pub fn code_copy(&mut self, code: &[u8]) {
let dest_offset = u256::u256_to_u64(self.pop()) as usize;
let offset = u256::u256_to_u64(self.pop()) as usize;
let length = u256::u256_to_u64(self.pop()) as usize;
self.extend_mem(dest_offset, length);
self.spend_gas_data_copy(length);
for i in 0..length {
if offset + i < code.len() {
self.mem[dest_offset + i] = code[offset + i];
} else {
self.mem[dest_offset + i] = 0;
}
}
// self.mem[dest_offset..dest_offset+length] =
} }
// blockchain context // blockchain context
@ -229,35 +277,59 @@ impl Stack {
return; return;
} }
let old_size = self.mem.len() / 32; let old_size = self.mem.len() / 32;
let new_size = (start + size) / 32;
let new_size = upper_multiple_of_32(start + size) / 32;
let old_total_fee = old_size * GMEMORY + old_size.pow(2) / GQUADRATICMEMDENOM; 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 new_total_fee = new_size * GMEMORY + new_size.pow(2) / GQUADRATICMEMDENOM;
let mem_fee = new_total_fee - old_total_fee; let mem_fee = new_total_fee - old_total_fee;
self.gas -= mem_fee as u64; self.gas -= mem_fee as u64;
let mut new_bytes: Vec<u8> = vec![0; size];
let mut new_bytes: Vec<u8> = vec![0; (new_size - old_size) * 32];
self.mem.append(&mut new_bytes); self.mem.append(&mut new_bytes);
} }
pub fn mload(&mut self) { pub fn mload(&mut self) {
let pos = u256_to_u64(self.pop()) as usize;
let pos = u256::u256_to_u64(self.pop()) as usize;
self.extend_mem(pos as usize, 32); self.extend_mem(pos as usize, 32);
let mem32 = self.mem[pos..pos + 32].to_vec(); let mem32 = self.mem[pos..pos + 32].to_vec();
self.push_arbitrary(&mem32); self.push_arbitrary(&mem32);
} }
pub fn mstore(&mut self) { pub fn mstore(&mut self) {
let pos = u256_to_u64(self.pop());
let pos = u256::u256_to_u64(self.pop());
let val = self.pop(); let val = self.pop();
self.extend_mem(pos as usize, 32); self.extend_mem(pos as usize, 32);
self.mem[pos as usize..].copy_from_slice(&val); self.mem[pos as usize..].copy_from_slice(&val);
} }
pub fn sstore(&mut self) {
let key = self.pop();
let value = self.pop();
if self.storage.contains_key(&key) {
let old_value = self.storage.get(&key).unwrap();
if &value.to_vec() == old_value {
// if the new value is the same as the old one, do not set
return;
}
// if value (from self.pop()) does not exist in the stack, is a STORAGEKILL TODO
println!("mingas {:?}", GSTORAGEMOD);
self.gas -= GSTORAGEMOD as u64;
} else {
// if value does not exist, substract gas for the addition
println!("mingas {:?}", GSTORAGEADD);
self.gas -= GSTORAGEADD as u64;
}
println!(
"insert {:?} - {:?}",
vec_u8_to_hex(key.to_vec()),
vec_u8_to_hex(value.to_vec())
);
self.storage.insert(key, value.to_vec());
}
pub fn jump(&mut self) { pub fn jump(&mut self) {
// TODO that jump destination is valid // TODO that jump destination is valid
self.pc = u256_to_u64(self.pop()) as usize;
self.pc = u256::u256_to_u64(self.pop()) as usize;
} }
pub fn jump_i(&mut self) { pub fn jump_i(&mut self) {
let new_pc = u256_to_u64(self.pop()) as usize;
let new_pc = u256::u256_to_u64(self.pop()) as usize;
if !self.stack.is_empty() { if !self.stack.is_empty() {
let cond = u256_to_u64(self.pop()) as usize;
let cond = u256::u256_to_u64(self.pop()) as usize;
if cond != 0 { if cond != 0 {
self.pc = new_pc; self.pc = new_pc;
} }
@ -269,3 +341,7 @@ impl Stack {
// TODO // TODO
} }
} }
fn upper_multiple_of_32(n: usize) -> usize {
((n - 1) | 31) + 1
}

+ 98
- 22
tests/execute.rs

@ -3,15 +3,21 @@ use evm::*;
#[test] #[test]
fn stack_simple_push_pop() { fn stack_simple_push_pop() {
let mut s = Stack::new(); let mut s = Stack::new();
s.push(str_to_u256("1"));
s.push(str_to_u256("2"));
s.push(str_to_u256("3"));
assert_eq!(s.pop(), str_to_u256("3"));
assert_eq!(s.pop(), str_to_u256("2"));
assert_eq!(s.pop(), str_to_u256("1"));
s.push(u256::str_to_u256("1"));
s.push(u256::str_to_u256("2"));
s.push(u256::str_to_u256("3"));
assert_eq!(s.pop(), u256::str_to_u256("3"));
assert_eq!(s.pop(), u256::str_to_u256("2"));
assert_eq!(s.pop(), u256::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
} }
#[test]
fn available_opcodes() {
let s = Stack::new();
println!("available opcodes {}/130", s.opcodes.len());
}
// arithmetic // arithmetic
#[test] #[test]
fn execute_opcodes_0() { fn execute_opcodes_0() {
@ -20,7 +26,7 @@ fn execute_opcodes_0() {
let mut s = Stack::new(); let mut s = Stack::new();
s.execute(&code, &calldata, false); s.execute(&code, &calldata, false);
assert_eq!(s.pop(), str_to_u256("17"));
assert_eq!(s.pop(), u256::str_to_u256("17"));
assert_eq!(s.gas, 9999999991); assert_eq!(s.gas, 9999999991);
assert_eq!(s.pc, 5); assert_eq!(s.pc, 5);
} }
@ -50,7 +56,7 @@ fn execute_opcodes_2() {
// assert_eq!(out[0], 0x09); // assert_eq!(out[0], 0x09);
assert_eq!(s.gas, 9999999991); assert_eq!(s.gas, 9999999991);
assert_eq!(s.pc, 7); assert_eq!(s.pc, 7);
assert_eq!(s.pop(), str_to_u256("515"));
assert_eq!(s.pop(), u256::str_to_u256("515"));
} }
#[test] #[test]
@ -64,7 +70,7 @@ fn execute_opcodes_3() {
assert_eq!(s.gas, 9999999985); assert_eq!(s.gas, 9999999985);
assert_eq!(s.pc, 7); assert_eq!(s.pc, 7);
assert_eq!(s.pop(), str_to_u256("9"));
assert_eq!(s.pop(), u256::str_to_u256("9"));
} }
// storage and execution // storage and execution
@ -115,16 +121,86 @@ fn execute_opcodes_5() {
assert_eq!(s.gas, 9999999864); assert_eq!(s.gas, 9999999864);
assert_eq!(s.pc, 12); assert_eq!(s.pc, 12);
} }
// #[test]
// fn execute_opcodes_6() {
// // 0x36: calldata_size
// let code = hex::decode("366020036101000a600035045b6001900380600c57").unwrap();
// let calldata = hex::decode("05").unwrap();
//
// let mut s = Stack::new();
// s.execute(&code, &calldata, false);
//
// assert_eq!(s.gas, 9999999788);
// assert_eq!(s.pc, 21);
// assert_eq!(s.stack.len(), 0);
// }
#[test]
fn execute_opcodes_6() {
// 0x36: calldata_size
let code = hex::decode("366020036101000a600035045b6001900380600c57").unwrap();
let calldata = hex::decode("01").unwrap();
let mut s = Stack::new();
s.execute(&code, &calldata, false);
assert_eq!(s.gas, 9999999892);
assert_eq!(s.pc, 21);
assert_eq!(s.stack.len(), 1);
let code = hex::decode("366020036101000a600035045b6001900380600c57").unwrap();
let calldata = hex::decode("05").unwrap();
let mut s = Stack::new();
s.execute(&code, &calldata, false);
assert_eq!(s.gas, 9999999788);
assert_eq!(s.pc, 21);
assert_eq!(s.stack.len(), 1);
let code = hex::decode("366020036101000a600035045b6001900380600c57").unwrap();
let calldata = hex::decode("0101").unwrap();
let mut s = Stack::new();
s.execute(&code, &calldata, false);
assert_eq!(s.gas, 9999993236);
assert_eq!(s.pc, 21);
assert_eq!(s.stack.len(), 1);
}
#[test]
fn execute_opcodes_7() {
// contract deployment (code_copy)
let code = hex::decode("600580600b6000396000f36005600401").unwrap();
let calldata = hex::decode("").unwrap();
let mut s = Stack::new();
let out = s.execute(&code, &calldata, true);
assert_eq!(s.gas, 9999999976);
assert_eq!(s.pc, 10);
assert_eq!(s.stack.len(), 0);
assert_eq!(s.mem.len(), 32);
assert_eq!(
s.mem,
hex::decode("6005600401000000000000000000000000000000000000000000000000000000").unwrap()
);
assert_eq!(out, hex::decode("6005600401").unwrap());
}
#[test]
fn execute_opcodes_8() {
let code = hex::decode("611000805151").unwrap();
let calldata = hex::decode("").unwrap();
let mut s = Stack::new();
s.execute(&code, &calldata, false);
assert_eq!(s.gas, 9999999569);
assert_eq!(s.pc, 6);
assert_eq!(s.stack.len(), 2);
assert_eq!(s.mem.len(), 4128);
}
#[test]
fn execute_opcodes_9() {
// sstore (0x55)
let code = hex::decode("60026000556001600055").unwrap();
let calldata = hex::decode("").unwrap();
let mut s = Stack::new();
s.execute(&code, &calldata, true);
assert_eq!(s.gas, 9999974988);
// assert_eq!(s.gas, 9999977788); // TODO WIP geth reported gas
assert_eq!(s.pc, 10);
assert_eq!(s.stack.len(), 0);
assert_eq!(s.storage.len(), 1);
}

Loading…
Cancel
Save