commit
5b0eeb75ef
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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)",
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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<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}
|
||||
fn test_add(factory: super::Factory) {
|
||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
|
@ -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};
|
||||
|
187
ethcore/vm/src/tests.rs
Normal file
187
ethcore/vm/src/tests.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -10,3 +10,4 @@ log = "0.3"
|
||||
parity-wasm = "0.12"
|
||||
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
||||
vm = { path = "../vm" }
|
||||
ethcore-logger = { path = "../../logger" }
|
@ -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],
|
||||
|
@ -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);
|
||||
|
@ -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<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
|
||||
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<interpreter::MemoryInstance>,
|
||||
context: RuntimeContext,
|
||||
}
|
||||
|
||||
impl<'a> Runtime<'a> {
|
||||
@ -72,6 +88,7 @@ impl<'a> Runtime<'a> {
|
||||
memory: Arc<interpreter::MemoryInstance>,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
pub fn malloc(&mut self, context: interpreter::CallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, 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)
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,16 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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,7 +272,98 @@ 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
|
||||
@ -294,5 +387,4 @@ fn realloc() {
|
||||
};
|
||||
assert_eq!(gas_left, U256::from(98326));
|
||||
assert_eq!(result, vec![0u8; 2]);
|
||||
|
||||
}
|
||||
|
@ -19,4 +19,6 @@ export TARGETS="
|
||||
-p ethcore-ipc-tests \
|
||||
-p ethcore-ipc-nano \
|
||||
-p ethcore-light \
|
||||
-p wasm \
|
||||
-p evm \
|
||||
-p parity"
|
||||
|
Loading…
Reference in New Issue
Block a user