fully working rust runner

This commit is contained in:
NikVolf
2017-05-16 16:10:04 +03:00
parent 20496ae9b2
commit 2f691bb360
10 changed files with 179 additions and 127 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
Cargo.lock
target
target
.cargo
+1 -1
View File
@@ -5,4 +5,4 @@ authors = ["NikVolf <nikvolf@gmail.com>"]
[dependencies]
parity-wasm = { git="https://github.com/nikvolf/parity-wasm" }
wasm_utils = { path = "../" }
wasm-utils = { path = "../" }
Regular → Executable
View File
Regular → Executable
View File
+26 -18
View File
@@ -31,11 +31,8 @@ impl From<interpreter::Error> for Error {
pub fn init(
env: &interpreter::ModuleInstanceInterface,
runtime: &runtime::Runtime,
context: &[u8],
input: &[u8],
) -> Result<WasmMemoryPtr, Error> {
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)
}
}
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<Vec<u8>, Error> {
// let memory = env.memory(DEFAULT_MEMORY_INDEX)?;
// }
+7 -12
View File
@@ -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");
// ???
}
+3 -2
View File
@@ -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<u64>,
pub gas_limit: u64,
pub dynamic_top: Cell<u32>,
pub storage: RefCell<Vec<u8>>,
pub storage: RefCell<HashMap<storage::StorageKey, storage::StorageValue>>,
}
#[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()),
}))
}
+66 -62
View File
@@ -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<u8>) -> Result<Self, Error> {
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<u8>) -> Result<Self, Error> {
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<Option<interpreter::RuntimeValue>, interpreter::Error> {
let offset = context.value_stack.pop_as::<i32>()?;
let len = context.value_stack.pop_as::<i32>()?;
let ptr = context.value_stack.pop_as::<i32>()?;
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::<i32>()?;
let key_ptr = context.value_stack.pop_as::<i32>()?;
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<Option<interpreter::RuntimeValue>, interpreter::Error> {
let offset = context.value_stack.pop_as::<i32>()?;
let len = context.value_stack.pop_as::<i32>()?;
let ptr = context.value_stack.pop_as::<i32>()?;
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::<i32>()?;
let key_ptr = context.value_stack.pop_as::<i32>()?;
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<Option<interpreter::RuntimeValue>, 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()))
}
}
+19 -31
View File
@@ -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<u32, Error> {
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<u32, Error> {
/// 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<u32, Error> {
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;
+55
View File
@@ -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);
}