commit
5b0eeb75ef
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -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)",
|
||||||
|
@ -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]
|
||||||
|
@ -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]
|
||||||
|
@ -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();
|
||||||
|
@ -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
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"
|
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" }
|
@ -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],
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,7 +272,98 @@ 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
|
||||||
@ -294,5 +387,4 @@ fn realloc() {
|
|||||||
};
|
};
|
||||||
assert_eq!(gas_left, U256::from(98326));
|
assert_eq!(gas_left, U256::from(98326));
|
||||||
assert_eq!(result, vec![0u8; 2]);
|
assert_eq!(result, vec![0u8; 2]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user