pub use primitive_types::U256; use tiny_keccak::Hasher; use tiny_keccak::Keccak; pub mod storage; pub use storage::Storage; macro_rules! repr_u8 { ($(#[$meta:meta])* $vis:vis enum $name:ident { $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)* }) => { $(#[$meta])* $vis enum $name { $($(#[$vmeta])* $vname $(= $val)?,)* } impl std::convert::TryFrom for $name { type Error = (); fn try_from(v: u8) -> Result { match v { $(x if x == $name::$vname as u8 => Ok($name::$vname),)* _ => Err(()), } } } } } fn to_signed(value: U256) -> U256 { match value.bit(255) { true => (!value).overflowing_add(U256::one()).0, false => value, } } repr_u8! { // EVM instructions #[repr(u8)] #[derive(Debug, Eq, PartialEq)] pub enum Instruction { Stop = 0x00, Add = 0x01, Mul = 0x02, Sub = 0x03, Div = 0x04, SDiv = 0x05, Mod = 0x06, SMod = 0x07, AddMod = 0x08, MulMod = 0x09, Exp = 0x0a, SignExtend = 0x0b, // 0x0c - 0x0f reserved Lt = 0x10, Gt = 0x11, SLt = 0x12, SGt = 0x13, Eq = 0x14, IsZero = 0x15, And = 0x16, Or = 0x17, Xor = 0x18, Not = 0x19, Byte = 0x1a, // EIP145 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md Shl = 0x1b, Shr = 0x1c, Sar = 0x1d, Keccak256 = 0x20, // 0x21 - 0x2f reserved Address = 0x30, Balance = 0x31, Origin = 0x32, Caller = 0x33, CallValue = 0x34, CallDataLoad = 0x35, CallDataSize = 0x36, CallDataCopy = 0x37, CodeSize = 0x38, CodeCopy = 0x39, GasPrice = 0x3a, ExtCodeSize = 0x3b, ExtCodeCopy = 0x3c, ReturnDataSize = 0x3d, ReturnDataCopy = 0x3e, BlockHash = 0x40, Coinbase = 0x41, Timestamp = 0x42, Number = 0x43, Difficulty = 0x44, GasLimit = 0x45, // EIP 1344 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1344.md ChainId = 0x46, // 0x47 - 0x4f reserved // EIP-3198 BaseFee = 0x48, Pop = 0x50, MLoad = 0x51, MStore = 0x52, MStore8 = 0x53, SLoad = 0x54, SStore = 0x55, Jump = 0x56, JumpI = 0x57, GetPc = 0x58, MSize = 0x59, Gas = 0x5a, JumpDest = 0x5b, // 0x5c - 0x5f reserved Push1 = 0x60, Push2 = 0x61, Push3 = 0x62, Push4 = 0x63, Push5 = 0x64, Push6 = 0x65, Push7 = 0x66, Push8 = 0x67, Push9 = 0x68, Push10 = 0x69, Push11 = 0x6a, Push12 = 0x6b, Push13 = 0x6c, Push14 = 0x6d, Push15 = 0x6e, Push16 = 0x6f, Push17 = 0x70, Push18 = 0x71, Push19 = 0x72, Push20 = 0x73, Push21 = 0x74, Push22 = 0x75, Push23 = 0x76, Push24 = 0x77, Push25 = 0x78, Push26 = 0x79, Push27 = 0x7a, Push28 = 0x7b, Push29 = 0x7c, Push30 = 0x7d, Push31 = 0x7e, Push32 = 0x7f, Dup1 = 0x80, Dup2 = 0x81, Dup3 = 0x82, Dup4 = 0x83, Dup5 = 0x84, Dup6 = 0x85, Dup7 = 0x86, Dup8 = 0x87, Dup9 = 0x88, Dup10 = 0x89, Dup11 = 0x8a, Dup12 = 0x8b, Dup13 = 0x8c, Dup14 = 0x8d, Dup15 = 0x8e, Dup16 = 0x8f, Swap1 = 0x90, Swap2 = 0x91, Swap3 = 0x92, Swap4 = 0x93, Swap5 = 0x94, Swap6 = 0x95, Swap7 = 0x96, Swap8 = 0x97, Swap9 = 0x98, Swap10 = 0x99, Swap11 = 0x9a, Swap12 = 0x9b, Swap13 = 0x9c, Swap14 = 0x9d, Swap15 = 0x9e, Swap16 = 0x9f, Log0 = 0xa0, Log1 = 0xa1, Log2 = 0xa2, Log3 = 0xa3, Log4 = 0xa4, // 0xa5 - 0xaf reserved Create = 0xf0, Call = 0xf1, CallCode = 0xf2, Return = 0xf3, DelegateCall = 0xf4, Create2 = 0xfb, Revert = 0xfd, StaticCall = 0xfa, SelfDestruct = 0xff, } } pub const MAX_STACK_SIZE: usize = 1024; #[derive(Debug)] pub struct Stack { pub data: Vec, } impl Default for Stack { fn default() -> Self { let data = Vec::with_capacity(MAX_STACK_SIZE); Stack { data } } } impl Stack { pub fn push(&mut self, value: U256) { self.data.push(value); } pub fn pop(&mut self) -> U256 { self.data.pop().unwrap() } pub fn peek(&self) -> U256 { self.data[self.data.len() - 1] } pub fn swap(&mut self, index: usize) { let ptr = self.data.len() - 1; // dbg!("Attempting swap ptr = {}, value = {}", ptr, self.data[ptr]); if ptr < index { return; } self.data.swap(ptr, ptr - index); // dbg!("Swapped {}", self.data[ptr]); } pub fn dup(&mut self, index: usize) { self.push(self.data[self.data.len() - index]); } } pub struct Machine<'a> { pub stack: Stack, state: U256, memory: Vec, pub result: Vec, // The cost function. cost_fn: Box U256>, fetch_contract: Box Option + 'a>, // Total gas used so far. // gas_used += cost_fn(instruction) gas_used: U256, // The input data. data: Vec, pub storage: Storage, owner: U256, } #[derive(PartialEq, Debug)] pub enum AbortError { DivZero, InvalidOpcode, } #[derive(PartialEq, Debug)] pub enum ExecutionState { Abort(AbortError), Revert, Ok, } #[derive(Default, Clone)] pub struct BlockInfo { pub timestamp: U256, pub difficulty: U256, pub block_hash: U256, pub number: U256, } pub struct ContractInfo { pub store: Storage, pub bytecode: Vec, } impl<'a> Machine<'a> { pub fn new(cost_fn: T) -> Self where T: Fn(&Instruction) -> U256 + 'static, { Machine { stack: Stack::default(), state: U256::zero(), memory: Vec::new(), result: Vec::new(), cost_fn: Box::new(cost_fn), fetch_contract: Box::new(|_| None), gas_used: U256::zero(), data: Vec::new(), storage: Storage::new(U256::zero()), owner: U256::zero(), } } pub fn new_with_data(cost_fn: T, data: Vec) -> Self where T: Fn(&Instruction) -> U256 + 'static, { Machine { stack: Stack::default(), state: U256::zero(), memory: Vec::new(), result: Vec::new(), cost_fn: Box::new(cost_fn), gas_used: U256::zero(), data, fetch_contract: Box::new(|_| None), storage: Storage::new(U256::zero()), owner: U256::zero(), } } pub fn set_storage(&mut self, storage: Storage) { self.storage = storage; } pub fn set_fetcher( &mut self, fetcher: Box Option + 'a>, ) { self.fetch_contract = fetcher; } pub fn execute( &mut self, bytecode: &[u8], block_info: BlockInfo, ) -> ExecutionState { let mut pc = 0; let len = bytecode.len(); while pc < len { let opcode = bytecode[pc]; let inst = match Instruction::try_from(opcode) { Ok(inst) => inst, Err(_) => { return ExecutionState::Abort(AbortError::InvalidOpcode); } }; let cost = (self.cost_fn)(&inst); pc += 1; println!("STACK: {:?}", self.stack); match inst { Instruction::Stop => {} Instruction::Add => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs + rhs); } Instruction::Sub => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs - rhs); } Instruction::Mul => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs * rhs); } Instruction::Div => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); if rhs == U256::zero() { return ExecutionState::Abort(AbortError::DivZero); } self.stack.push(lhs / rhs); } Instruction::SDiv => { let dividend = to_signed(self.stack.pop()); let divisor = to_signed(self.stack.pop()); const U256_ZERO: U256 = U256::zero(); let quotient = if divisor == U256_ZERO { U256_ZERO } else { let min = (U256::one() << 255) - U256::one(); if dividend == min && divisor == !U256::one() { min } else { let sign = dividend.bit(255) ^ divisor.bit(255); match sign { true => !(dividend / divisor), false => dividend / divisor, } } }; self.stack.push(quotient); } Instruction::Mod => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); let res = match lhs.checked_rem(rhs) { Some(res) => res, None => U256::zero(), }; self.stack.push(res); } Instruction::SMod => { fn to_signed(value: U256) -> U256 { match value.bit(255) { true => (!value).overflowing_add(U256::one()).0, false => value, } } let lhs = self.stack.pop(); let signed_lhs = to_signed(lhs); let sign = lhs.bit(255); let rhs = to_signed(self.stack.pop()); if rhs == U256::zero() { self.stack.push(U256::zero()); } else { let value = signed_lhs % rhs; self.stack.push(match sign { true => (!value).overflowing_add(U256::one()).0, false => value, }); } } Instruction::AddMod => { let a = self.stack.pop(); let b = self.stack.pop(); let c = self.stack.pop(); let res = match a.checked_add(b) { Some(res) => res % c, None => U256::zero(), }; self.stack.push(res); } Instruction::MulMod => { let a = self.stack.pop(); let b = self.stack.pop(); let c = self.stack.pop(); let res = match a.checked_mul(b) { Some(res) => res % c, None => U256::zero(), }; self.stack.push(res); } Instruction::Exp => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs.overflowing_pow(rhs).0) } Instruction::SignExtend => { let pos = self.stack.pop(); let value = self.stack.pop(); if pos > U256::from(32) { self.stack.push(value); } else { let bit_pos = (pos.low_u64() * 8 + 7) as usize; let bit = value.bit(bit_pos); let mask = (U256::one() << bit_pos) - U256::one(); let result = if bit { value | !mask } else { value & mask }; self.stack.push(result); } } Instruction::Lt => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self .stack .push(if lhs < rhs { U256::one() } else { U256::zero() }); } Instruction::Gt => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self .stack .push(if lhs > rhs { U256::one() } else { U256::zero() }); } Instruction::SLt => { let (lhs, l_sign) = { let lhs = self.stack.pop(); let l_sign = lhs.bit(255); (to_signed(lhs), l_sign) }; let (rhs, r_sign) = { let rhs = self.stack.pop(); let r_sign = rhs.bit(255); (to_signed(rhs), r_sign) }; let result = match (l_sign, r_sign) { (false, false) => lhs < rhs, (true, true) => lhs > rhs, (true, false) => true, (false, true) => false, }; self .stack .push(if result { U256::one() } else { U256::zero() }); } Instruction::SGt => { let (lhs, l_sign) = { let lhs = self.stack.pop(); let l_sign = lhs.bit(255); (to_signed(lhs), l_sign) }; let (rhs, r_sign) = { let rhs = self.stack.pop(); let r_sign = rhs.bit(255); (to_signed(rhs), r_sign) }; let result = match (l_sign, r_sign) { (false, false) => lhs > rhs, (true, true) => lhs < rhs, (true, false) => false, (false, true) => true, }; self .stack .push(if result { U256::one() } else { U256::zero() }); } Instruction::Shr => { let rhs = self.stack.pop(); let lhs = self.stack.pop(); if rhs < U256::from(256) { self.stack.push(lhs >> rhs); } else { self.stack.push(U256::zero()); } } Instruction::Eq => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(if lhs == rhs { U256::one() } else { U256::zero() }); } Instruction::IsZero => { let val = self.stack.pop(); self.stack.push(if val == U256::zero() { U256::one() } else { U256::zero() }); } Instruction::And => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs & rhs); } Instruction::Or => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs | rhs); } Instruction::Xor => { let lhs = self.stack.pop(); let rhs = self.stack.pop(); self.stack.push(lhs ^ rhs); } Instruction::Not => { let val = self.stack.pop(); self.stack.push(!val); } Instruction::Byte => { let rhs = self.stack.pop(); let lhs = self.stack.pop(); match rhs > U256::from(32) { true => { self.stack.push(U256::zero()); } false => { let byte = lhs.byte(rhs.as_u64() as usize); self.stack.push(U256::from(byte)); } } } Instruction::Keccak256 => { let offset = self.stack.pop().low_u64() as usize; let size = self.stack.pop().low_u64() as usize; let data = &self.memory[offset..offset + size]; let mut result = [0u8; 32]; let mut keccak = Keccak::v256(); keccak.update(data); keccak.finalize(&mut result); self.stack.push(U256::from(result)); } Instruction::Address => { self.stack.push(self.owner); } Instruction::Balance => { let _addr = self.stack.pop(); // TODO: balance self.stack.push(U256::zero()); } Instruction::Origin => { // TODO: origin self.stack.push(U256::zero()); } Instruction::Caller => { // TODO: caller self.stack.push(U256::zero()); } Instruction::CallValue => { self.stack.push(self.state); } Instruction::CallDataLoad => { let offset = self.stack.pop(); let offset = match offset > usize::max_value().into() { true => self.data.len(), false => offset.low_u64() as usize, }; let end = std::cmp::min(offset + 32, self.data.len()); let mut data = self.data[offset..end].to_vec(); data.resize(32, 0u8); self.stack.push(U256::from(data.as_slice())); } Instruction::CallDataSize => { self.stack.push(U256::from(self.data.len())); } Instruction::CallDataCopy => { let mem_offset = self.stack.pop(); let offset = self.stack.pop(); let size = self.stack.pop(); if offset > U256::from(self.data.len()) || offset.overflowing_add(size).1 { return ExecutionState::Ok; } let offset = offset.low_u64() as usize; let size = size.low_u64() as usize; let end = std::cmp::min(offset + size, self.data.len()); let mut data = self.data[offset..end].to_vec(); data.resize(32, 0u8); let mem_offset = mem_offset.low_u64() as usize; self.memory[mem_offset..mem_offset + 32] .copy_from_slice(data.as_slice()); } Instruction::CodeSize => { self.stack.push(U256::from(len)); } Instruction::CodeCopy => { let mem_offset = self.stack.pop().low_u64() as usize; let code_offset = self.stack.pop(); let len = self.stack.pop().low_u64() as usize; if code_offset > usize::max_value().into() { dbg!("CODECOPY: offset too large"); } let code_offset = code_offset.low_u64() as usize; if code_offset < self.data.len() { let code = &bytecode[code_offset..code_offset + len]; if self.memory.len() < mem_offset + 32 { self.memory.resize(mem_offset + 32, 0); } for i in 0..32 { if i > code.len() { self.memory[mem_offset + i] = 0; } else { self.memory[mem_offset + i] = code[i]; } } } } Instruction::ExtCodeSize => { // Fetch the `Contract-Src` from Arweave for the contract. } Instruction::ExtCodeCopy => { // Fetch the `Contract-Src` from Arweave for the contract. } Instruction::ReturnDataSize => { self.stack.push(U256::from(self.result.len())); } Instruction::ReturnDataCopy => { let mem_offset = self.stack.pop().low_u64() as usize; let data_offset = self.stack.pop().low_u64() as usize; let length = self.stack.pop().low_u64() as usize; if self.result.len() < data_offset + length { panic!("Return data out of bounds"); } let data = &self.result[data_offset..data_offset + length]; if self.memory.len() < mem_offset + 32 { self.memory.resize(mem_offset + 32, 0); } for i in 0..32 { if i > data.len() { self.memory[mem_offset + i] = 0; } else { self.memory[mem_offset + i] = data[i]; } } } Instruction::BlockHash => { self.stack.push(block_info.block_hash); } Instruction::Timestamp => { self.stack.push(block_info.timestamp); } Instruction::Number => { self.stack.push(block_info.number); } Instruction::Difficulty => { self.stack.push(block_info.difficulty); } Instruction::GasLimit => { self.stack.push(U256::MAX); } Instruction::Pop => { self.stack.pop(); } Instruction::MLoad => { let offset = self.stack.pop(); if offset > usize::max_value().into() { dbg!("MLOAD: offset too large"); } let len = offset.low_u64() as usize; let mut data = vec![0u8; 32]; for (idx, mem_ptr) in (0..len).zip(len..len + 32) { data[idx] = self.memory[mem_ptr]; } self.stack.push(U256::from(data.as_slice())); } Instruction::MStore => { let offset = self.stack.pop(); let val = self.stack.pop(); if offset > usize::max_value().into() { dbg!("MStore: offset too large"); } let offset = offset.low_u64() as usize; if self.memory.len() <= offset + 32 { self.memory.resize(offset + 32, 0); } for i in 0..32 { let mem_ptr = offset + i; // Big endian byte let index = 4 * 8 - 1 - i; self.memory[mem_ptr] = val.byte(index); } } Instruction::MStore8 => { let offset = self.stack.pop(); let val = self.stack.pop(); if offset > usize::max_value().into() { dbg!("MStore8: offset too large"); } let mem_ptr = offset.low_u64() as usize; if mem_ptr >= self.memory.len() { self.memory.resize(mem_ptr + 32, 0); } self.memory[mem_ptr] = val.byte(0); } Instruction::SLoad => { let offset = self.stack.pop(); let data = self.storage.get(&self.owner, &offset); self.stack.push(data); } Instruction::SStore => { let offset = self.stack.pop(); let val = self.stack.pop(); self.storage.insert(&self.owner, offset, val); } Instruction::Jump => { let offset = self.stack.pop(); pc = offset.low_u64() as usize; } Instruction::JumpI => { let offset = self.stack.pop(); let condition = self.stack.pop(); if condition != U256::zero() { pc = offset.low_u64() as usize; } } Instruction::GetPc => { self.stack.push(U256::from(pc)); } Instruction::MSize | Instruction::Gas | Instruction::GasPrice | Instruction::Coinbase => { self.stack.push(U256::zero()); } Instruction::JumpDest => {} Instruction::Push1 | Instruction::Push2 | Instruction::Push3 | Instruction::Push4 | Instruction::Push5 | Instruction::Push6 | Instruction::Push7 | Instruction::Push8 | Instruction::Push9 | Instruction::Push10 | Instruction::Push11 | Instruction::Push12 | Instruction::Push13 | Instruction::Push14 | Instruction::Push15 | Instruction::Push16 | Instruction::Push17 | Instruction::Push18 | Instruction::Push19 | Instruction::Push20 | Instruction::Push21 | Instruction::Push22 | Instruction::Push23 | Instruction::Push24 | Instruction::Push25 | Instruction::Push26 | Instruction::Push27 | Instruction::Push28 | Instruction::Push29 | Instruction::Push30 | Instruction::Push31 | Instruction::Push32 => { let value_size = (opcode - 0x60 + 1) as usize; let value = &bytecode[pc..pc + value_size]; pc += value_size; self.stack.push(U256::from(value)); } Instruction::Dup1 | Instruction::Dup2 | Instruction::Dup3 | Instruction::Dup4 | Instruction::Dup5 | Instruction::Dup6 | Instruction::Dup7 | Instruction::Dup8 | Instruction::Dup9 | Instruction::Dup10 | Instruction::Dup11 | Instruction::Dup12 | Instruction::Dup13 | Instruction::Dup14 | Instruction::Dup15 | Instruction::Dup16 => { let size = (opcode - 0x80 + 1) as usize; self.stack.dup(size); } Instruction::Swap1 | Instruction::Swap2 | Instruction::Swap3 | Instruction::Swap4 | Instruction::Swap5 | Instruction::Swap6 | Instruction::Swap7 | Instruction::Swap8 | Instruction::Swap9 | Instruction::Swap10 | Instruction::Swap11 | Instruction::Swap12 | Instruction::Swap13 | Instruction::Swap14 | Instruction::Swap15 | Instruction::Swap16 => { let size = (opcode - 0x90 + 1) as usize; self.stack.swap(size); } Instruction::Log0 | Instruction::Log1 | Instruction::Log2 | Instruction::Log3 | Instruction::Log4 => {} Instruction::Create => { // TODO } Instruction::Call => { // Call parameters let _gas = self.stack.pop(); let addr = self.stack.pop(); let _value = self.stack.pop(); let in_offset = self.stack.pop().low_u64() as usize; let in_size = self.stack.pop().low_u64() as usize; let out_offset = self.stack.pop().low_u64() as usize; let out_size = self.stack.pop().low_u64() as usize; let input = &bytecode[in_offset..in_offset + in_size]; let mut evm = Self::new_with_data(|_| U256::zero(), input.to_vec()); let contract = (self.fetch_contract)(&addr) .expect("No fetch contract handler provided."); evm.set_storage(contract.store); evm.execute(&contract.bytecode, block_info.clone()); self.memory[out_offset..out_offset + out_size] .copy_from_slice(&evm.result); } Instruction::CallCode | Instruction::DelegateCall => { // Call parameters let _gas = self.stack.pop(); let addr = self.stack.pop(); let in_offset = self.stack.pop().low_u64() as usize; let in_size = self.stack.pop().low_u64() as usize; let out_offset = self.stack.pop().low_u64() as usize; let out_size = self.stack.pop().low_u64() as usize; let input = &bytecode[in_offset..in_offset + in_size]; let mut evm = Self::new_with_data(|_| U256::zero(), input.to_vec()); let contract = (self.fetch_contract)(&addr) .expect("No fetch contract handler provided."); evm.set_storage(contract.store); evm.execute(&contract.bytecode, block_info.clone()); self.memory[out_offset..out_offset + out_size] .copy_from_slice(&evm.result); } Instruction::Return => { let offset = self.stack.pop(); if offset > usize::max_value().into() { dbg!("Return: offset too large"); } let offset = offset.low_u64() as usize; let size = self.stack.pop().low_u64() as usize; let mut data = vec![]; for idx in offset..offset + size { if idx >= self.memory.len() { data.push(0); } else { data.push(self.memory[idx]); } } self.result = data; break; } Instruction::Revert => { return ExecutionState::Revert; } _ => unimplemented!(), } self.gas_used += cost; } ExecutionState::Ok } } #[cfg(test)] mod tests { use crate::storage::Storage; use crate::ExecutionState; use crate::Instruction; use crate::Machine; use crate::Stack; use hex_literal::hex; use primitive_types::U256; fn test_cost_fn(_: &Instruction) -> U256 { U256::zero() } #[allow(dead_code)] fn print_vm_memory(vm: &Machine) { let mem = &vm.memory; println!("{:?}", mem); for (i, cell) in mem.iter().enumerate() { if i % 16 == 0 { print!("\n{:x}: ", i); } print!("{:#04x} ", cell); } } #[test] fn test_basic() { let mut machine = Machine::new(test_cost_fn); let status = machine.execute( &[ Instruction::Push1 as u8, 0x01, Instruction::Push1 as u8, 0x02, Instruction::Add as u8, ], Default::default(), ); assert_eq!(status, ExecutionState::Ok); assert_eq!(machine.stack.pop(), U256::from(0x03)); } #[test] fn test_stack_swap() { let mut stack = Stack::default(); stack.push(U256::from(0x01)); stack.push(U256::from(0x02)); stack.swap(1); stack.pop(); stack.swap(1); assert_eq!(stack.pop(), U256::from(0x02)); } #[test] fn test_swap_jump() { let mut machine = Machine::new(test_cost_fn); let status = machine.execute( &[ Instruction::Push1 as u8, 0x00, Instruction::Push1 as u8, 0x03, Instruction::Swap1 as u8, Instruction::Pop as u8, Instruction::Swap1 as u8, ], Default::default(), ); assert_eq!(status, ExecutionState::Ok); assert_eq!(machine.stack.pop(), U256::from(0x03)); } #[test] fn test_sdiv() { let mut machine = Machine::new(test_cost_fn); let status = machine.execute( &[ Instruction::Push1 as u8, 0x02, Instruction::Push1 as u8, 0x04, Instruction::SDiv as u8, ], Default::default(), ); assert_eq!(status, ExecutionState::Ok); assert_eq!(machine.stack.pop(), U256::from(0x02)); } #[test] fn test_add_solidity() { // label_0000: // // Inputs[1] { @0005 msg.value } // 0000 60 PUSH1 0x80 // 0002 60 PUSH1 0x40 // 0004 52 MSTORE // 0005 34 CALLVALUE // 0006 80 DUP1 // 0007 15 ISZERO // 0008 60 PUSH1 0x0f // 000A 57 *JUMPI // // Stack delta = +1 // // Outputs[2] // // { // // @0004 memory[0x40:0x60] = 0x80 // // @0005 stack[0] = msg.value // // } // // Block ends with conditional jump to 0x000f, if !msg.value // label_000B: // // Incoming jump from 0x000A, if not !msg.value // // Inputs[1] { @000E memory[0x00:0x00] } // 000B 60 PUSH1 0x00 // 000D 80 DUP1 // 000E FD *REVERT // // Stack delta = +0 // // Outputs[1] { @000E revert(memory[0x00:0x00]); } // // Block terminates // label_000F: // // Incoming jump from 0x000A, if !msg.value // // Inputs[1] { @001B memory[0x00:0x77] } // 000F 5B JUMPDEST // 0010 50 POP // 0011 60 PUSH1 0x77 // 0013 80 DUP1 // 0014 60 PUSH1 0x1d // 0016 60 PUSH1 0x00 // 0018 39 CODECOPY // 0019 60 PUSH1 0x00 // 001B F3 *RETURN // // Stack delta = -1 // // Outputs[2] // // { // // @0018 memory[0x00:0x77] = code[0x1d:0x94] // // @001B return memory[0x00:0x77]; // // } // // Block terminates // 001C FE *ASSERT // 001D 60 PUSH1 0x80 // 001F 60 PUSH1 0x40 // 0021 52 MSTORE // 0022 34 CALLVALUE // 0023 80 DUP1 // 0024 15 ISZERO // 0025 60 PUSH1 0x0f // 0027 57 *JUMPI // 0028 60 PUSH1 0x00 // 002A 80 DUP1 // 002B FD *REVERT // 002C 5B JUMPDEST // 002D 50 POP // 002E 60 PUSH1 0x04 // 0030 36 CALLDATASIZE // 0031 10 LT // 0032 60 PUSH1 0x28 // 0034 57 *JUMPI // 0035 60 PUSH1 0x00 // 0037 35 CALLDATALOAD // 0038 60 PUSH1 0xe0 // 003A 1C SHR // 003B 80 DUP1 // 003C 63 PUSH4 0x4f2be91f // 0041 14 EQ // 0042 60 PUSH1 0x2d // 0044 57 *JUMPI // 0045 5B JUMPDEST // 0046 60 PUSH1 0x00 // 0048 80 DUP1 // 0049 FD *REVERT // 004A 5B JUMPDEST // 004B 60 PUSH1 0x03 // 004D 60 PUSH1 0x40 // 004F 51 MLOAD // 0050 90 SWAP1 // 0051 81 DUP2 // 0052 52 MSTORE // 0053 60 PUSH1 0x20 // 0055 01 ADD // 0056 60 PUSH1 0x40 // 0058 51 MLOAD // 0059 80 DUP1 // 005A 91 SWAP2 // 005B 03 SUB // 005C 90 SWAP1 // 005D F3 *RETURN // 005E FE *ASSERT // 005F A2 LOG2 // 0060 64 PUSH5 0x6970667358 // 0066 22 22 // 0067 12 SLT // 0068 20 SHA3 // 0069 FD *REVERT // 006A A5 A5 // 006B D9 D9 // 006C D2 D2 // 006D 16 AND // 006E 9A SWAP11 // 006F B4 B4 // 0070 47 SELFBALANCE // 0071 B0 PUSH // 0072 A2 LOG2 // 0073 35 CALLDATALOAD // 0074 7F PUSH32 0xe5d46648452a2ffa2d4046a757ef013fbfe7a7d764736f6c634300080a0033 let hex_code = hex!("6080604052348015600f57600080fd5b506004361060285760003560e01c80634f2be91f14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006003905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea26469706673582212200047574855cc88b41f29d7879f8126fe8da6f03c5f30c66c8e1290510af5253964736f6c634300080a0033"); let mut machine = Machine::new_with_data(test_cost_fn, hex!("4f2be91f").to_vec()); let status = machine.execute(&hex_code, Default::default()); assert_eq!(status, ExecutionState::Ok); assert_eq!(machine.result.len(), 32); assert_eq!(machine.result.pop(), Some(0x03)); } #[test] fn test_mstore() { // let mut machine = Machine::new(test_cost_fn); // memory[0x40:0x60] = 0x80 let status = machine.execute( &[ Instruction::Push1 as u8, 0x80, Instruction::Push1 as u8, 0x40, Instruction::MStore as u8, ], Default::default(), ); assert_eq!(status, ExecutionState::Ok); } #[test] fn test_keccak256() { // object "object" { // code { // mstore(0, 0x10) // pop(keccak256(0, 0x20)) // } // } let bytes = hex!("6010600052602060002050"); let mut machine = Machine::new(test_cost_fn); let status = machine.execute(&bytes, Default::default()); assert_eq!(status, ExecutionState::Ok); } #[test] fn test_storage_constructor() { let bytes = hex!("608060405234801561001057600080fd5b506040518060400160405280600a81526020017f6c6974746c6564697679000000000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610166565b82805461006e90610134565b90600052602060002090601f01602090048101928261009057600085556100d7565b82601f106100a957805160ff19168380011785556100d7565b828001600101855582156100d7579182015b828111156100d65782518255916020019190600101906100bb565b5b5090506100e491906100e8565b5090565b5b808211156101015760008160009055506001016100e9565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061014c57607f821691505b602082108114156101605761015f610105565b5b50919050565b6104a8806101756000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80633a525c291461003b5780636b701e0814610059575b600080fd5b610043610075565b604051610050919061025d565b60405180910390f35b610073600480360381019061006e91906103c8565b610107565b005b60606000805461008490610440565b80601f01602080910402602001604051908101604052809291908181526020018280546100b090610440565b80156100fd5780601f106100d2576101008083540402835291602001916100fd565b820191906000526020600020905b8154815290600101906020018083116100e057829003601f168201915b5050505050905090565b806000908051906020019061011d929190610121565b5050565b82805461012d90610440565b90600052602060002090601f01602090048101928261014f5760008555610196565b82601f1061016857805160ff1916838001178555610196565b82800160010185558215610196579182015b8281111561019557825182559160200191906001019061017a565b5b5090506101a391906101a7565b5090565b5b808211156101c05760008160009055506001016101a8565b5090565b600081519050919050565b600082825260208201905092915050565b60005b838110156101fe5780820151818401526020810190506101e3565b8381111561020d576000848401525b50505050565b6000601f19601f8301169050919050565b600061022f826101c4565b61023981856101cf565b93506102498185602086016101e0565b61025281610213565b840191505092915050565b600060208201905081810360008301526102778184610224565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6102d582610213565b810181811067ffffffffffffffff821117156102f4576102f361029d565b5b80604052505050565b600061030761027f565b905061031382826102cc565b919050565b600067ffffffffffffffff8211156103335761033261029d565b5b61033c82610213565b9050602081019050919050565b82818337600083830152505050565b600061036b61036684610318565b6102fd565b90508281526020810184848401111561038757610386610298565b5b610392848285610349565b509392505050565b600082601f8301126103af576103ae610293565b5b81356103bf848260208601610358565b91505092915050565b6000602082840312156103de576103dd610289565b5b600082013567ffffffffffffffff8111156103fc576103fb61028e565b5b6104088482850161039a565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061045857607f821691505b6020821081141561046c5761046b610411565b5b5091905056fea264697066735822122007a3fec27bf391246bb4a62e66c81e304129cd8c6427df54eb8e9cebec9c658f64736f6c634300080a0033"); let mut machine = Machine::new(test_cost_fn); let status = machine.execute(&bytes, Default::default()); assert_eq!(status, ExecutionState::Ok); } #[test] fn test_storage_retrieve() { let bytes = hex!("608060405234801561001057600080fd5b50600436106100365760003560e01c80633a525c291461003b5780636b701e0814610059575b600080fd5b610043610075565b6040516100509190610259565b60405180910390f35b610073600480360381019061006e91906102ea565b610107565b005b60606000805461008490610366565b80601f01602080910402602001604051908101604052809291908181526020018280546100b090610366565b80156100fd5780601f106100d2576101008083540402835291602001916100fd565b820191906000526020600020905b8154815290600101906020018083116100e057829003601f168201915b5050505050905090565b81816000919061011892919061011d565b505050565b82805461012990610366565b90600052602060002090601f01602090048101928261014b5760008555610192565b82601f1061016457803560ff1916838001178555610192565b82800160010185558215610192579182015b82811115610191578235825591602001919060010190610176565b5b50905061019f91906101a3565b5090565b5b808211156101bc5760008160009055506001016101a4565b5090565b600081519050919050565b600082825260208201905092915050565b60005b838110156101fa5780820151818401526020810190506101df565b83811115610209576000848401525b50505050565b6000601f19601f8301169050919050565b600061022b826101c0565b61023581856101cb565b93506102458185602086016101dc565b61024e8161020f565b840191505092915050565b600060208201905081810360008301526102738184610220565b905092915050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126102aa576102a9610285565b5b8235905067ffffffffffffffff8111156102c7576102c661028a565b5b6020830191508360018202830111156102e3576102e261028f565b5b9250929050565b600080602083850312156103015761030061027b565b5b600083013567ffffffffffffffff81111561031f5761031e610280565b5b61032b85828601610294565b92509250509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061037e57607f821691505b6020821081141561039257610391610337565b5b5091905056fea26469706673582212207d39b40255f686f82c889c56bfa8000e6be27070f005d24ec016e786e6ce64fc64736f6c634300080a0033"); let mut machine = Machine::new_with_data(test_cost_fn, hex!("3a525c29").to_vec()); let account = U256::zero(); let mut storage = Storage::new(account); storage.insert( &account, U256::zero(), U256::from( "0x6c6974746c656469767900000000000000000000000000000000000000000014", ), ); machine.set_storage(storage); let status = machine.execute(&bytes, Default::default()); assert_eq!(status, ExecutionState::Ok); assert_eq!(machine.result.len(), 96); let len = U256::from(&machine.result[32..64]).low_u64() as usize; let result_string = &machine.result[64..64 + len]; assert_eq!(std::str::from_utf8(result_string).unwrap(), "littledivy"); } }