mirror of
https://github.com/fluencelabs/wasm-utils
synced 2025-03-16 03:20:50 +00:00
working refactored runtime
This commit is contained in:
parent
b9acb877bc
commit
360c308e09
@ -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)
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user