From 5180919e52d9d06b9772935d22504ba601088bd9 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 24 Jul 2017 17:45:15 +0300 Subject: [PATCH] wasm mvp continued --- Cargo.lock | 1 + ethcore/evm/Cargo.toml | 2 + ethcore/evm/src/lib.rs | 1 + ethcore/evm/src/wasm/env.rs | 55 ++++------- ethcore/evm/src/wasm/mod.rs | 3 +- ethcore/evm/src/wasm/runtime.rs | 158 +++++++++++++++++++++++++++++++- ethcore/evm/src/wasm/tests.rs | 108 ++++++++++++++++++++-- ethcore/res/wasm-tests | 2 +- 8 files changed, 281 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7046b759..16d378150 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,6 +878,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", diff --git a/ethcore/evm/Cargo.toml b/ethcore/evm/Cargo.toml index b48dd2346..bd95e0587 100644 --- a/ethcore/evm/Cargo.toml +++ b/ethcore/evm/Cargo.toml @@ -14,8 +14,10 @@ lazy_static = "0.2" log = "0.3" rlp = { path = "../../util/rlp" } parity-wasm = "0.12" +ethcore-logger = { path = "../../logger" } wasm-utils = { git = "https://github.com/paritytech/wasm-utils" } + [dev-dependencies] rustc-hex = "1.0" diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index fa4d12315..55f7c5090 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -24,6 +24,7 @@ extern crate ethjson; extern crate rlp; extern crate parity_wasm; extern crate wasm_utils; +extern crate ethcore_logger; #[macro_use] extern crate lazy_static; diff --git a/ethcore/evm/src/wasm/env.rs b/ethcore/evm/src/wasm/env.rs index cabd38bd9..e68e50432 100644 --- a/ethcore/evm/src/wasm/env.rs +++ b/ethcore/evm/src/wasm/env.rs @@ -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], @@ -71,49 +86,19 @@ 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, - ), 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), ) ]; diff --git a/ethcore/evm/src/wasm/mod.rs b/ethcore/evm/src/wasm/mod.rs index a7186add5..acad9e5cb 100644 --- a/ethcore/evm/src/wasm/mod.rs +++ b/ethcore/evm/src/wasm/mod.rs @@ -34,7 +34,7 @@ use wasm_utils; use evm::{self, GasLeft, ReturnData}; use action_params::ActionParams; -use self::runtime::Runtime; +use self::runtime::{Runtime, RuntimeContext}; pub use self::runtime::Error as RuntimeError; @@ -82,6 +82,7 @@ impl evm::Evm 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); diff --git a/ethcore/evm/src/wasm/runtime.rs b/ethcore/evm/src/wasm/runtime.rs index 7beb4c599..adcb14de1 100644 --- a/ethcore/evm/src/wasm/runtime.rs +++ b/ethcore/evm/src/wasm/runtime.rs @@ -25,6 +25,7 @@ use ext; use parity_wasm::interpreter; use util::{Address, H256, U256}; +use call_type::CallType; use super::ptr::{WasmPtr, Error as PtrError}; use super::call_args::CallArgs; @@ -57,6 +58,20 @@ impl From 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, @@ -64,6 +79,7 @@ pub struct Runtime<'a> { dynamic_top: u32, ext: &'a mut ext::Ext, memory: Arc, + context: RuntimeContext, } impl<'a> Runtime<'a> { @@ -73,6 +89,7 @@ impl<'a> Runtime<'a> { memory: Arc, stack_space: u32, gas_limit: u64, + context: RuntimeContext, ) -> Runtime<'b> { Runtime { gas_counter: 0, @@ -80,6 +97,7 @@ impl<'a> Runtime<'a> { dynamic_top: stack_space, memory: memory, ext: ext, + context: context, } } @@ -139,13 +157,13 @@ impl<'a> Runtime<'a> { trace!(target: "wasm", "runtime: create contract"); let mut context = context; let result_ptr = context.value_stack.pop_as::()? as u32; - trace!(target: "wasm", " result_ptr: {:?}", result_ptr); + trace!(target: "wasm", "result_ptr: {:?}", result_ptr); let code_len = context.value_stack.pop_as::()? as u32; - trace!(target: "wasm", " code_len: {:?}", code_len); + trace!(target: "wasm", " code_len: {:?}", code_len); let code_ptr = context.value_stack.pop_as::()? as u32; - trace!(target: "wasm", " code_ptr: {:?}", code_ptr); + trace!(target: "wasm", " code_ptr: {:?}", code_ptr); let endowment = self.pop_u256(&mut context)?; - trace!(target: "wasm", " val: {:?}", endowment); + trace!(target: "wasm", " val: {:?}", endowment); let code = self.memory.get(code_ptr, code_len as usize)?; @@ -167,6 +185,127 @@ impl<'a> Runtime<'a> { } } + pub fn call(&mut self, context: interpreter::CallerContext) + -> Result, 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, 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, interpreter::Error> + { + + trace!(target: "wasm", "runtime: call code"); + let mut context = context; + let result_alloc_len = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " result_len: {:?}", result_alloc_len); + + let result_ptr = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " result_ptr: {:?}", result_ptr); + + let input_len = context.value_stack.pop_as::()? as u32; + trace!(target: "wasm", " input_len: {:?}", input_len); + + let input_ptr = context.value_stack.pop_as::()? 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 { + ext::MessageCallResult::Success(gas_left, _) => { + self.gas_counter = self.gas_limit - gas_left.low_u64(); + self.memory.set(result_ptr, &result)?; + Ok(Some(0i32.into())) + }, + ext::MessageCallResult::Failed => { + Ok(Some((-1i32).into())) + } + } + } + + pub fn static_call(&mut self, context: interpreter::CallerContext) + -> Result, 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, interpreter::Error> @@ -338,6 +477,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) }, @@ -348,7 +496,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) } } diff --git a/ethcore/evm/src/wasm/tests.rs b/ethcore/evm/src/wasm/tests.rs index 8ae13daae..c9d02a4ad 100644 --- a/ethcore/evm/src/wasm/tests.rs +++ b/ethcore/evm/src/wasm/tests.rs @@ -21,6 +21,7 @@ use super::WasmInterpreter; use evm::{self, Evm, GasLeft}; use action_params::{ActionParams, ActionValue}; use util::{U256, H256, Address}; +use byteorder::{LittleEndian, ByteOrder}; macro_rules! load_sample { ($name: expr) => { @@ -85,7 +86,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 +137,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 +171,7 @@ fn dispersion() { } }; - assert_eq!(gas_left, U256::from(99_402)); + assert_eq!(gas_left, U256::from(99_423)); assert_eq!( result, @@ -199,7 +200,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 +234,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 +265,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,5 +273,96 @@ 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); +} \ No newline at end of file diff --git a/ethcore/res/wasm-tests b/ethcore/res/wasm-tests index 9ed630431..8361f18c7 160000 --- a/ethcore/res/wasm-tests +++ b/ethcore/res/wasm-tests @@ -1 +1 @@ -Subproject commit 9ed6304313fa949ed92aa0570fb2bc759fb6dc58 +Subproject commit 8361f18c7ea133d9b85edf7dea02f05b9feb1938