Merge pull request #6132 from paritytech/wasm-mvp

WASM MVP continued
This commit is contained in:
Marek Kotewicz 2017-08-01 14:08:58 +02:00 committed by GitHub
commit 5b0eeb75ef
12 changed files with 511 additions and 277 deletions

4
Cargo.lock generated
View File

@ -3,6 +3,7 @@ name = "wasm"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-logger 1.8.0",
"ethcore-util 1.8.0", "ethcore-util 1.8.0",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"common-types 0.1.0", "common-types 0.1.0",
"ethcore-logger 1.8.0",
"ethcore-util 1.8.0", "ethcore-util 1.8.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"evmjit 1.8.0", "evmjit 1.8.0",
@ -3110,7 +3112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "wasm-utils" name = "wasm-utils"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/paritytech/wasm-utils#fee06b6d5826c2dc1fc1aa183b0c2c75e3e140c3" source = "git+https://github.com/paritytech/wasm-utils#9462bcc0680f0ec2c876abdf75bae981dd4344a5"
dependencies = [ dependencies = [
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -15,6 +15,7 @@ log = "0.3"
rlp = { path = "../../util/rlp" } rlp = { path = "../../util/rlp" }
vm = { path = "../vm" } vm = { path = "../vm" }
parity-wasm = "0.12" parity-wasm = "0.12"
ethcore-logger = { path = "../../logger" }
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
[dev-dependencies] [dev-dependencies]

View File

@ -24,6 +24,7 @@ extern crate ethjson;
extern crate rlp; extern crate rlp;
extern crate parity_wasm; extern crate parity_wasm;
extern crate wasm_utils; extern crate wasm_utils;
extern crate ethcore_logger;
extern crate vm; extern crate vm;
#[macro_use] #[macro_use]

View File

@ -17,204 +17,11 @@
use std::fmt::Debug; use std::fmt::Debug;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use util::*; use util::*;
use evm::{self, GasLeft}; use vm::{self, ActionParams, ActionValue};
use vm::{ use vm::tests::{FakeExt, FakeCall, FakeCallType, test_finalize};
self, CallType, Schedule, EnvInfo, ActionParams, ActionValue,
ReturnData, Ext, ContractCreateResult, MessageCallResult,
CreateContractAddress,
};
use factory::Factory; use factory::Factory;
use vmtype::VMType; use vmtype::VMType;
pub struct FakeLogEntry {
topics: Vec<H256>,
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<Address>,
pub receive_address: Option<Address>,
pub value: Option<U256>,
pub data: Bytes,
pub code_address: Option<Address>,
}
/// Fake externalities test structure.
///
/// Can't do recursive calls.
#[derive(Default)]
pub struct FakeExt {
pub store: HashMap<H256, H256>,
pub suicides: HashSet<Address>,
pub calls: HashSet<FakeCall>,
sstore_clears: usize,
depth: usize,
blockhashes: HashMap<U256, H256>,
codes: HashMap<Address, Arc<Bytes>>,
logs: Vec<FakeLogEntry>,
info: EnvInfo,
schedule: Schedule,
balances: HashMap<Address, U256>,
}
// similar to the normal `finalize` function, but ignoring NeedsReturn.
fn test_finalize(res: Result<GasLeft, vm::Error>) -> Result<U256, vm::Error> {
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<H256> {
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<bool> {
Ok(self.balances.contains_key(address))
}
fn exists_and_not_null(&self, address: &Address) -> vm::Result<bool> {
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
}
fn origin_balance(&self) -> vm::Result<U256> {
unimplemented!()
}
fn balance(&self, address: &Address) -> vm::Result<U256> {
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<U256>,
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<Arc<Bytes>> {
Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone())
}
fn extcodesize(&self, address: &Address) -> vm::Result<usize> {
Ok(self.codes.get(address).map_or(0, |c| c.len()))
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> vm::Result<()> {
self.logs.push(FakeLogEntry {
topics: topics,
data: data.to_vec()
});
Ok(())
}
fn ret(self, _gas: &U256, _data: &ReturnData) -> vm::Result<U256> {
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<evm::Evm> = Box::new(super::interpreter::Interpreter::<usize>::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} evm_test!{test_add: test_add_jit, test_add_int}
fn test_add(factory: super::Factory) { fn test_add(factory: super::Factory) {
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();

View File

@ -29,6 +29,8 @@ mod ext;
mod return_data; mod return_data;
mod error; mod error;
pub mod tests;
pub use action_params::{ActionParams, ActionValue}; pub use action_params::{ActionParams, ActionValue};
pub use call_type::CallType; pub use call_type::CallType;
pub use env_info::{EnvInfo, LastHashes}; pub use env_info::{EnvInfo, LastHashes};

187
ethcore/vm/src/tests.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<H256>,
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<Address>,
pub receive_address: Option<Address>,
pub value: Option<U256>,
pub data: Bytes,
pub code_address: Option<Address>,
}
/// Fake externalities test structure.
///
/// Can't do recursive calls.
#[derive(Default)]
pub struct FakeExt {
pub store: HashMap<H256, H256>,
pub suicides: HashSet<Address>,
pub calls: HashSet<FakeCall>,
pub sstore_clears: usize,
pub depth: usize,
pub blockhashes: HashMap<U256, H256>,
pub codes: HashMap<Address, Arc<Bytes>>,
pub logs: Vec<FakeLogEntry>,
pub info: EnvInfo,
pub schedule: Schedule,
pub balances: HashMap<Address, U256>,
}
// similar to the normal `finalize` function, but ignoring NeedsReturn.
pub fn test_finalize(res: Result<GasLeft>) -> Result<U256> {
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<H256> {
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<bool> {
Ok(self.balances.contains_key(address))
}
fn exists_and_not_null(&self, address: &Address) -> Result<bool> {
Ok(self.balances.get(address).map_or(false, |b| !b.is_zero()))
}
fn origin_balance(&self) -> Result<U256> {
unimplemented!()
}
fn balance(&self, address: &Address) -> Result<U256> {
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<U256>,
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<Arc<Bytes>> {
Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone())
}
fn extcodesize(&self, address: &Address) -> Result<usize> {
Ok(self.codes.get(address).map_or(0, |c| c.len()))
}
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()> {
self.logs.push(FakeLogEntry {
topics: topics,
data: data.to_vec()
});
Ok(())
}
fn ret(self, _gas: &U256, _data: &ReturnData) -> Result<U256> {
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;
}
}

View File

@ -10,3 +10,4 @@ log = "0.3"
parity-wasm = "0.12" parity-wasm = "0.12"
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
vm = { path = "../vm" } vm = { path = "../vm" }
ethcore-logger = { path = "../../logger" }

View File

@ -61,6 +61,21 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[I32; 4], &[I32; 4],
Some(I32), Some(I32),
), ),
Static(
"_ccall",
&[I32; 6],
Some(I32),
),
Static(
"_dcall",
&[I32; 5],
Some(I32),
),
Static(
"_scall",
&[I32; 5],
Some(I32),
),
Static( Static(
"abort", "abort",
&[I32], &[I32],
@ -72,50 +87,34 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
None, None,
), ),
Static( Static(
"invoke_vii", "abortOnCannotGrowMemory",
&[I32; 3], &[I32; 0],
None, Some(I32)
),
Static(
"invoke_vi",
&[I32; 2],
None,
),
Static(
"invoke_v",
&[I32],
None,
),
Static(
"invoke_iii",
&[I32; 3],
Some(I32),
),
Static(
"___resumeException",
&[I32],
None,
), ),
/*
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( Static(
"_rust_begin_unwind", "_rust_begin_unwind",
&[I32; 4], &[I32; 4],
None, None,
), ),
Static(
"___cxa_find_matching_catch_2",
&[],
Some(I32),
),
Static(
"___gxx_personality_v0",
&[I32; 6],
Some(I32),
),
Static( Static(
"_emscripten_memcpy_big", "_emscripten_memcpy_big",
&[I32; 3], &[I32; 3],
Some(I32), Some(I32),
), ),
Static(
"___syscall6",
&[I32; 2],
Some(I32),
),
Static( Static(
"___syscall140", "___syscall140",
&[I32; 2], &[I32; 2],
@ -131,21 +130,11 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[I32; 2], &[I32; 2],
Some(I32) Some(I32)
), ),
Static(
"___syscall6",
&[I32; 2],
Some(I32)
),
Static( Static(
"_llvm_trap", "_llvm_trap",
&[I32; 0], &[I32; 0],
None None
), ),
Static(
"abortOnCannotGrowMemory",
&[I32; 0],
Some(I32)
),
Static( Static(
"___setErrNo", "___setErrNo",
&[I32; 1], &[I32; 1],

View File

@ -19,6 +19,7 @@
extern crate vm; extern crate vm;
extern crate ethcore_util as util; extern crate ethcore_util as util;
#[macro_use] extern crate log; #[macro_use] extern crate log;
extern crate ethcore_logger;
extern crate byteorder; extern crate byteorder;
extern crate parity_wasm; extern crate parity_wasm;
extern crate wasm_utils; extern crate wasm_utils;
@ -39,7 +40,7 @@ use parity_wasm::{interpreter, elements};
use parity_wasm::interpreter::ModuleInstanceInterface; use parity_wasm::interpreter::ModuleInstanceInterface;
use vm::{GasLeft, ReturnData, ActionParams}; use vm::{GasLeft, ReturnData, ActionParams};
use self::runtime::Runtime; use self::runtime::{Runtime, RuntimeContext};
pub use self::runtime::Error as RuntimeError; pub use self::runtime::Error as RuntimeError;
@ -87,6 +88,7 @@ impl vm::Vm for WasmInterpreter {
env_memory, env_memory,
DEFAULT_STACK_SPACE, DEFAULT_STACK_SPACE,
params.gas.low_u64(), params.gas.low_u64(),
RuntimeContext::new(params.address, params.sender),
); );
let mut cursor = ::std::io::Cursor::new(&*code); let mut cursor = ::std::io::Cursor::new(&*code);

View File

@ -24,6 +24,7 @@ use vm;
use parity_wasm::interpreter; use parity_wasm::interpreter;
use util::{Address, H256, U256}; use util::{Address, H256, U256};
use vm::CallType;
use super::ptr::{WasmPtr, Error as PtrError}; use super::ptr::{WasmPtr, Error as PtrError};
use super::call_args::CallArgs; use super::call_args::CallArgs;
@ -56,6 +57,20 @@ impl From<PtrError> 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 /// Runtime enviroment data for wasm contract execution
pub struct Runtime<'a> { pub struct Runtime<'a> {
gas_counter: u64, gas_counter: u64,
@ -63,6 +78,7 @@ pub struct Runtime<'a> {
dynamic_top: u32, dynamic_top: u32,
ext: &'a mut vm::Ext, ext: &'a mut vm::Ext,
memory: Arc<interpreter::MemoryInstance>, memory: Arc<interpreter::MemoryInstance>,
context: RuntimeContext,
} }
impl<'a> Runtime<'a> { impl<'a> Runtime<'a> {
@ -72,6 +88,7 @@ impl<'a> Runtime<'a> {
memory: Arc<interpreter::MemoryInstance>, memory: Arc<interpreter::MemoryInstance>,
stack_space: u32, stack_space: u32,
gas_limit: u64, gas_limit: u64,
context: RuntimeContext,
) -> Runtime<'b> { ) -> Runtime<'b> {
Runtime { Runtime {
gas_counter: 0, gas_counter: 0,
@ -79,6 +96,7 @@ impl<'a> Runtime<'a> {
dynamic_top: stack_space, dynamic_top: stack_space,
memory: memory, memory: memory,
ext: ext, ext: ext,
context: context,
} }
} }
@ -138,13 +156,13 @@ impl<'a> Runtime<'a> {
trace!(target: "wasm", "runtime: create contract"); trace!(target: "wasm", "runtime: create contract");
let mut context = context; let mut context = context;
let result_ptr = context.value_stack.pop_as::<i32>()? as u32; let result_ptr = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " result_ptr: {:?}", result_ptr); trace!(target: "wasm", "result_ptr: {:?}", result_ptr);
let code_len = context.value_stack.pop_as::<i32>()? as u32; let code_len = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " code_len: {:?}", code_len); trace!(target: "wasm", " code_len: {:?}", code_len);
let code_ptr = context.value_stack.pop_as::<i32>()? as u32; let code_ptr = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " code_ptr: {:?}", code_ptr); trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
let endowment = self.pop_u256(&mut context)?; 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)?; 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<Option<interpreter::RuntimeValue>, 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<Option<interpreter::RuntimeValue>, 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<Option<interpreter::RuntimeValue>, interpreter::Error>
{
trace!(target: "wasm", "runtime: call code");
let mut context = context;
let result_alloc_len = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " result_len: {:?}", result_alloc_len);
let result_ptr = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " result_ptr: {:?}", result_ptr);
let input_len = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " input_len: {:?}", input_len);
let input_ptr = context.value_stack.pop_as::<i32>()? 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<Option<interpreter::RuntimeValue>, 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 /// Allocate memory using the wasm stack params
pub fn malloc(&mut self, context: interpreter::CallerContext) pub fn malloc(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error> -> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
@ -337,6 +476,15 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
"_create" => { "_create" => {
self.create(context) self.create(context)
}, },
"_ccall" => {
self.call(context)
},
"_dcall" => {
self.call_code(context)
},
"_scall" => {
self.static_call(context)
},
"_debug" => { "_debug" => {
self.debug_log(context) self.debug_log(context)
}, },
@ -347,7 +495,7 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
self.mem_copy(context) self.mem_copy(context)
}, },
_ => { _ => {
trace!("Unknown env func: '{}'", name); trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
self.user_trap(context) self.user_trap(context)
} }
} }

View File

@ -15,16 +15,16 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use std::sync::Arc;
use byteorder::{LittleEndian, ByteOrder};
use super::super::tests::{FakeExt, FakeCall, FakeCallType};
use super::WasmInterpreter;
use evm::{self, Evm, GasLeft};
use action_params::{ActionParams, ActionValue};
use util::{U256, H256, Address}; 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 { macro_rules! load_sample {
($name: expr) => { ($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); 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(); let address_val: H256 = address.into();
assert_eq!( assert_eq!(
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"), 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!( assert_eq!(
Address::from_slice(&result), 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!( assert_eq!(
result, 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!( assert_eq!(
result, 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)); assert!(ext.suicides.contains(&refund));
} }
#[test] #[test]
fn create() { fn create() {
::ethcore_logger::init_log();
let mut params = ActionParams::default(); let mut params = ActionParams::default();
params.gas = U256::from(100_000); params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("creator.wasm"))); params.code = Some(Arc::new(load_sample!("creator.wasm")));
@ -262,7 +264,7 @@ fn create() {
assert!(ext.calls.contains( assert!(ext.calls.contains(
&FakeCall { &FakeCall {
call_type: FakeCallType::Create, call_type: FakeCallType::Create,
gas: U256::from(99_778), gas: U256::from(99_767),
sender_address: None, sender_address: None,
receive_address: None, receive_address: None,
value: Some(1_000_000_000.into()), value: Some(1_000_000_000.into()),
@ -270,29 +272,119 @@ fn create() {
code_address: None, 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 // Realloc test
#[test] #[test]
fn realloc() { fn realloc() {
let code = load_sample!("realloc.wasm"); let code = load_sample!("realloc.wasm");
let mut params = ActionParams::default(); let mut params = ActionParams::default();
params.gas = U256::from(100_000); params.gas = U256::from(100_000);
params.code = Some(Arc::new(code)); params.code = Some(Arc::new(code));
params.data = Some(vec![0u8]); params.data = Some(vec![0u8]);
let mut ext = FakeExt::new(); 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 (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]);
} }

View File

@ -19,4 +19,6 @@ export TARGETS="
-p ethcore-ipc-tests \ -p ethcore-ipc-tests \
-p ethcore-ipc-nano \ -p ethcore-ipc-nano \
-p ethcore-light \ -p ethcore-light \
-p wasm \
-p evm \
-p parity" -p parity"