Jelajahi Sumber

wasm: first working function drk_log. Notes:

* Env no longer needs to be Copy in API upgrade. Begin removing Arcs.
  Env is now passed around as a ref (we should check how this works
  since lifetimes are somehow hidden).
* Switch from internal MemoryManipulation to library functions using
  WasmPtr. Can possibly be deprecated later.
* Add MemoryView convenience functions used in 3.0 API.
* Engine has been removed.
x 3 tahun lalu
induk
melakukan
080c2edfbd
3 mengubah file dengan 59 tambahan dan 43 penghapusan
  1. 5 4
      src/runtime/chain_state.rs
  2. 15 20
      src/runtime/util.rs
  3. 39 19
      src/runtime/vm_runtime.rs

+ 5 - 4
src/runtime/chain_state.rs

@@ -18,13 +18,14 @@
 
 use darkfi_sdk::crypto::{MerkleNode, Nullifier};
 use log::{debug, error};
+use wasmer::FunctionEnvMut;
 
 use super::{memory::MemoryManipulation, vm_runtime::Env};
 use crate::node::state::ProgramState;
 
 /// Try to read a `Nullifier` from the given pointer and check if it's
 /// an existing nullifier in the blockchain state machine.
-pub fn nullifier_exists(env: &Env, ptr: u32, len: u32) -> i32 {
+pub fn nullifier_exists(mut env: FunctionEnvMut<Env>, ptr: u32, len: u32) -> i32 {
     /*
     if let Some(bytes) = env.memory.get_ref().unwrap().read(ptr, len as usize) {
         debug!(target: "wasm_runtime::nullifier_exists", "Read bytes: {:?}", bytes);
@@ -45,15 +46,15 @@ pub fn nullifier_exists(env: &Env, ptr: u32, len: u32) -> i32 {
             false => return 0,
         }
     }
+    */
 
     error!(target: "wasm_runtime::nullifier_exists", "Failed to read bytes from VM memory");
-    */
     -2
 }
 
 /// Try to read a `MerkleNode` from the given pointer and check if it's
 /// a valid Merkle root in the chain's Merkle tree.
-pub fn is_valid_merkle(env: &Env, ptr: u32, len: u32) -> i32 {
+pub fn is_valid_merkle(mut env: FunctionEnvMut<Env>, ptr: u32, len: u32) -> i32 {
     /*
     if let Some(bytes) = env.memory.get_ref().unwrap().read(ptr, len as usize) {
         debug!(target: "wasm_runtime::is_valid_merkle", "Read bytes: {:?}", bytes);
@@ -74,8 +75,8 @@ pub fn is_valid_merkle(env: &Env, ptr: u32, len: u32) -> i32 {
             false => return 0,
         }
     }
+    */
 
     error!(target: "wasm_runtime::is_valid_merkle", "Failed to read bytes from VM memory");
-    */
     -2
 }

+ 15 - 20
src/runtime/util.rs

@@ -17,6 +17,7 @@
  */
 
 use log::{error, warn};
+use wasmer::{FunctionEnvMut, AsStoreRef, WasmPtr};
 
 use super::{memory::MemoryManipulation, vm_runtime::Env};
 
@@ -35,25 +36,19 @@ pub fn serialize_payload(payload: &[u8]) -> Vec<u8> {
 
 /// Host function for logging strings.
 /// This is injected into the runtime with wasmer's `imports!` macro.
-pub(crate) fn drk_log(env: &Env, ptr: u32, len: u32) {
-    // DISABLED
-    /*
-    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) => {
-                warn!(target: "wasm_runtime", "Invalid UTF-8 string: {:?}", e);
-                return
-            }
-        };
-
-        let mut logs = env.logs.lock().unwrap();
-        logs.push(msg);
-        std::mem::drop(logs);
-        return
+pub(crate) fn drk_log(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) {
+    let env = ctx.data();
+    let memory_view = env.memory_view(&ctx);
+
+    match ptr.read_utf8_string(&memory_view, len) {
+        Ok(msg) => {
+            let mut logs = env.logs.borrow_mut();
+            logs.push(msg);
+            std::mem::drop(logs);
+        },
+        Err(_) => {
+            error!(target: "wasm_runtime::drk_log", "Failed to UTF-8 string from VM memory");
+        }
     }
-
-    error!(target: "wasm_runtime::drk_log", "Failed to read any bytes from VM memory");
-    */
 }
+

+ 39 - 19
src/runtime/vm_runtime.rs

@@ -16,13 +16,16 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::sync::{Arc, Mutex};
+use std::{
+    cell::RefCell,
+    sync::{Arc, Mutex},
+};
 
 use darkfi_sdk::entrypoint;
 use log::{debug, info};
 use wasmer::{
-    imports, wasmparser::Operator, CompilerConfig, Function, FunctionEnv, Instance,
-    Memory, Module, Store, Value,
+    imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance,
+    Memory, MemoryView, Module, Store, Value,
 };
 use wasmer_compiler_singlepass::Singlepass;
 use wasmer_middlewares::{
@@ -50,10 +53,9 @@ pub const ENTRYPOINT: &str = "entrypoint";
 const GAS_LIMIT: u64 = 200000;
 
 /// The wasm vm runtime instantiated for every smart contract that runs.
-#[derive(Clone)]
 pub struct Env {
     /// Logs produced by the contract
-    pub logs: Arc<Mutex<Vec<String>>>,
+    pub logs: RefCell<Vec<String>>,
     /// Direct memory access to the VM
     pub memory: Option<Memory>,
     /// Cloned state machine living in memory
@@ -62,6 +64,24 @@ pub struct Env {
     pub state_updates: Arc<Mutex<Vec<StateUpdate>>>,
 }
 
+impl Env {
+    /// Providers safe access to the memory
+    /// (it must be initialized before it can be used)
+    ///
+    ///     // ctx: FunctionEnvMut<Env>
+    ///     let env = ctx.data();
+    ///     let memory = env.memory_view(&ctx);
+    ///
+    pub fn memory_view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> {
+        self.memory().view(store)
+    }
+
+    /// Get memory, that needs to have been set fist
+    pub fn memory(&self) -> &Memory {
+        self.memory.as_ref().unwrap()
+    }
+}
+
 /*
 impl WasmerEnv for Env {
     fn init_with_instance(
@@ -87,7 +107,7 @@ pub struct ExecutionResult {
 
 pub struct Runtime {
     pub instance: Instance,
-    //pub env: Env,
+    pub env: FunctionEnv<Env>,
 }
 
 impl Runtime {
@@ -120,44 +140,44 @@ impl Runtime {
         debug!(target: "wasm_runtime::new", "Compiling module");
         let module = Module::new(&store, wasm_bytes)?;
 
+        // This section will need changing
         debug!(target: "wasm_runtime::new", "Importing functions");
-        let logs = Arc::new(Mutex::new(vec![]));
+        let logs = RefCell::new(vec![]);
         let state_machine = Arc::new(state_machine);
         let state_updates = Arc::new(Mutex::new(vec![]));
 
-        let env = FunctionEnv::new(&mut store, Env { logs, memory: None, state_machine, state_updates });
+        let env =
+            FunctionEnv::new(&mut store, Env { logs, memory: None, state_machine, state_updates });
 
         let imports = imports! {
             "env" => {
-                /*
                 "drk_log_" => Function::new_typed_with_env(
-                    &store,
-                    env.clone(),
+                    &mut store,
+                    &env,
                     drk_log,
                 ),
 
                 "nullifier_exists_" => Function::new_typed_with_env(
-                    &store,
-                    env.clone(),
+                    &mut store,
+                    &env,
                     nullifier_exists,
                 ),
 
                 "is_valid_merkle_" => Function::new_typed_with_env(
-                    &store,
-                    env.clone(),
+                    &mut store,
+                    &env,
                     is_valid_merkle,
                 ),
-                */
             }
         };
 
         debug!(target: "wasm_runtime::new", "Instantiating module");
         let instance = Instance::new(&mut store, &module, &imports)?;
 
-        //let mut env_mut = env.as_mut(&mut store);
-        //env_mut.memory = Some(instance.exports.get_with_generics_weak(MEMORY)?);
+        let mut env_mut = env.as_mut(&mut store);
+        env_mut.memory = Some(instance.exports.get_with_generics(MEMORY)?);
 
-        Ok(Self { instance /*, env */ })
+        Ok(Self { instance, env })
     }
 
     /// Run the hardcoded `ENTRYPOINT` function with the given payload as input.