mirror of
https://github.com/arnaucube/evm-rs.git
synced 2026-02-02 17:06:40 +01:00
Add boolean opcodes & other
- Add boolean opcodes - Add jump_dest validity check - Add tests for opcode exceptions - Update calldataload opcode logic - Add stack.peek()
This commit is contained in:
@@ -7,6 +7,7 @@ license = "GPL-3.0"
|
|||||||
repository = "https://github.com/arnaucube/evm-rs"
|
repository = "https://github.com/arnaucube/evm-rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-bigint = "0.4"
|
|
||||||
num = "0.4.0"
|
num = "0.4.0"
|
||||||
|
num-traits = "0.2.14"
|
||||||
|
num-bigint = "0.4"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
|||||||
27
src/lib.rs
27
src/lib.rs
@@ -1,6 +1,5 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use num_bigint::BigUint;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
pub mod opcodes;
|
pub mod opcodes;
|
||||||
pub mod u256;
|
pub mod u256;
|
||||||
@@ -82,6 +81,12 @@ impl Stack {
|
|||||||
None => Err("pop err".to_string()), // WIP
|
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> {
|
pub fn substract_gas(&mut self, val: u64) -> Result<(), String> {
|
||||||
if self.gas < val {
|
if self.gas < val {
|
||||||
return Err("out of gas".to_string());
|
return Err("out of gas".to_string());
|
||||||
@@ -142,9 +147,27 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
self.pc += 1;
|
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 => {
|
0x30 => {
|
||||||
match opcode {
|
match opcode {
|
||||||
0x35 => self.calldata_load(&calldata),
|
0x35 => self.calldata_load(&calldata)?,
|
||||||
0x36 => self.calldata_size(&calldata),
|
0x36 => self.calldata_size(&calldata),
|
||||||
0x39 => self.code_copy(&code)?,
|
0x39 => self.code_copy(&code)?,
|
||||||
_ => return Err(format!("unimplemented {:x}", opcode)),
|
_ => return Err(format!("unimplemented {:x}", opcode)),
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
use num_traits::identities::Zero;
|
||||||
|
|
||||||
// Non-opcode gas prices
|
// Non-opcode gas prices
|
||||||
const GDEFAULT: usize = 1;
|
const GDEFAULT: usize = 1;
|
||||||
@@ -8,6 +10,7 @@ const GSTORAGEREFUND: usize = 15000;
|
|||||||
const GSTORAGEKILL: usize = 5000;
|
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 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
|
||||||
@@ -244,12 +247,77 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// boolean
|
// 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
|
// crypto
|
||||||
|
|
||||||
// contract context
|
// contract context
|
||||||
pub fn calldata_load(&mut self, calldata: &[u8]) {
|
pub fn calldata_load(&mut self, calldata: &[u8]) -> Result<(), String> {
|
||||||
self.put_arbitrary(&calldata[self.calldata_i..self.calldata_i + self.calldata_size]);
|
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;
|
self.calldata_i += self.calldata_size;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn calldata_size(&mut self, calldata: &[u8]) {
|
pub fn calldata_size(&mut self, calldata: &[u8]) {
|
||||||
self.calldata_size = calldata.len();
|
self.calldata_size = calldata.len();
|
||||||
@@ -310,6 +378,7 @@ impl Stack {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn sstore(&mut self) -> Result<(), String> {
|
pub fn sstore(&mut self) -> Result<(), String> {
|
||||||
|
// TODO WIP
|
||||||
let key = self.pop()?;
|
let key = self.pop()?;
|
||||||
let value = self.pop()?;
|
let value = self.pop()?;
|
||||||
if self.storage.contains_key(&key) {
|
if self.storage.contains_key(&key) {
|
||||||
|
|||||||
@@ -215,9 +215,29 @@ fn execute_opcodes_9() {
|
|||||||
let mut s = Stack::new();
|
let mut s = Stack::new();
|
||||||
s.execute(&code, &calldata, false).unwrap();
|
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, 9999977788); // TODO WIP geth reported gas
|
||||||
|
assert_eq!(s.gas, 9999974988);
|
||||||
assert_eq!(s.pc, 10);
|
assert_eq!(s.pc, 10);
|
||||||
assert_eq!(s.stack.len(), 0);
|
assert_eq!(s.stack.len(), 0);
|
||||||
assert_eq!(s.storage.len(), 1);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user