diff --git a/Cargo.lock b/Cargo.lock index 49a657806..a000c6c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,7 @@ name = "wasm" version = "0.1.0" dependencies = [ "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-logger 1.8.0", "ethcore-util 1.8.0", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -891,6 +892,7 @@ dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", + "ethcore-logger 1.8.0", "ethcore-util 1.8.0", "ethjson 0.1.0", "evmjit 1.8.0", @@ -3110,7 +3112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-utils" version = "0.1.0" -source = "git+https://github.com/paritytech/wasm-utils#fee06b6d5826c2dc1fc1aa183b0c2c75e3e140c3" +source = "git+https://github.com/paritytech/wasm-utils#9462bcc0680f0ec2c876abdf75bae981dd4344a5" dependencies = [ "clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/evm/Cargo.toml b/ethcore/evm/Cargo.toml index f3bd208ec..2780703da 100644 --- a/ethcore/evm/Cargo.toml +++ b/ethcore/evm/Cargo.toml @@ -15,6 +15,7 @@ log = "0.3" rlp = { path = "../../util/rlp" } vm = { path = "../vm" } parity-wasm = "0.12" +ethcore-logger = { path = "../../logger" } wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } [dev-dependencies] diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index aa4d87697..833b26664 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -24,6 +24,7 @@ extern crate ethjson; extern crate rlp; extern crate parity_wasm; extern crate wasm_utils; +extern crate ethcore_logger; extern crate vm; #[macro_use] diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index a80893ac3..95f3f346f 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -17,204 +17,11 @@ use std::fmt::Debug; use rustc_hex::FromHex; use util::*; -use evm::{self, GasLeft}; -use vm::{ - self, CallType, Schedule, EnvInfo, ActionParams, ActionValue, - ReturnData, Ext, ContractCreateResult, MessageCallResult, - CreateContractAddress, -}; +use vm::{self, ActionParams, ActionValue}; +use vm::tests::{FakeExt, FakeCall, FakeCallType, test_finalize}; use factory::Factory; use vmtype::VMType; -pub struct FakeLogEntry { - topics: Vec, - data: Bytes -} - -#[derive(PartialEq, Eq, Hash, Debug)] -pub enum FakeCallType { - Call, Create -} - -#[derive(PartialEq, Eq, Hash, Debug)] -pub struct FakeCall { - pub call_type: FakeCallType, - pub gas: U256, - pub sender_address: Option
, - pub receive_address: Option
, - pub value: Option, - pub data: Bytes, - pub code_address: Option
, -} - -/// Fake externalities test structure. -/// -/// Can't do recursive calls. -#[derive(Default)] -pub struct FakeExt { - pub store: HashMap, - pub suicides: HashSet
, - pub calls: HashSet, - sstore_clears: usize, - depth: usize, - blockhashes: HashMap, - codes: HashMap>, - logs: Vec, - info: EnvInfo, - schedule: Schedule, - balances: HashMap, -} - -// similar to the normal `finalize` function, but ignoring NeedsReturn. -fn test_finalize(res: Result) -> Result { - match res { - Ok(GasLeft::Known(gas)) => Ok(gas), - Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented. - Err(e) => Err(e), - } -} - -impl FakeExt { - pub fn new() -> Self { - FakeExt::default() - } -} - -impl Ext for FakeExt { - fn storage_at(&self, key: &H256) -> vm::Result { - Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) - } - - fn set_storage(&mut self, key: H256, value: H256) -> vm::Result<()> { - self.store.insert(key, value); - Ok(()) - } - - fn exists(&self, address: &Address) -> vm::Result { - Ok(self.balances.contains_key(address)) - } - - fn exists_and_not_null(&self, address: &Address) -> vm::Result { - Ok(self.balances.get(address).map_or(false, |b| !b.is_zero())) - } - - fn origin_balance(&self) -> vm::Result { - unimplemented!() - } - - fn balance(&self, address: &Address) -> vm::Result { - Ok(self.balances[address]) - } - - fn blockhash(&mut self, number: &U256) -> H256 { - self.blockhashes.get(number).unwrap_or(&H256::new()).clone() - } - - fn create(&mut self, gas: &U256, value: &U256, code: &[u8], _address: CreateContractAddress) -> ContractCreateResult { - self.calls.insert(FakeCall { - call_type: FakeCallType::Create, - gas: *gas, - sender_address: None, - receive_address: None, - value: Some(*value), - data: code.to_vec(), - code_address: None - }); - ContractCreateResult::Failed - } - - fn call(&mut self, - gas: &U256, - sender_address: &Address, - receive_address: &Address, - value: Option, - data: &[u8], - code_address: &Address, - _output: &mut [u8], - _call_type: CallType - ) -> MessageCallResult { - - self.calls.insert(FakeCall { - call_type: FakeCallType::Call, - gas: *gas, - sender_address: Some(sender_address.clone()), - receive_address: Some(receive_address.clone()), - value: value, - data: data.to_vec(), - code_address: Some(code_address.clone()) - }); - MessageCallResult::Success(*gas, ReturnData::empty()) - } - - fn extcode(&self, address: &Address) -> vm::Result> { - Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone()) - } - - fn extcodesize(&self, address: &Address) -> vm::Result { - Ok(self.codes.get(address).map_or(0, |c| c.len())) - } - - fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { - self.logs.push(FakeLogEntry { - topics: topics, - data: data.to_vec() - }); - Ok(()) - } - - fn ret(self, _gas: &U256, _data: &ReturnData) -> vm::Result { - unimplemented!(); - } - - fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> { - self.suicides.insert(refund_address.clone()); - Ok(()) - } - - fn schedule(&self) -> &Schedule { - &self.schedule - } - - fn env_info(&self) -> &EnvInfo { - &self.info - } - - fn depth(&self) -> usize { - self.depth - } - - fn inc_sstore_clears(&mut self) { - self.sstore_clears += 1; - } -} - -#[test] -fn test_stack_underflow() { - let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "01600055".from_hex().unwrap(); - - let mut params = ActionParams::default(); - params.address = address.clone(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - let mut ext = FakeExt::new(); - - let err = { - let mut vm : Box = Box::new(super::interpreter::Interpreter::::new(Arc::new(super::interpreter::SharedCache::default()))); - test_finalize(vm.exec(params, &mut ext)).unwrap_err() - }; - - match err { - vm::Error::StackUnderflow {wanted, on_stack, ..} => { - assert_eq!(wanted, 2); - assert_eq!(on_stack, 0); - } - _ => { - assert!(false, "Expected StackUndeflow") - } - }; -} - evm_test!{test_add: test_add_jit, test_add_int} fn test_add(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index 2bf669951..0c9e32dc6 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -29,6 +29,8 @@ mod ext; mod return_data; mod error; +pub mod tests; + pub use action_params::{ActionParams, ActionValue}; pub use call_type::CallType; pub use env_info::{EnvInfo, LastHashes}; diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs new file mode 100644 index 000000000..c8afedaff --- /dev/null +++ b/ethcore/vm/src/tests.rs @@ -0,0 +1,187 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::Arc; +use std::collections::{HashMap, HashSet}; + +use util::{H256, U256, Address, Bytes}; +use { + CallType, Schedule, EnvInfo, + ReturnData, Ext, ContractCreateResult, MessageCallResult, + CreateContractAddress, Result, GasLeft, +}; + +pub struct FakeLogEntry { + pub topics: Vec, + pub data: Bytes +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub enum FakeCallType { + Call, Create +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub struct FakeCall { + pub call_type: FakeCallType, + pub gas: U256, + pub sender_address: Option
, + pub receive_address: Option
, + pub value: Option, + pub data: Bytes, + pub code_address: Option
, +} + +/// Fake externalities test structure. +/// +/// Can't do recursive calls. +#[derive(Default)] +pub struct FakeExt { + pub store: HashMap, + pub suicides: HashSet
, + pub calls: HashSet, + pub sstore_clears: usize, + pub depth: usize, + pub blockhashes: HashMap, + pub codes: HashMap>, + pub logs: Vec, + pub info: EnvInfo, + pub schedule: Schedule, + pub balances: HashMap, +} + +// similar to the normal `finalize` function, but ignoring NeedsReturn. +pub fn test_finalize(res: Result) -> Result { + match res { + Ok(GasLeft::Known(gas)) => Ok(gas), + Ok(GasLeft::NeedsReturn{..}) => unimplemented!(), // since ret is unimplemented. + Err(e) => Err(e), + } +} + +impl FakeExt { + pub fn new() -> Self { + FakeExt::default() + } +} + +impl Ext for FakeExt { + fn storage_at(&self, key: &H256) -> Result { + Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) + } + + fn set_storage(&mut self, key: H256, value: H256) -> Result<()> { + self.store.insert(key, value); + Ok(()) + } + + fn exists(&self, address: &Address) -> Result { + Ok(self.balances.contains_key(address)) + } + + fn exists_and_not_null(&self, address: &Address) -> Result { + Ok(self.balances.get(address).map_or(false, |b| !b.is_zero())) + } + + fn origin_balance(&self) -> Result { + unimplemented!() + } + + fn balance(&self, address: &Address) -> Result { + Ok(self.balances[address]) + } + + fn blockhash(&mut self, number: &U256) -> H256 { + self.blockhashes.get(number).unwrap_or(&H256::new()).clone() + } + + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], _address: CreateContractAddress) -> ContractCreateResult { + self.calls.insert(FakeCall { + call_type: FakeCallType::Create, + gas: *gas, + sender_address: None, + receive_address: None, + value: Some(*value), + data: code.to_vec(), + code_address: None + }); + ContractCreateResult::Failed + } + + fn call(&mut self, + gas: &U256, + sender_address: &Address, + receive_address: &Address, + value: Option, + data: &[u8], + code_address: &Address, + _output: &mut [u8], + _call_type: CallType + ) -> MessageCallResult { + + self.calls.insert(FakeCall { + call_type: FakeCallType::Call, + gas: *gas, + sender_address: Some(sender_address.clone()), + receive_address: Some(receive_address.clone()), + value: value, + data: data.to_vec(), + code_address: Some(code_address.clone()) + }); + MessageCallResult::Success(*gas, ReturnData::empty()) + } + + fn extcode(&self, address: &Address) -> Result> { + Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone()) + } + + fn extcodesize(&self, address: &Address) -> Result { + Ok(self.codes.get(address).map_or(0, |c| c.len())) + } + + fn log(&mut self, topics: Vec, data: &[u8]) -> Result<()> { + self.logs.push(FakeLogEntry { + topics: topics, + data: data.to_vec() + }); + Ok(()) + } + + fn ret(self, _gas: &U256, _data: &ReturnData) -> Result { + unimplemented!(); + } + + fn suicide(&mut self, refund_address: &Address) -> Result<()> { + self.suicides.insert(refund_address.clone()); + Ok(()) + } + + fn schedule(&self) -> &Schedule { + &self.schedule + } + + fn env_info(&self) -> &EnvInfo { + &self.info + } + + fn depth(&self) -> usize { + self.depth + } + + fn inc_sstore_clears(&mut self) { + self.sstore_clears += 1; + } +} diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index bda35e3ef..bbeeeffc5 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -9,4 +9,5 @@ ethcore-util = { path = "../../util" } log = "0.3" parity-wasm = "0.12" wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } -vm = { path = "../vm" } \ No newline at end of file +vm = { path = "../vm" } +ethcore-logger = { path = "../../logger" } \ No newline at end of file diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index 00770bb4a..cb7462046 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! Wasm env module bindings +//! Wasm env module bindings use parity_wasm::elements::ValueType::*; use parity_wasm::interpreter::UserFunctionDescriptor; @@ -61,6 +61,21 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[ &[I32; 4], Some(I32), ), + Static( + "_ccall", + &[I32; 6], + Some(I32), + ), + Static( + "_dcall", + &[I32; 5], + Some(I32), + ), + Static( + "_scall", + &[I32; 5], + Some(I32), + ), Static( "abort", &[I32], @@ -72,50 +87,34 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[ None, ), Static( - "invoke_vii", - &[I32; 3], - None, - ), - Static( - "invoke_vi", - &[I32; 2], - None, - ), - Static( - "invoke_v", - &[I32], - None, - ), - Static( - "invoke_iii", - &[I32; 3], - Some(I32), - ), - Static( - "___resumeException", - &[I32], - None, + "abortOnCannotGrowMemory", + &[I32; 0], + Some(I32) ), + + /* + THIS IS EXPERIMENTAL RUST-ONLY RUNTIME EXTERNS, THEY ARE SUBJECT TO CHANGE + + AVOID YOUR WASM CONTAINS ANY OF THESE OTHERWISE + EITHER FACE THE NEED OF HARDFORK + OR YOU CAN STUCK ON SPECIFIC RUST VERSION FOR WASM COMPILATION + */ + Static( "_rust_begin_unwind", &[I32; 4], None, ), - Static( - "___cxa_find_matching_catch_2", - &[], - Some(I32), - ), - Static( - "___gxx_personality_v0", - &[I32; 6], - Some(I32), - ), Static( "_emscripten_memcpy_big", &[I32; 3], Some(I32), ), + Static( + "___syscall6", + &[I32; 2], + Some(I32), + ), Static( "___syscall140", &[I32; 2], @@ -131,21 +130,11 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[ &[I32; 2], Some(I32) ), - Static( - "___syscall6", - &[I32; 2], - Some(I32) - ), Static( "_llvm_trap", &[I32; 0], None ), - Static( - "abortOnCannotGrowMemory", - &[I32; 0], - Some(I32) - ), Static( "___setErrNo", &[I32; 1], diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 9ca3869ef..ec6e67405 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -19,6 +19,7 @@ extern crate vm; extern crate ethcore_util as util; #[macro_use] extern crate log; +extern crate ethcore_logger; extern crate byteorder; extern crate parity_wasm; extern crate wasm_utils; @@ -39,7 +40,7 @@ use parity_wasm::{interpreter, elements}; use parity_wasm::interpreter::ModuleInstanceInterface; use vm::{GasLeft, ReturnData, ActionParams}; -use self::runtime::Runtime; +use self::runtime::{Runtime, RuntimeContext}; pub use self::runtime::Error as RuntimeError; @@ -87,6 +88,7 @@ impl vm::Vm for WasmInterpreter { env_memory, DEFAULT_STACK_SPACE, params.gas.low_u64(), + RuntimeContext::new(params.address, params.sender), ); let mut cursor = ::std::io::Cursor::new(&*code); diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 5c6a4a626..a983c954f 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -24,6 +24,7 @@ use vm; use parity_wasm::interpreter; use util::{Address, H256, U256}; +use vm::CallType; use super::ptr::{WasmPtr, Error as PtrError}; use super::call_args::CallArgs; @@ -56,6 +57,20 @@ impl From for Error { } } +pub struct RuntimeContext { + address: Address, + sender: Address, +} + +impl RuntimeContext { + pub fn new(address: Address, sender: Address) -> Self { + RuntimeContext { + address: address, + sender: sender, + } + } +} + /// Runtime enviroment data for wasm contract execution pub struct Runtime<'a> { gas_counter: u64, @@ -63,6 +78,7 @@ pub struct Runtime<'a> { dynamic_top: u32, ext: &'a mut vm::Ext, memory: Arc, + context: RuntimeContext, } impl<'a> Runtime<'a> { @@ -72,6 +88,7 @@ impl<'a> Runtime<'a> { memory: Arc, stack_space: u32, gas_limit: u64, + context: RuntimeContext, ) -> Runtime<'b> { Runtime { gas_counter: 0, @@ -79,6 +96,7 @@ impl<'a> Runtime<'a> { dynamic_top: stack_space, memory: memory, ext: ext, + context: context, } } @@ -138,13 +156,13 @@ impl<'a> Runtime<'a> { trace!(target: "wasm", "runtime: create contract"); let mut context = context; let result_ptr = context.value_stack.pop_as::()? as u32; - trace!(target: "wasm", " result_ptr: {:?}", result_ptr); + trace!(target: "wasm", "result_ptr: {:?}", result_ptr); let code_len = context.value_stack.pop_as::()? as u32; - trace!(target: "wasm", " code_len: {:?}", code_len); + trace!(target: "wasm", " code_len: {:?}", code_len); let code_ptr = context.value_stack.pop_as::()? as u32; - trace!(target: "wasm", " code_ptr: {:?}", code_ptr); + trace!(target: "wasm", " code_ptr: {:?}", code_ptr); let endowment = self.pop_u256(&mut context)?; - trace!(target: "wasm", " val: {:?}", endowment); + trace!(target: "wasm", " val: {:?}", endowment); let code = self.memory.get(code_ptr, code_len as usize)?; @@ -166,6 +184,127 @@ impl<'a> Runtime<'a> { } } + pub fn call(&mut self, context: interpreter::CallerContext) + -> Result, interpreter::Error> + { + // + // method signature: + // fn ( + // address: *const u8, + // val_ptr: *const u8, + // input_ptr: *const u8, + // input_len: u32, + // result_ptr: *mut u8, + // result_len: u32, + // ) -> i32 + + self.do_call(true, CallType::Call, context) + } + + + fn call_code(&mut self, context: interpreter::CallerContext) + -> Result, interpreter::Error> + { + // + // signature (same as static call): + // fn ( + // address: *const u8, + // input_ptr: *const u8, + // input_len: u32, + // result_ptr: *mut u8, + // result_len: u32, + // ) -> i32 + + self.do_call(false, CallType::CallCode, context) + } + + fn do_call( + &mut self, + use_val: bool, + call_type: CallType, + context: interpreter::CallerContext, + ) + -> Result, interpreter::Error> + { + + trace!(target: "wasm", "runtime: call code"); + let mut context = context; + let result_alloc_len = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " result_len: {:?}", result_alloc_len); + + let result_ptr = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " result_ptr: {:?}", result_ptr); + + let input_len = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " input_len: {:?}", input_len); + + let input_ptr = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " input_ptr: {:?}", input_ptr); + + let val = if use_val { Some(self.pop_u256(&mut context)?) } + else { None }; + trace!(target: "wasm", " val: {:?}", val); + + let address = self.pop_address(&mut context)?; + trace!(target: "wasm", " address: {:?}", address); + + if let Some(ref val) = val { + let address_balance = self.ext.balance(&self.context.address) + .map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?; + + if &address_balance < val { + trace!(target: "wasm", "runtime: call failed due to balance check"); + return Ok(Some((-1i32).into())); + } + } + + let mut result = Vec::with_capacity(result_alloc_len as usize); + result.resize(result_alloc_len as usize, 0); + let gas = self.gas_left() + .map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))? + .into(); + // todo: optimize to use memory views once it's in + let payload = self.memory.get(input_ptr, input_len as usize)?; + + let call_result = self.ext.call( + &gas, + &self.context.sender, + &self.context.address, + val, + &payload, + &address, + &mut result[..], + call_type, + ); + + match call_result { + vm::MessageCallResult::Success(gas_left, _) => { + self.gas_counter = self.gas_limit - gas_left.low_u64(); + self.memory.set(result_ptr, &result)?; + Ok(Some(0i32.into())) + }, + vm::MessageCallResult::Failed => { + Ok(Some((-1i32).into())) + } + } + } + + pub fn static_call(&mut self, context: interpreter::CallerContext) + -> Result, interpreter::Error> + { + // signature (same as code call): + // fn ( + // address: *const u8, + // input_ptr: *const u8, + // input_len: u32, + // result_ptr: *mut u8, + // result_len: u32, + // ) -> i32 + + self.do_call(false, CallType::StaticCall, context) + } + + /// Allocate memory using the wasm stack params pub fn malloc(&mut self, context: interpreter::CallerContext) -> Result, interpreter::Error> @@ -337,6 +476,15 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> { "_create" => { self.create(context) }, + "_ccall" => { + self.call(context) + }, + "_dcall" => { + self.call_code(context) + }, + "_scall" => { + self.static_call(context) + }, "_debug" => { self.debug_log(context) }, @@ -347,7 +495,7 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> { self.mem_copy(context) }, _ => { - trace!("Unknown env func: '{}'", name); + trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name); self.user_trap(context) } } diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index f856ae767..c12dc8935 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -15,16 +15,16 @@ // along with Parity. If not, see . use std::sync::Arc; - -use super::super::tests::{FakeExt, FakeCall, FakeCallType}; -use super::WasmInterpreter; -use evm::{self, Evm, GasLeft}; -use action_params::{ActionParams, ActionValue}; +use byteorder::{LittleEndian, ByteOrder}; use util::{U256, H256, Address}; +use super::WasmInterpreter; +use vm::{self, Vm, GasLeft, ActionParams, ActionValue}; +use vm::tests::{FakeCall, FakeExt, FakeCallType}; + macro_rules! load_sample { ($name: expr) => { - include_bytes!(concat!("../../../res/wasm-tests/compiled/", $name)).to_vec() + include_bytes!(concat!("../../res/wasm-tests/compiled/", $name)).to_vec() } } @@ -85,7 +85,7 @@ fn logger() { }; println!("ext.store: {:?}", ext.store); - assert_eq!(gas_left, U256::from(99581)); + assert_eq!(gas_left, U256::from(99590)); let address_val: H256 = address.into(); assert_eq!( ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"), @@ -136,7 +136,7 @@ fn identity() { } }; - assert_eq!(gas_left, U256::from(99_689)); + assert_eq!(gas_left, U256::from(99_687)); assert_eq!( Address::from_slice(&result), @@ -170,7 +170,7 @@ fn dispersion() { } }; - assert_eq!(gas_left, U256::from(99_402)); + assert_eq!(gas_left, U256::from(99_423)); assert_eq!( result, @@ -199,7 +199,7 @@ fn suicide_not() { } }; - assert_eq!(gas_left, U256::from(99_703)); + assert_eq!(gas_left, U256::from(99_656)); assert_eq!( result, @@ -233,12 +233,14 @@ fn suicide() { } }; - assert_eq!(gas_left, U256::from(99_747)); + assert_eq!(gas_left, U256::from(99_740)); assert!(ext.suicides.contains(&refund)); } #[test] fn create() { + ::ethcore_logger::init_log(); + let mut params = ActionParams::default(); params.gas = U256::from(100_000); params.code = Some(Arc::new(load_sample!("creator.wasm"))); @@ -262,7 +264,7 @@ fn create() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Create, - gas: U256::from(99_778), + gas: U256::from(99_767), sender_address: None, receive_address: None, value: Some(1_000_000_000.into()), @@ -270,29 +272,119 @@ fn create() { code_address: None, } )); - assert_eq!(gas_left, U256::from(99_768)); + assert_eq!(gas_left, U256::from(99_759)); +} + + +#[test] +fn call_code() { + ::ethcore_logger::init_log(); + + let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call_code.wasm"))); + params.data = Some(Vec::new()); + params.value = ActionValue::transfer(1_000_000_000); + + let mut ext = FakeExt::new(); + + let (gas_left, result) = { + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { panic!("Call test should return payload"); }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains( + &FakeCall { + call_type: FakeCallType::Call, + gas: U256::from(99_061), + sender_address: Some(sender), + receive_address: Some(receiver), + value: None, + data: vec![1u8, 2, 3, 5, 7, 11], + code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()), + } + )); + assert_eq!(gas_left, U256::from(94196)); + + // siphash result + let res = LittleEndian::read_u32(&result[..]); + assert_eq!(res, 4198595614); +} + +#[test] +fn call_static() { + ::ethcore_logger::init_log(); + + let sender: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap(); + let receiver: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap(); + + let mut params = ActionParams::default(); + params.sender = sender.clone(); + params.address = receiver.clone(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(load_sample!("call_static.wasm"))); + params.data = Some(Vec::new()); + params.value = ActionValue::transfer(1_000_000_000); + + let mut ext = FakeExt::new(); + + let (gas_left, result) = { + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { panic!("Static call test should return payload"); }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + } + }; + + trace!(target: "wasm", "fake_calls: {:?}", &ext.calls); + assert!(ext.calls.contains( + &FakeCall { + call_type: FakeCallType::Call, + gas: U256::from(99_061), + sender_address: Some(sender), + receive_address: Some(receiver), + value: None, + data: vec![1u8, 2, 3, 5, 7, 11], + code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), + } + )); + assert_eq!(gas_left, U256::from(94196)); + + // siphash result + let res = LittleEndian::read_u32(&result[..]); + assert_eq!(res, 317632590); } // Realloc test #[test] fn realloc() { - let code = load_sample!("realloc.wasm"); + let code = load_sample!("realloc.wasm"); - let mut params = ActionParams::default(); - params.gas = U256::from(100_000); - params.code = Some(Arc::new(code)); - params.data = Some(vec![0u8]); - let mut ext = FakeExt::new(); - - let (gas_left, result) = { - let mut interpreter = wasm_interpreter(); - let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); - match result { - GasLeft::Known(_) => { panic!("Realloc should return payload"); }, - GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), - } - }; - assert_eq!(gas_left, U256::from(98326)); - assert_eq!(result, vec![0u8; 2]); + let mut params = ActionParams::default(); + params.gas = U256::from(100_000); + params.code = Some(Arc::new(code)); + params.data = Some(vec![0u8]); + let mut ext = FakeExt::new(); + let (gas_left, result) = { + let mut interpreter = wasm_interpreter(); + let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); + match result { + GasLeft::Known(_) => { panic!("Realloc should return payload"); }, + GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), + } + }; + assert_eq!(gas_left, U256::from(98326)); + assert_eq!(result, vec![0u8; 2]); } diff --git a/scripts/targets.sh b/scripts/targets.sh index fb10c43f2..6ea74c23e 100755 --- a/scripts/targets.sh +++ b/scripts/targets.sh @@ -19,4 +19,6 @@ export TARGETS=" -p ethcore-ipc-tests \ -p ethcore-ipc-nano \ -p ethcore-light \ + -p wasm \ + -p evm \ -p parity"