WASM math test (#6305)

* wasm tests initial

* fix bug in bitswap
This commit is contained in:
Nikolay Volf 2017-08-20 07:02:59 +03:00 committed by Gav Wood
parent 407c8c3fb9
commit 9e4c122cf3
5 changed files with 114 additions and 27 deletions

@ -1 +1 @@
Subproject commit 330f748b1eece451f460224b48d515489dd86f5c
Subproject commit 85e76c5ea2a54c6c54e35014643b5080a50460c5

View File

@ -17,8 +17,9 @@
//! Wasm env module bindings
use parity_wasm::elements::ValueType::*;
use parity_wasm::interpreter::UserFunctionDescriptor;
use parity_wasm::interpreter::{self, UserFunctionDescriptor};
use parity_wasm::interpreter::UserFunctionDescriptor::*;
use super::runtime::Runtime;
pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
Static(
@ -93,4 +94,17 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[I32; 0],
None
),
Static(
"_llvm_bswap_i64",
&[I32; 2],
Some(I32)
),
];
pub fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> {
interpreter::UserFunctions {
executor: runtime,
functions: ::std::borrow::Cow::from(SIGNATURES),
}
}

View File

@ -32,8 +32,6 @@ mod result;
mod tests;
mod env;
use std::sync::Arc;
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;
use parity_wasm::{interpreter, elements};
@ -89,6 +87,7 @@ impl vm::Vm for WasmInterpreter {
DEFAULT_STACK_SPACE,
params.gas.low_u64(),
RuntimeContext::new(params.address, params.sender),
&self.program,
);
let mut cursor = ::std::io::Cursor::new(&*code);
@ -112,16 +111,8 @@ impl vm::Vm for WasmInterpreter {
)?;
{
let execution_params = interpreter::ExecutionParams::with_external(
"env".into(),
Arc::new(
interpreter::env_native_module(env_instance, native_bindings(&mut runtime))
.map_err(|err| {
// todo: prefer explicit panic here also?
vm::Error::Wasm(format!("Error instantiating native bindings: {:?}", err))
})?
)
).add_argument(interpreter::RuntimeValue::I32(d_ptr.as_raw() as i32));
let execution_params = runtime.execution_params()
.add_argument(interpreter::RuntimeValue::I32(d_ptr.as_raw() as i32));
let module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals))
.map_err(|err| {
@ -158,13 +149,6 @@ impl vm::Vm for WasmInterpreter {
}
}
fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> {
interpreter::UserFunctions {
executor: runtime,
functions: ::std::borrow::Cow::from(env::SIGNATURES),
}
}
impl From<runtime::Error> for vm::Error {
fn from(err: runtime::Error) -> vm::Error {
vm::Error::Wasm(format!("WASM runtime-error: {:?}", err))

View File

@ -72,24 +72,26 @@ impl RuntimeContext {
}
/// Runtime enviroment data for wasm contract execution
pub struct Runtime<'a> {
pub struct Runtime<'a, 'b> {
gas_counter: u64,
gas_limit: u64,
dynamic_top: u32,
ext: &'a mut vm::Ext,
memory: Arc<interpreter::MemoryInstance>,
context: RuntimeContext,
instance: &'b interpreter::ProgramInstance,
}
impl<'a> Runtime<'a> {
impl<'a, 'b> Runtime<'a, 'b> {
/// New runtime for wasm contract with specified params
pub fn with_params<'b>(
ext: &'b mut vm::Ext,
pub fn with_params<'c, 'd>(
ext: &'c mut vm::Ext,
memory: Arc<interpreter::MemoryInstance>,
stack_space: u32,
gas_limit: u64,
context: RuntimeContext,
) -> Runtime<'b> {
program_instance: &'d interpreter::ProgramInstance,
) -> Runtime<'c, 'd> {
Runtime {
gas_counter: 0,
gas_limit: gas_limit,
@ -97,6 +99,7 @@ impl<'a> Runtime<'a> {
memory: memory,
ext: ext,
context: context,
instance: program_instance,
}
}
@ -449,9 +452,58 @@ impl<'a> Runtime<'a> {
Ok(Some(0i32.into()))
}
fn bswap_32(x: u32) -> u32 {
x >> 24 | x >> 8 & 0xff00 | x << 8 & 0xff0000 | x << 24
}
fn bitswap_i64(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
let x1 = context.value_stack.pop_as::<i32>()?;
let x2 = context.value_stack.pop_as::<i32>()?;
let result = ((Runtime::bswap_32(x2 as u32) as u64) << 32
| Runtime::bswap_32(x1 as u32) as u64) as i64;
self.return_i64(result)
}
fn return_i64(&mut self, val: i64) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
let uval = val as u64;
let hi = (uval >> 32) as i32;
let lo = (uval << 32 >> 32) as i32;
let target = self.instance.module("contract")
.ok_or(interpreter::Error::Trap("Error locating main execution entry".to_owned()))?;
target.execute_export(
"setTempRet0",
self.execution_params().add_argument(
interpreter::RuntimeValue::I32(hi).into()
),
)?;
Ok(Some(
(lo).into()
))
}
pub fn execution_params(&mut self) -> interpreter::ExecutionParams {
use super::env;
let env_instance = self.instance.module("env")
.expect("Env module always exists; qed");
interpreter::ExecutionParams::with_external(
"env".into(),
Arc::new(
interpreter::env_native_module(env_instance, env::native_bindings(self))
.expect("Env module always exists; qed")
)
)
}
}
impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
impl<'a, 'b> interpreter::UserFunctionExecutor for Runtime<'a, 'b> {
fn execute(&mut self, name: &str, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
@ -494,6 +546,9 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
"_emscripten_memcpy_big" => {
self.mem_copy(context)
},
"_llvm_bswap_i64" => {
self.bitswap_i64(context)
},
_ => {
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
self.user_trap(context)

View File

@ -414,3 +414,37 @@ fn storage_read() {
assert_eq!(gas_left, U256::from(99682));
assert_eq!(Address::from(&result[12..32]), address);
}
// Tests that contract's ability to read from a storage
// Test prepopulates address into storage, than executes a contract which read that address from storage and write this address into result
#[test]
fn math_add() {
::ethcore_logger::init_log();
let code = load_sample!("math.wasm");
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(code));
let mut args = [0u8; 64];
let arg_a = U256::from_dec_str("999999999999999999999999999999").unwrap();
let arg_b = U256::from_dec_str("888888888888888888888888888888").unwrap();
arg_a.to_big_endian(&mut args[0..32]);
arg_b.to_big_endian(&mut args[32..64]);
params.data = Some(args.to_vec());
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut FakeExt::new()).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("storage_read should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
let sum: U256 = (&result[..]).into();
assert_eq!(gas_left, U256::from(96284));
assert_eq!(sum, U256::from_dec_str("1888888888888888888888888888887").unwrap());
}