Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

WASM math test #6305

Merged
merged 2 commits into from
Aug 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethcore/res/wasm-tests
16 changes: 15 additions & 1 deletion ethcore/wasm/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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),
}
}
22 changes: 3 additions & 19 deletions ethcore/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand All @@ -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| {
Expand Down Expand Up @@ -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))
Expand Down
67 changes: 61 additions & 6 deletions ethcore/wasm/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,31 +72,34 @@ 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,
dynamic_top: stack_space,
memory: memory,
ext: ext,
context: context,
instance: program_instance,
}
}

Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that actually how it's supposed to work?
[a b c d e f g h] -> [d c b a h g f e]

instead of [h g f e d c b a]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stack reverses the parameter ordering, so first parameter is popped last


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>
{
Expand Down Expand Up @@ -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)
Expand Down
34 changes: 34 additions & 0 deletions ethcore/wasm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like there should be a different commentary..

// 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());
}