From 9e4c122cf3e918f5f1ce6b9ab63f727bd324db40 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Sun, 20 Aug 2017 07:02:59 +0300 Subject: [PATCH] WASM math test (#6305) * wasm tests initial * fix bug in bitswap --- ethcore/res/wasm-tests | 2 +- ethcore/wasm/src/env.rs | 16 ++++++++- ethcore/wasm/src/lib.rs | 22 ++---------- ethcore/wasm/src/runtime.rs | 67 +++++++++++++++++++++++++++++++++---- ethcore/wasm/src/tests.rs | 34 +++++++++++++++++++ 5 files changed, 114 insertions(+), 27 deletions(-) diff --git a/ethcore/res/wasm-tests b/ethcore/res/wasm-tests index 330f748b1..85e76c5ea 160000 --- a/ethcore/res/wasm-tests +++ b/ethcore/res/wasm-tests @@ -1 +1 @@ -Subproject commit 330f748b1eece451f460224b48d515489dd86f5c +Subproject commit 85e76c5ea2a54c6c54e35014643b5080a50460c5 diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index 7a21ca70d..777016a1b 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -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), + } +} \ No newline at end of file diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index ec6e67405..3fec0f781 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -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 for vm::Error { fn from(err: runtime::Error) -> vm::Error { vm::Error::Wasm(format!("WASM runtime-error: {:?}", err)) diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index a983c954f..e1def857e 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -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, 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, 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, interpreter::Error> + { + let x1 = context.value_stack.pop_as::()?; + let x2 = context.value_stack.pop_as::()?; + + 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, 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, 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) diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 207c9910c..df01b8df6 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -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()); +}