tImplement logging facilities. - wasm-runtime - A wasm runtime HTML git clone https://git.parazyd.org/wasm-runtime DIR Log DIR Files DIR Refs DIR README --- DIR commit ce3551c60e64d4c3a1e54afe5c0cb4ef67d1a17a DIR parent 74eb493001e713d6233e3970de1344e986d88209 HTML Author: parazyd <parazyd@dyne.org> Date: Wed, 9 Mar 2022 15:06:05 +0100 Implement logging facilities. Diffstat: M drk-sdk/src/lib.rs | 1 + A drk-sdk/src/log.rs | 22 ++++++++++++++++++++++ M smart-contract/src/lib.rs | 3 +++ M src/runtime.rs | 67 ++++++++++++++++++++++++++++--- 4 files changed, 88 insertions(+), 5 deletions(-) --- DIR diff --git a/drk-sdk/src/lib.rs b/drk-sdk/src/lib.rs t@@ -1,2 +1,3 @@ pub mod entrypoint; pub mod error; +pub mod log; DIR diff --git a/drk-sdk/src/log.rs b/drk-sdk/src/log.rs t@@ -0,0 +1,22 @@ +#[macro_export] +macro_rules! msg { + ($msg:expr) => { + $crate::log::drk_log($msg) + }; + ($($arg:tt)*) => ($crate::log::drk_log(&format!($($arg)*))); +} + +#[inline] +pub fn drk_log(message: &str) { + #[cfg(target_arch = "wasm32")] + unsafe { + drk_log_(message.as_ptr(), message.len()); + } + + #[cfg(not(target_arch = "wasm32"))] + println!("{}", message); +} + +extern "C" { + pub fn drk_log_(ptr: *const u8, len: usize); +} DIR diff --git a/smart-contract/src/lib.rs b/smart-contract/src/lib.rs t@@ -2,6 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use drk_sdk::{ entrypoint, error::{ContractError, ContractResult}, + msg, }; use pasta_curves::pallas; t@@ -19,5 +20,7 @@ fn process_instruction(ix: &[u8]) -> ContractResult { return Err(ContractError::Custom(69)) } + msg!("Hello from the VM runtime!"); + Ok(()) } DIR diff --git a/src/runtime.rs b/src/runtime.rs t@@ -1,9 +1,9 @@ use anyhow::{anyhow, Result}; use drk_sdk::entrypoint; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use wasmer::{ - imports, wasmparser::Operator, CompilerConfig, Instance, Memory, Module, Store, Universal, - Value, + imports, wasmparser::Operator, CompilerConfig, Function, HostEnvInitError, Instance, LazyInit, + Memory, Module, Store, Universal, Value, WasmerEnv, }; use wasmer_compiler_singlepass::Singlepass; use wasmer_middlewares::{ t@@ -22,8 +22,15 @@ pub const ENTRYPOINT: &str = "entrypoint"; /// Gas limit for a contract pub const GAS_LIMIT: u64 = 200000; +#[derive(Clone)] +pub struct Env { + pub logs: Arc<Mutex<Vec<String>>>, + pub memory: LazyInit<Memory>, +} + pub struct Runtime { pub(crate) instance: Instance, + pub(crate) env: Env, } impl Runtime { t@@ -54,12 +61,21 @@ impl Runtime { let module = Module::new(&store, wasm_bytes)?; println!("Importing functions..."); - let import_object = imports! {}; + let env = Env { logs: Arc::new(Mutex::new(vec![])), memory: LazyInit::new() }; + let import_object = imports! { + "env" => { + "drk_log_" => Function::new_native_with_env( + &store, + env.clone(), + drk_log, + ), + } + }; println!("Instantiating module..."); let instance = Instance::new(&module, &import_object)?; - Ok(Self { instance }) + Ok(Self { instance, env }) } /// Run the hardcoded [ENTRYPOINT] function with the given payload as input. t@@ -76,12 +92,15 @@ impl Runtime { println!("{:#?}", entrypoint); println!("Executing wasm..."); + let ret = match entrypoint.call(&[Value::I32(mem_offset as i32)]) { Ok(v) => { + self.print_logs(); println!("{}", self.gas_info()); v } Err(e) => { + self.print_logs(); println!("{}", self.gas_info()); return Err(e.into()) } t@@ -101,6 +120,13 @@ impl Runtime { } } + fn print_logs(&self) { + let logs = self.env.logs.lock().unwrap(); + for msg in logs.iter() { + println!("Contract log: {}", msg); + } + } + fn gas_info(&self) -> String { let remaining_points = get_remaining_points(&self.instance); match remaining_points { t@@ -125,3 +151,34 @@ impl Runtime { Ok(self.instance.exports.get_memory(MEMORY)?) } } + +/// Host function for logging strings. This is injected into the runtime. +fn drk_log(env: &Env, ptr: u32, len: u32) { + if let Some(bytes) = env.memory.get_ref().unwrap().read(ptr, len as usize) { + // Piece the string together + let msg = match String::from_utf8(bytes.to_vec()) { + Ok(v) => v, + Err(e) => { + println!("Invalid UTF-8 string: {:?}", e); + return + } + }; + + let mut logs = env.logs.lock().unwrap(); + logs.push(msg); + return + } + + println!("Failed to read any bytes from VM memory"); +} + +impl WasmerEnv for Env { + fn init_with_instance( + &mut self, + instance: &Instance, + ) -> std::result::Result<(), HostEnvInitError> { + let memory: Memory = instance.exports.get_with_generics_weak("memory").unwrap(); + self.memory.initialize(memory); + Ok(()) + } +}