working refactored runtime

This commit is contained in:
NikVolf 2017-05-19 16:46:34 +03:00
parent b9acb877bc
commit 360c308e09
3 changed files with 146 additions and 89 deletions

View File

@ -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<runtime::ErrorAlloc> for Error {
fn from(err: alloc::ErrorAlloc) -> Self {
fn from(err: runtime::ErrorAlloc) -> Self {
Error::Allocator(err)
}
}
@ -29,7 +29,7 @@ impl From<interpreter::Error> for Error {
}
pub fn init(
memory: &interpreter::Memory,
memory: &interpreter::MemoryInstance,
runtime: &mut runtime::Runtime,
input: &[u8],
) -> Result<WasmMemoryPtr, Error> {
@ -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)
}

View File

@ -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");
}
}

View File

@ -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<u8>) -> Result<Self, ErrorStorage> {
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<u8>) -> Result<Self, ErrorStorage> {
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::StorageKey, storage::StorageValue>,
storage: HashMap<StorageKey, StorageValue>,
memory: Arc<interpreter::MemoryInstance>,
}
#[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<interpreter::MemoryInstance>, 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<interpreter::Memory>, context: interpreter::CallerContext)
pub fn storage_write(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
let val_ptr = context.value_stack.pop_as::<i32>()?;
let key_ptr = context.value_stack.pop_as::<i32>()?;
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<interpreter::Memory>, context: interpreter::CallerContext)
pub fn storage_read(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
// 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 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<interpreter::Memory>, context: interpreter::CallerContext)
pub fn malloc(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
let amount = context.value_stack.pop_as::<i32>()? 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<u32, ErrorAlloc> {
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<interpreter::Memory>, context: interpreter::CallerContext)
fn gas(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
let prev = self.gas_counter;
let update = context.value_stack.pop_as::<i32>()? 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<interpreter::Memory>, _context: interpreter::CallerContext)
fn user_trap(&mut self, _context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
Err(interpreter::Error::Trap(self.0.clone()))
Err(interpreter::Error::Trap("unknown trap".to_owned()))
}
fn user_noop(&mut self,
_memory: Arc<interpreter::Memory>,
_context: interpreter::CallerContext
) -> Result<Option<interpreter::RuntimeValue>, interpreter::Error> {
Ok(None)
}
}
impl interpreter::UserFunctionExecutor for Runtime {
fn execute(&mut self, name: &str, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, 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)
}
}
}
}