tImplement logging facilities. - wasm-runtime - A wasm runtime
 (HTM) git clone https://git.parazyd.org/wasm-runtime
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit ce3551c60e64d4c3a1e54afe5c0cb4ef67d1a17a
 (DIR) parent 74eb493001e713d6233e3970de1344e986d88209
 (HTM) 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(())
       +    }
       +}