diff --git a/.gitignore b/.gitignore index cb117fd..8a0f2d4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ Cargo.lock -target \ No newline at end of file +target +.cargo \ No newline at end of file diff --git a/ext/Cargo.toml b/ext/Cargo.toml index f69ebed..99c6f9f 100644 --- a/ext/Cargo.toml +++ b/ext/Cargo.toml @@ -5,4 +5,4 @@ authors = ["NikVolf "] [dependencies] parity-wasm = { git="https://github.com/nikvolf/parity-wasm" } -wasm_utils = { path = "../" } \ No newline at end of file +wasm-utils = { path = "../" } \ No newline at end of file diff --git a/js-runner/build.sh b/js-runner/build.sh old mode 100644 new mode 100755 diff --git a/js-runner/linker_emcc.sh b/js-runner/linker_emcc.sh old mode 100644 new mode 100755 diff --git a/rust-runner/src/call_args.rs b/rust-runner/src/call_args.rs index 75f244d..ec06680 100644 --- a/rust-runner/src/call_args.rs +++ b/rust-runner/src/call_args.rs @@ -31,11 +31,8 @@ impl From for Error { pub fn init( env: &interpreter::ModuleInstanceInterface, runtime: &runtime::Runtime, - context: &[u8], input: &[u8], ) -> Result { - let mut context_ptr_slc = [0u8; 4]; - let mut context_length = [0u8; 4]; let mut input_ptr_slc = [0u8; 4]; let mut input_length = [0u8; 4]; @@ -46,29 +43,40 @@ pub fn init( println!("descriptor_ptr: {}", descriptor_ptr); let memory = env.memory(DEFAULT_MEMORY_INDEX)?; - if context.len() > 0 { - let context_ptr = allocator.alloc(context.len() as u32)?; - write_u32(&mut context_ptr_slc, context_ptr); - write_u32(&mut context_length, context.len() as u32); - memory.set(context_ptr, context)?; - println!("context_ptr: {}", context_ptr); - } - - if input.len() > 0 { + if input.len() > 0 { let input_ptr = allocator.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)?; println!("input_ptr: {}", input_ptr); - println!("input: {:?}", memory.get(input_ptr, input.len())); + } else { + write_u32(&mut input_ptr_slc, 0); + write_u32(&mut input_length, 0); } - memory.set(descriptor_ptr, &context_ptr_slc)?; - memory.set(descriptor_ptr+4, &context_length)?; - memory.set(descriptor_ptr+8, &input_ptr_slc)?; - memory.set(descriptor_ptr+12, &input_length)?; + memory.set(descriptor_ptr, &input_ptr_slc)?; + memory.set(descriptor_ptr+4, &input_length)?; + + // zero result ptr/len + memory.set(descriptor_ptr+8, &[0u8; 4])?; + memory.set(descriptor_ptr+12, &[0u8; 4])?; println!("descriptor: {:?}", memory.get(descriptor_ptr, 16)); Ok(descriptor_ptr as i32) -} \ No newline at end of file +} + +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 diff --git a/rust-runner/src/main.rs b/rust-runner/src/main.rs index 1e00daf..d9c0f62 100644 --- a/rust-runner/src/main.rs +++ b/rust-runner/src/main.rs @@ -53,28 +53,22 @@ fn main() { closure: Box::new(runtime.allocator()), } ); - user_functions.insert("_storage_size".to_owned(), - interpreter::UserFunction { - params: vec![elements::ValueType::I32], - result: Some(elements::ValueType::I32), - closure: Box::new(runtime.storage().sizer()), - } - ); user_functions.insert("_storage_read".to_owned(), interpreter::UserFunction { - params: vec![elements::ValueType::I32], + 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], + 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) @@ -87,12 +81,13 @@ fn main() { let descriptor = call_args::init( &*program.module("env").expect("env module to exist"), &runtime, - &[], - &[0u8; 128], + &[3u8; 128], ).expect("call descriptor initialization to succeed"); // Invoke _call method of the module - module_instance.execute_export("_call", vec![descriptor.into()]).expect("_call to execute successfully"); + let return_ptr = module_instance.execute_export("_call", vec![descriptor.into()]) + .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 83ecf35..fcc8989 100644 --- a/rust-runner/src/runtime.rs +++ b/rust-runner/src/runtime.rs @@ -1,5 +1,6 @@ use std::sync::Arc; use std::cell::{Cell, RefCell}; +use std::collections::HashMap; use parity_wasm::{interpreter, elements}; use {alloc, gas_counter, storage}; @@ -9,7 +10,7 @@ pub struct RuntimeEnv { pub gas_counter: Cell, pub gas_limit: u64, pub dynamic_top: Cell, - pub storage: RefCell>, + pub storage: RefCell>, } #[derive(Default, Clone)] @@ -21,7 +22,7 @@ impl Runtime { gas_counter: Cell::new(0), gas_limit: gas_limit, dynamic_top: Cell::new(stack_space), - storage: Default::default(), + storage: RefCell::new(HashMap::new()), })) } diff --git a/rust-runner/src/storage.rs b/rust-runner/src/storage.rs index 4e97a28..246d688 100644 --- a/rust-runner/src/storage.rs +++ b/rust-runner/src/storage.rs @@ -4,6 +4,37 @@ 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, } @@ -18,46 +49,6 @@ impl Storage { } } - pub fn read(&self, module: &interpreter::ModuleInstance, offset: u32, len: u32, dst: u32) -> i32 { - let data = self.runtime.env().storage.borrow(); - - let memory = match module.memory(DEFAULT_MEMORY_INDEX) { - Err(_) => { return -1; }, - Ok(memory) => memory, - }; - - match memory.set(dst, &data[offset as usize..offset as usize + len as usize]) { - Err(_) => { return -1; } - Ok(_) => { return len as i32; } - } - } - - pub fn write(&mut self, module: &interpreter::ModuleInstance, offset: u32, len: u32, src: u32) -> i32 { - let mut data = self.runtime.env().storage.borrow_mut(); - - let memory = match module.memory(DEFAULT_MEMORY_INDEX) { - Err(_) => { return -1; }, - Ok(memory) => memory, - }; - - let slice = match memory.get(src, len as usize) { - Err(_) => { return -1; } - Ok(slice) => slice, - }; - - if data.len() < offset as usize + slice.len() { - data.reserve(offset as usize + slice.len()); - unsafe { - data.set_len(offset as usize + slice.len()); - } - } - data[offset as usize..offset as usize + slice.len()].copy_from_slice(&slice[..]); - - slice.len() as i32 - } - - pub fn size(&self, _module: &interpreter::ModuleInstance) -> u32 { self.runtime.env().storage.borrow().len() as u32 } - pub fn writer(self) -> StorageWrite { StorageWrite(self) } @@ -65,10 +56,6 @@ impl Storage { pub fn reader(self) -> StorageRead { StorageRead(self) } - - pub fn sizer(self) -> StorageSize { - StorageSize(self) - } } pub struct StorageWrite(Storage); @@ -78,11 +65,24 @@ impl interpreter::UserFunctionInterface for StorageWrite { module: &interpreter::ModuleInstance, context: interpreter::CallerContext, ) -> Result, interpreter::Error> { - let offset = context.value_stack.pop_as::()?; - let len = context.value_stack.pop_as::()?; - let ptr = context.value_stack.pop_as::()?; - Ok(Some(self.0.write(module, offset as u32, len as u32, ptr as u32).into())) + // 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())) } } @@ -93,22 +93,26 @@ impl interpreter::UserFunctionInterface for StorageRead { module: &interpreter::ModuleInstance, context: interpreter::CallerContext, ) -> Result, interpreter::Error> { - let offset = context.value_stack.pop_as::()?; - let len = context.value_stack.pop_as::()?; - let ptr = context.value_stack.pop_as::()?; - Ok(Some(self.0.read(module, offset as u32, len as u32, ptr as u32).into())) - } -} + // 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, + }; -pub struct StorageSize(Storage); + 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); -impl interpreter::UserFunctionInterface for StorageSize { - fn call(&mut self, - module: &interpreter::ModuleInstance, - context: interpreter::CallerContext, - ) -> Result, interpreter::Error> - { - Ok(Some((self.0.size(module) as i32).into())) + 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 diff --git a/samples/logger_contract.rs b/samples/logger_contract.rs index 745801a..6e2af87 100644 --- a/samples/logger_contract.rs +++ b/samples/logger_contract.rs @@ -9,52 +9,36 @@ use std::slice; #[link_args = "-s NO_EXIT_RUNTIME=1 -s NO_FILESYSTEM=1 -s"] extern {} -/// Wrapper over storage read/write/size externs -/// Storage api is a file-like with random reads/writes +/// Wrapper over storage read/write externs +/// Storage api is a key-value storage where both key and value are 32 bytes in len mod storage { pub struct Error; #[link(name = "env")] extern { - fn storage_read(offset: u32, len: u32, dst: *mut u8) -> i32; - fn storage_write(offset: u32, len: u32, src: *const u8) -> i32; - fn storage_size() -> u32; + fn storage_read(key: *const u8, dst: *mut u8) -> i32; + fn storage_write(key: *const u8, src: *const u8) -> i32; } - /// Performs read from storage to the specified slice `dst`, using all slice length + /// Performs read from storage to the specified slice `dst` /// Can return `Error` if data is read from outside of the storage boundaries - pub fn read(offset: u32, dst: &mut [u8]) -> Result { + pub fn read(key: &[u8; 32], dst: &mut [u8; 32]) -> Result<(), Error> { match unsafe { - storage_read(offset, dst.len() as u32, dst.as_mut_ptr()) + let mut dst = dst; + storage_read(key.as_ptr(), dst.as_mut_ptr()) } { x if x < 0 => Err(Error), - x => Ok(x as u32), + _ => Ok(()), } } - /// Performs write to the storage from the specified slice `src` - pub fn write(offset: u32, src: &[u8]) -> Result { + /// Performs write to the storage from the specified `src` + pub fn write(key: &[u8; 32], src: &[u8; 32]) -> Result<(), Error> { match unsafe { - storage_write(offset, src.len() as u32, src.as_ptr()) + storage_write(key.as_ptr(), src.as_ptr()) } { x if x < 0 => Err(Error), - x => Ok(x as u32), - } - } - - /// Returns current length of the contract storage - pub fn size() -> u32 { - unsafe { - storage_size() - } - } - - /// Appends the slice content to the end of the storage - pub fn append(src: &[u8]) -> Result { - let sz = size(); - match write(sz, src) { - Ok(_) => Ok(sz), - Err(e) => Err(e), + _ => Ok(()), } } } @@ -136,8 +120,12 @@ pub fn call(descriptor: *mut u8) { // Copies all contract input data to the separate buffer let data = ctx.context().to_vec(); - // Appends all input to the storage (as it is a logger contract) - let _ = storage::append(&data); + let storage_key = [1u8; 32]; + let mut storage_val = [0u8; 32]; + storage_val.copy_from_slice(&data[0..32]); + + // Sets the key [1, 1, 1 ..., 1] to the first 32 bytes of passed input + let _ = storage::write(&storage_key, &mut storage_val); // Returns all that passed to this contract as an output *ctx.result_mut() = data; diff --git a/samples/storage.rs b/samples/storage.rs new file mode 100644 index 0000000..97402a7 --- /dev/null +++ b/samples/storage.rs @@ -0,0 +1,55 @@ +#![feature(link_args)] +#![no_main] + +// as it is experimental preamble +#![allow(dead_code)] + +use std::slice; + +#[link_args = "-s NO_EXIT_RUNTIME=1 -s NO_FILESYSTEM=1 -s"] +extern {} + +/// Wrapper over storage read/write externs +/// Storage api is a key-value storage where both key and value are 32 bytes in len +mod storage { + pub struct Error; + + #[link(name = "env")] + extern { + fn storage_read(key: *const u8, dst: *mut u8) -> i32; + fn storage_write(key: *const u8, src: *const u8) -> i32; + } + + /// Performs read from storage to the specified slice `dst`, using all slice length + /// Can return `Error` if data is read from outside of the storage boundaries + pub fn read(key: &[u8; 32], dst: &mut [u8; 32]) -> Result<(), Error> { + match unsafe { + let mut dst = dst; + storage_read(key.as_ptr(), dst.as_mut_ptr()) + } { + x if x < 0 => Err(Error), + _ => Ok(()), + } + } + + /// Performs write to the storage from the specified slice `src` + pub fn write(key: &[u8; 32], src: &[u8; 32]) -> Result<(), Error> { + match unsafe { + storage_write(key.as_ptr(), src.as_ptr()) + } { + x if x < 0 => Err(Error), + _ => Ok(()), + } + } +} + +#[no_mangle] +pub fn call(_descriptor: *mut u8) { + let storage_key = [1u8; 32]; + let mut storage_val = [2u8; 32]; + let storage_dup_key = [3u8; 32]; + + let _ = storage::write(&storage_key, &storage_val); + let _ = storage::read(&storage_dup_key, &mut storage_val); + let _ = storage::write(&storage_key, &storage_val); +}