diff --git a/rust-runner/src/call_args.rs b/rust-runner/src/call_args.rs index 092fa5b..2e9bd8d 100644 --- a/rust-runner/src/call_args.rs +++ b/rust-runner/src/call_args.rs @@ -1,7 +1,7 @@ -use parity_wasm::interpreter::{self, ModuleInstanceInterface}; -use {alloc, runtime}; +use parity_wasm::interpreter; +use runtime; -use {DEFAULT_MEMORY_INDEX, WasmMemoryPtr}; +use WasmMemoryPtr; fn write_u32(dst: &mut [u8], val: u32) { dst[0] = (val & 0x000000ff) as u8; @@ -17,7 +17,7 @@ pub enum Error { } impl From for Error { - fn from(err: alloc::ErrorAlloc) -> Self { + fn from(err: runtime::ErrorAlloc) -> Self { Error::Allocator(err) } } @@ -29,7 +29,7 @@ impl From for Error { } pub fn init( - memory: &interpreter::Memory, + memory: &interpreter::MemoryInstance, runtime: &mut runtime::Runtime, input: &[u8], ) -> Result { @@ -39,7 +39,6 @@ pub fn init( let descriptor_ptr = runtime.alloc(16)?; println!("descriptor_ptr: {}", descriptor_ptr); - let memory = env.memory(DEFAULT_MEMORY_INDEX)?; if input.len() > 0 { let input_ptr = runtime.alloc(input.len() as u32)?; @@ -64,7 +63,7 @@ pub fn init( Ok(descriptor_ptr as i32) } -fn read_u32(slc: &[u8]) -> u32 { +fn _read_u32(slc: &[u8]) -> u32 { use std::ops::Shl; (slc[0] as u32) + (slc[1] as u32).shl(8) + (slc[2] as u32).shl(16) + (slc[3] as u32).shl(24) } \ No newline at end of file diff --git a/rust-runner/src/main.rs b/rust-runner/src/main.rs index d9c0f62..8693e89 100644 --- a/rust-runner/src/main.rs +++ b/rust-runner/src/main.rs @@ -7,13 +7,11 @@ extern crate parity_wasm; extern crate wasm_utils; -mod alloc; -mod storage; mod call_args; mod runtime; -mod gas_counter; use std::env; +use std::sync::Arc; use parity_wasm::interpreter::{self, ModuleInstanceInterface}; use parity_wasm::elements; @@ -32,62 +30,70 @@ fn main() { let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - // Second, create runtime and program instance - let runtime = runtime::Runtime::with_params( - 5*1024*1024, // default stack space - 65536, // runner arbitrary gas limit - ); - - let mut user_functions = interpreter::UserFunctions::new(); - user_functions.insert("gas".to_owned(), - interpreter::UserFunction { - params: vec![elements::ValueType::I32], - result: None, - closure: Box::new(runtime.gas_counter()), - } - ); - user_functions.insert("_malloc".to_owned(), - interpreter::UserFunction { - params: vec![elements::ValueType::I32], - result: Some(elements::ValueType::I32), - closure: Box::new(runtime.allocator()), - } - ); - user_functions.insert("_storage_read".to_owned(), - interpreter::UserFunction { - params: vec![elements::ValueType::I32, elements::ValueType::I32], - result: Some(elements::ValueType::I32), - closure: Box::new(runtime.storage().reader()), - } - ); - user_functions.insert("_storage_write".to_owned(), - interpreter::UserFunction { - params: vec![elements::ValueType::I32, elements::ValueType::I32], - result: Some(elements::ValueType::I32), - closure: Box::new(runtime.storage().writer()), - } - ); - runtime::user_trap(&mut user_functions, "_emscripten_memcpy_big"); - runtime::user_trap(&mut user_functions, "invoke_vii"); - runtime::user_noop(&mut user_functions, "_free"); - - let program = parity_wasm::interpreter::ProgramInstance::with_functions(user_functions) + let program = parity_wasm::interpreter::ProgramInstance::new() .expect("Program instance to be created"); // Add module to the programm let module_instance = program.add_module("contract", module).expect("Module to be added successfully"); - // Initialize call descriptor - let descriptor = call_args::init( - &*program.module("env").expect("env module to exist"), - &runtime, - &[3u8; 128], - ).expect("call descriptor initialization to succeed"); + { + let env_instance = program.module("env").expect("env module to exist"); + let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0)) + .expect("liner memory to exist"); - // Invoke _call method of the module - let return_ptr = module_instance.execute_export("_call", vec![descriptor.into()]) - .expect("_call to execute successfully") - .expect("_call function to return result ptr"); + // Second, create runtime and program instance + let mut runtime = runtime::Runtime::with_params( + env_memory.clone(), // memory shared ptr + 5*1024*1024, // default stack space + 65536, // runner arbitrary gas limit + ); - // ??? + // Initialize call descriptor + let descriptor = call_args::init( + &*env_memory, + &mut runtime, + &[3u8; 128], + ).expect("call descriptor initialization to succeed"); + + // create native env module with native add && sub implementations + let functions = interpreter::UserFunctions { + executor: &mut runtime, + functions: vec![ + interpreter::UserFunction { + name: "_storage_read".to_owned(), + params: vec![elements::ValueType::I32, elements::ValueType::I32], + result: Some(elements::ValueType::I32), + }, + interpreter::UserFunction { + name: "_storage_write".to_owned(), + params: vec![elements::ValueType::I32, elements::ValueType::I32], + result: Some(elements::ValueType::I32), + }, + interpreter::UserFunction { + name: "_malloc".to_owned(), + params: vec![elements::ValueType::I32], + result: Some(elements::ValueType::I32), + }, + interpreter::UserFunction { + name: "gas".to_owned(), + params: vec![elements::ValueType::I32], + result: None, + }, + interpreter::UserFunction { + name: "_free".to_owned(), + params: vec![elements::ValueType::I32], + result: None, + }, + ], + }; + let native_env_instance = Arc::new(interpreter::env_native_module(env_instance, functions).unwrap()); + + // Form ExecutionParams (payload + env link) + let params = interpreter::ExecutionParams::with_external("env".into(), native_env_instance) + .add_argument(interpreter::RuntimeValue::I32(descriptor)); + + module_instance.execute_export("_call", params) + .expect("_call to execute successfully") + .expect("_call function to return result ptr"); + } } \ No newline at end of file diff --git a/rust-runner/src/runtime.rs b/rust-runner/src/runtime.rs index 3e61f2c..9f751c2 100644 --- a/rust-runner/src/runtime.rs +++ b/rust-runner/src/runtime.rs @@ -1,9 +1,7 @@ use std::sync::Arc; -use std::cell::{Cell, RefCell}; use std::collections::HashMap; -use parity_wasm::{interpreter, elements}; -use {alloc, gas_counter, storage}; +use parity_wasm::interpreter; #[derive(Hash, PartialEq, Eq, Debug)] pub struct StorageKey([u8; 32]); @@ -11,101 +9,155 @@ pub struct StorageKey([u8; 32]); #[derive(Debug, Default)] pub struct StorageValue([u8; 32]); +struct ErrorStorage; + +impl StorageKey { + // todo: deal with memory views + fn from_mem(vec: Vec) -> Result { + if vec.len() != 32 { return Err(ErrorStorage); } + let mut result = StorageKey([0u8; 32]); + result.0.copy_from_slice(&vec[0..32]); + Ok(result) + } +} + +impl StorageValue { + // todo: deal with memory views + // todo: deal with variable-length values when it comes + fn from_mem(vec: Vec) -> Result { + if vec.len() != 32 { return Err(ErrorStorage); } + let mut result = StorageValue([0u8; 32]); + result.0.copy_from_slice(&vec[0..32]); + Ok(result) + } + + fn as_slice(&self) -> &[u8] { + &self.0 + } +} + pub struct Runtime { gas_counter: u64, gas_limit: u64, dynamic_top: u32, - storage: HashMap, + storage: HashMap, + memory: Arc, } #[derive(Debug)] -struct ErrorAlloc; +pub struct ErrorAlloc; impl Runtime { - pub fn with_params(stack_space: u32, gas_limit: u64) -> Runtime { - Runtime(Arc::new(RuntimeEnv { + pub fn with_params(memory: Arc, stack_space: u32, gas_limit: u64) -> Runtime { + Runtime { gas_counter: 0, gas_limit: gas_limit, dynamic_top: stack_space, storage: HashMap::new(), - })) + memory: memory, + } } - pub fn storage_write(&mut self, memory: Arc, context: interpreter::CallerContext) + pub fn storage_write(&mut self, context: interpreter::CallerContext) -> Result, interpreter::Error> { let val_ptr = context.value_stack.pop_as::()?; let key_ptr = context.value_stack.pop_as::()?; - let key = StorageKey::from_mem(memory.get(key_ptr as u32, 32)?) + let key = StorageKey::from_mem(self.memory.get(key_ptr as u32, 32)?) .map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?; - let val = StorageValue::from_mem(memory.get(val_ptr as u32, 32)?) + let val = StorageValue::from_mem(self.memory.get(val_ptr as u32, 32)?) .map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?; self.storage.insert(key, val); - Ok(0.into()) + Ok(Some(0i32.into())) } - pub fn storage_write(&mut self, memory: Arc, context: interpreter::CallerContext) + pub fn storage_read(&mut self, context: interpreter::CallerContext) -> Result, interpreter::Error> { // arguments passed are in backward order (since it is stack) let val_ptr = context.value_stack.pop_as::()?; let key_ptr = context.value_stack.pop_as::()?; - let key = StorageKey::from_mem(memory.get(key_ptr as u32, 32)?) + let key = StorageKey::from_mem(self.memory.get(key_ptr as u32, 32)?) .map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?; let empty = StorageValue([0u8; 32]); - let storage = self.0.runtime.env().storage.borrow(); - let val = storage.get(&key).unwrap_or(&empty); + let val = self.storage.get(&key).unwrap_or(&empty); - memory.set(val_ptr as u32, val.as_slice()); + self.memory.set(val_ptr as u32, val.as_slice())?; println!("read storage {:?} (evaluated as {:?})", key, val); Ok(Some(0.into())) } - pub fn malloc(&mut self, _memory: Arc, context: interpreter::CallerContext) + pub fn malloc(&mut self, context: interpreter::CallerContext) -> Result, interpreter::Error> { let amount = context.value_stack.pop_as::()? as u32; let previous_top = self.dynamic_top; - self.dynamic_top = previous_top + size; - Ok(previous_top.into()) + self.dynamic_top = previous_top + amount; + Ok(Some((previous_top as i32).into())) } pub fn alloc(&mut self, amount: u32) -> Result { let previous_top = self.dynamic_top; - self.dynamic_top = previous_top + size; - Ok(previous_top.into()) + self.dynamic_top = previous_top + amount; + Ok(previous_top.into()) } - fn gas(&mut self, _memory: Arc, context: interpreter::CallerContext) + fn gas(&mut self, context: interpreter::CallerContext) -> Result, interpreter::Error> { let prev = self.gas_counter; let update = context.value_stack.pop_as::()? as u64; if prev + update > self.gas_limit { // exceeds gas - Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.runtime.env().gas_limit))) + Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.gas_limit))) } else { - self.gas_counter.set(prev + update); + self.gas_counter = prev + update; Ok(None) } } - fn user_trap(&mut self, _memory: Arc, _context: interpreter::CallerContext) + fn user_trap(&mut self, _context: interpreter::CallerContext) -> Result, interpreter::Error> { - Err(interpreter::Error::Trap(self.0.clone())) + Err(interpreter::Error::Trap("unknown trap".to_owned())) } fn user_noop(&mut self, - _memory: Arc, _context: interpreter::CallerContext ) -> Result, interpreter::Error> { Ok(None) } } + +impl interpreter::UserFunctionExecutor for Runtime { + fn execute(&mut self, name: &str, context: interpreter::CallerContext) + -> Result, interpreter::Error> + { + match name { + "_malloc" => { + self.malloc(context) + }, + "_free" => { + self.user_noop(context) + }, + "_storage_read" => { + self.storage_read(context) + }, + "_storage_write" => { + self.storage_write(context) + }, + "gas" => { + self.gas(context) + }, + _ => { + self.user_trap(context) + } + } + } +} \ No newline at end of file