Browse Source

Add boolean opcodes & other

- Add boolean opcodes
- Add jump_dest validity check
- Add tests for opcode exceptions
- Update calldataload opcode logic
- Add stack.peek()
master
arnaucube 9 months ago
parent
commit
dac8a29e7e
4 changed files with 119 additions and 6 deletions
  1. +2
    -1
      Cargo.toml
  2. +25
    -2
      src/lib.rs
  3. +71
    -2
      src/opcodes.rs
  4. +21
    -1
      tests/execute.rs

+ 2
- 1
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"

+ 25
- 2
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)),

+ 71
- 2
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::<BigUint>()
.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) {

+ 21
- 1
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);
}

Loading…
Cancel
Save