commit ce3551c60e64d4c3a1e54afe5c0cb4ef67d1a17a
parent 74eb493001e713d6233e3970de1344e986d88209
Author: parazyd <parazyd@dyne.org>
Date: Wed, 9 Mar 2022 15:06:05 +0100
Implement logging facilities.
Diffstat:
4 files changed, 88 insertions(+), 5 deletions(-)
diff --git a/drk-sdk/src/lib.rs b/drk-sdk/src/lib.rs
@@ -1,2 +1,3 @@
pub mod entrypoint;
pub mod error;
+pub mod log;
diff --git a/drk-sdk/src/log.rs b/drk-sdk/src/log.rs
@@ -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);
+}
diff --git a/smart-contract/src/lib.rs b/smart-contract/src/lib.rs
@@ -2,6 +2,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
use drk_sdk::{
entrypoint,
error::{ContractError, ContractResult},
+ msg,
};
use pasta_curves::pallas;
@@ -19,5 +20,7 @@ fn process_instruction(ix: &[u8]) -> ContractResult {
return Err(ContractError::Custom(69))
}
+ msg!("Hello from the VM runtime!");
+
Ok(())
}
diff --git a/src/runtime.rs b/src/runtime.rs
@@ -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::{
@@ -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 {
@@ -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.
@@ -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())
}
@@ -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 {
@@ -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(())
+ }
+}