diff --git a/rust-runner/src/gas_counter.rs b/rust-runner/src/gas_counter.rs new file mode 100644 index 0000000..924147a --- /dev/null +++ b/rust-runner/src/gas_counter.rs @@ -0,0 +1,20 @@ +use parity_wasm::interpreter; +use runtime::Runtime; + +pub struct GasCounter { + pub runtime: Runtime, +} + +impl interpreter::UserFunctionInterface for GasCounter { + fn call(&mut self, 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/main.rs b/rust-runner/src/main.rs index 1f1733d..af0901d 100644 --- a/rust-runner/src/main.rs +++ b/rust-runner/src/main.rs @@ -15,6 +15,7 @@ mod gas_counter; use std::env; use parity_wasm::interpreter::{self, ModuleInstanceInterface}; +use parity_wasm::elements; pub const DEFAULT_MEMORY_INDEX: interpreter::ItemIndex = interpreter::ItemIndex::Internal(0); pub type WasmMemoryPtr = i32; @@ -31,14 +32,36 @@ fn main() { let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - // Second, create program instance - let program = parity_wasm::interpreter::ProgramInstance::new().expect("Program instance to be created"); + // 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()), + } + ); + runtime::user_trap(&mut user_functions, "_emscripten_memcpy_big"); + + let program = parity_wasm::interpreter::ProgramInstance::with_functions(user_functions) + .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"); // Create allocator - let runtime = runtime::Runtime::default(); runtime.allocator().alloc(5*1024*1024).expect("to allocate 5mb successfully"); // reserve stack space // Initialize call descriptor diff --git a/rust-runner/src/runtime.rs b/rust-runner/src/runtime.rs new file mode 100644 index 0000000..ff48a04 --- /dev/null +++ b/rust-runner/src/runtime.rs @@ -0,0 +1,60 @@ +use std::sync::Arc; +use std::cell::Cell; + +use parity_wasm::{interpreter, elements}; +use {alloc, gas_counter}; + +#[derive(Default)] +pub struct RuntimeEnv { + pub gas_counter: Cell, + pub gas_limit: u64, + pub dynamic_top: Cell, +} + +#[derive(Default, Clone)] +pub struct Runtime(Arc); + +impl Runtime { + pub fn with_params(stack_space: u32, gas_limit: u64) -> Runtime { + Runtime(Arc::new(RuntimeEnv { + gas_counter: Cell::new(0), + gas_limit: gas_limit, + dynamic_top: Cell::new(stack_space), + })) + } + + pub fn allocator(&self) -> alloc::Arena { + alloc::Arena { + runtime: self.clone(), + } + } + + pub fn gas_counter(&self) -> gas_counter::GasCounter { + gas_counter::GasCounter { + runtime: 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![elements::ValueType::I32], + result: Some(elements::ValueType::I32), + closure: Box::new(UserTrap(func_str)), + } + ); +} + +struct UserTrap(String); + +impl interpreter::UserFunctionInterface for UserTrap { + fn call(&mut self, context: interpreter::CallerContext) -> Result, interpreter::Error> { + Err(interpreter::Error::Trap(self.0.clone())) + } +} \ No newline at end of file