// 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 . //! Wasm Interpreter extern crate vm; extern crate ethcore_util as util; #[macro_use] extern crate log; extern crate byteorder; extern crate parity_wasm; extern crate wasm_utils; mod runtime; mod ptr; mod call_args; mod result; #[cfg(test)] mod tests; mod env; use std::sync::Arc; const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024; use parity_wasm::{interpreter, elements}; use parity_wasm::interpreter::ModuleInstanceInterface; use vm::{GasLeft, ReturnData, ActionParams}; use self::runtime::Runtime; pub use self::runtime::Error as RuntimeError; const DEFAULT_RESULT_BUFFER: usize = 1024; /// Wasm interpreter instance pub struct WasmInterpreter { program: interpreter::ProgramInstance, result: Vec, } impl WasmInterpreter { /// New wasm interpreter instance pub fn new() -> Result { Ok(WasmInterpreter { program: interpreter::ProgramInstance::new()?, result: Vec::with_capacity(DEFAULT_RESULT_BUFFER), }) } } impl vm::Vm for WasmInterpreter { fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result { use parity_wasm::elements::Deserialize; let code = params.code.expect("exec is only called on contract with code; qed"); trace!(target: "wasm", "Started wasm interpreter with code.len={:?}", code.len()); let env_instance = self.program.module("env") // prefer explicit panic here .expect("Wasm program to contain env module"); let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0)) // prefer explicit panic here .expect("Linear memory to exist in wasm runtime"); if params.gas > ::std::u64::MAX.into() { return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas >= 2^64".to_owned())); } let mut runtime = Runtime::with_params( ext, env_memory, DEFAULT_STACK_SPACE, params.gas.low_u64(), ); let mut cursor = ::std::io::Cursor::new(&*code); let contract_module = wasm_utils::inject_gas_counter( elements::Module::deserialize( &mut cursor ).map_err(|err| { vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err)) })? ); let d_ptr = runtime.write_descriptor( call_args::CallArgs::new( params.address, params.sender, params.origin, params.value.value(), params.data.unwrap_or(Vec::with_capacity(0)), ) )?; { 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 module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals)) .map_err(|err| { trace!(target: "wasm", "Error adding contract module: {:?}", err); vm::Error::from(RuntimeError::Interpreter(err)) })?; module_instance.execute_export("_call", execution_params) .map_err(|err| { trace!(target: "wasm", "Error executing contract: {:?}", err); vm::Error::from(RuntimeError::Interpreter(err)) })?; } let result = result::WasmResult::new(d_ptr); if result.peek_empty(&*runtime.memory())? { trace!(target: "wasm", "Contract execution result is empty."); Ok(GasLeft::Known(runtime.gas_left()?.into())) } else { self.result.clear(); // todo: use memory views to avoid copy self.result.extend(result.pop(&*runtime.memory())?); let len = self.result.len(); Ok(GasLeft::NeedsReturn { gas_left: runtime.gas_left()?.into(), data: ReturnData::new( ::std::mem::replace(&mut self.result, Vec::with_capacity(DEFAULT_RESULT_BUFFER)), 0, len, ), apply_state: true, }) } } } 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)) } }