diff --git a/rust-runner/src/call_args.rs b/rust-runner/src/call_args.rs index ec06680..092fa5b 100644 --- a/rust-runner/src/call_args.rs +++ b/rust-runner/src/call_args.rs @@ -12,12 +12,12 @@ fn write_u32(dst: &mut [u8], val: u32) { #[derive(Debug)] pub enum Error { - Allocator(alloc::Error), + Allocator(runtime::ErrorAlloc), Interpreter(interpreter::Error), } -impl From for Error { - fn from(err: alloc::Error) -> Self { +impl From for Error { + fn from(err: alloc::ErrorAlloc) -> Self { Error::Allocator(err) } } @@ -29,22 +29,20 @@ impl From for Error { } pub fn init( - env: &interpreter::ModuleInstanceInterface, - runtime: &runtime::Runtime, + memory: &interpreter::Memory, + runtime: &mut runtime::Runtime, input: &[u8], ) -> Result { let mut input_ptr_slc = [0u8; 4]; let mut input_length = [0u8; 4]; - let allocator = runtime.allocator(); - - let descriptor_ptr = allocator.alloc(16)?; + 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 = allocator.alloc(input.len() as u32)?; + let input_ptr = runtime.alloc(input.len() as u32)?; write_u32(&mut input_ptr_slc, input_ptr); write_u32(&mut input_length, input.len() as u32); memory.set(input_ptr, input)?; @@ -69,14 +67,4 @@ pub fn init( 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) -} - -// pub fn retrieve(env: -// env: &interpreter::ModuleInstanceInterface, -// runtime: &runtime::Runtime, -// descriptor_ptr: u32, -// result_ptr: u32, -// ) -> Result, Error> { -// let memory = env.memory(DEFAULT_MEMORY_INDEX)?; - -// } \ No newline at end of file +} \ No newline at end of file diff --git a/rust-runner/src/gas_counter.rs b/rust-runner/src/gas_counter.rs deleted file mode 100644 index 2dc3e84..0000000 --- a/rust-runner/src/gas_counter.rs +++ /dev/null @@ -1,20 +0,0 @@ -use parity_wasm::interpreter::{self, ModuleInstance}; -use runtime::Runtime; - -pub struct GasCounter { - pub runtime: Runtime, -} - -impl interpreter::UserFunctionInterface for GasCounter { - fn call(&mut self, _module: &ModuleInstance, context: interpreter::CallerContext) -> Result, interpreter::Error> { - let prev = self.runtime.env().gas_counter.get(); - let update = context.value_stack.pop_as::()? as u64; - if prev + update > self.runtime.env().gas_limit { - // exceeds gas - Err(interpreter::Error::Trap(format!("Gas exceeds limits of {}", self.runtime.env().gas_limit))) - } else { - self.runtime.env().gas_counter.set(prev + update); - Ok(None) - } - } -} \ No newline at end of file diff --git a/rust-runner/src/runtime.rs b/rust-runner/src/runtime.rs index fcc8989..3e61f2c 100644 --- a/rust-runner/src/runtime.rs +++ b/rust-runner/src/runtime.rs @@ -5,88 +5,107 @@ use std::collections::HashMap; use parity_wasm::{interpreter, elements}; use {alloc, gas_counter, storage}; -#[derive(Default)] -pub struct RuntimeEnv { - pub gas_counter: Cell, - pub gas_limit: u64, - pub dynamic_top: Cell, - pub storage: RefCell>, +#[derive(Hash, PartialEq, Eq, Debug)] +pub struct StorageKey([u8; 32]); + +#[derive(Debug, Default)] +pub struct StorageValue([u8; 32]); + +pub struct Runtime { + gas_counter: u64, + gas_limit: u64, + dynamic_top: u32, + storage: HashMap, } -#[derive(Default, Clone)] -pub struct Runtime(Arc); +#[derive(Debug)] +struct ErrorAlloc; impl Runtime { pub fn with_params(stack_space: u32, gas_limit: u64) -> Runtime { Runtime(Arc::new(RuntimeEnv { - gas_counter: Cell::new(0), + gas_counter: 0, gas_limit: gas_limit, - dynamic_top: Cell::new(stack_space), - storage: RefCell::new(HashMap::new()), + dynamic_top: stack_space, + storage: HashMap::new(), })) } - pub fn allocator(&self) -> alloc::Arena { - alloc::Arena { - runtime: self.clone(), + pub fn storage_write(&mut self, memory: Arc, 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)?) + .map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?; + let val = StorageValue::from_mem(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()) + } + + pub fn storage_write(&mut self, memory: Arc, 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)?) + .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); + + 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) + -> 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()) + } + + pub fn alloc(&mut self, amount: u32) -> Result { + let previous_top = self.dynamic_top; + self.dynamic_top = previous_top + size; + Ok(previous_top.into()) + } + + fn gas(&mut self, _memory: Arc, 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))) + } else { + self.gas_counter.set(prev + update); + Ok(None) } } - pub fn gas_counter(&self) -> gas_counter::GasCounter { - gas_counter::GasCounter { - runtime: self.clone(), - } - } - - pub fn storage(&self) -> storage::Storage { - storage::Storage::new(self.clone()) - } - - pub fn env(&self) -> &RuntimeEnv { - &*self.0 - } -} - -pub fn user_trap(funcs: &mut interpreter::UserFunctions, func_name: &str) { - let func_str = func_name.to_owned(); - funcs.insert(func_str.clone(), - interpreter::UserFunction { - params: vec![], - result: Some(elements::ValueType::I32), - closure: Box::new(UserTrap(func_str)), - } - ); -} - -struct UserTrap(String); - -impl interpreter::UserFunctionInterface for UserTrap { - fn call(&mut self, - _module: &interpreter::ModuleInstance, - _context: interpreter::CallerContext - ) -> Result, interpreter::Error> { + fn user_trap(&mut self, _memory: Arc, _context: interpreter::CallerContext) + -> Result, interpreter::Error> + { Err(interpreter::Error::Trap(self.0.clone())) } -} -struct UserNoop; - -pub fn user_noop(funcs: &mut interpreter::UserFunctions, func_name: &str) { - let func_str = func_name.to_owned(); - funcs.insert(func_str.clone(), - interpreter::UserFunction { - params: vec![], - result: None, - closure: Box::new(UserNoop), - } - ); -} - -impl interpreter::UserFunctionInterface for UserNoop { - fn call(&mut self, - _module: &interpreter::ModuleInstance, + fn user_noop(&mut self, + _memory: Arc, _context: interpreter::CallerContext ) -> Result, interpreter::Error> { Ok(None) - } -} \ No newline at end of file + } +} diff --git a/rust-runner/src/storage.rs b/rust-runner/src/storage.rs deleted file mode 100644 index 246d688..0000000 --- a/rust-runner/src/storage.rs +++ /dev/null @@ -1,118 +0,0 @@ -use parity_wasm::interpreter::{self, ItemIndex, ModuleInstanceInterface}; -use std::sync::Arc; - -use DEFAULT_MEMORY_INDEX; -use runtime::Runtime; - -#[derive(Hash, PartialEq, Eq, Debug)] -pub struct StorageKey([u8; 32]); - -#[derive(Debug, Default)] -pub struct StorageValue([u8; 32]); - -impl StorageKey { - // todo: deal with memory views - fn from_mem(vec: Vec) -> Result { - if vec.len() != 32 { return Err(Error); } - 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(Error); } - 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 Storage { - runtime: Runtime, -} - -pub struct Error; - -impl Storage { - - pub fn new(runtime: Runtime) -> Self { - Storage { - runtime: runtime, - } - } - - pub fn writer(self) -> StorageWrite { - StorageWrite(self) - } - - pub fn reader(self) -> StorageRead { - StorageRead(self) - } -} - -pub struct StorageWrite(Storage); - -impl interpreter::UserFunctionInterface for StorageWrite { - fn call(&mut self, - module: &interpreter::ModuleInstance, - 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 memory = match module.memory(DEFAULT_MEMORY_INDEX) { - Err(_) => { return Ok(Some((-1i32).into())) }, - Ok(memory) => memory, - }; - - let key = StorageKey::from_mem(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)?) - .map_err(|_| interpreter::Error::Trap("Memory access violation".to_owned()))?; - - println!("set storage {:?} = {:?}", key, val); - - Ok(Some(0.into())) - } -} - -pub struct StorageRead(Storage); - -impl interpreter::UserFunctionInterface for StorageRead { - fn call(&mut self, - module: &interpreter::ModuleInstance, - 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 memory = match module.memory(DEFAULT_MEMORY_INDEX) { - Err(_) => { return Ok(Some((-1i32).into())) }, - Ok(memory) => memory, - }; - - let key = StorageKey::from_mem(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); - - memory.set(val_ptr as u32, val.as_slice()); - - println!("read storage {:?} (evaluated as {:?})", key, val); - - Ok(Some(0.into())) - } -} \ No newline at end of file