diff --git a/Cargo.toml b/Cargo.toml index 604c5f1..a4f1d55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ rusk-abi = { version = "0.12.0-rc.0", default-features = false } [dev-dependencies] rand = "^0.8" -wasmer = "=3.1" +wasmtime = "20" [build-dependencies] schemafy_lib = "0.6" diff --git a/assets/dusk_wallet_core.wasm b/assets/dusk_wallet_core.wasm index a04a5e5..23f63b8 100755 Binary files a/assets/dusk_wallet_core.wasm and b/assets/dusk_wallet_core.wasm differ diff --git a/src/ffi.rs b/src/ffi.rs index 00b9382..3aa61c2 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -6,7 +6,10 @@ //! FFI bindings exposed to WASM module. -use alloc::{vec, vec::Vec}; +use alloc::{ + alloc::{alloc, dealloc, Layout}, + vec::Vec, +}; use core::mem; use dusk_bytes::Serializable; @@ -15,22 +18,28 @@ use sha2::{Digest, Sha512}; use crate::{key, tx, types, utils, MAX_KEY, MAX_LEN}; +/// The alignment of the memory allocated by the FFI. +/// +/// This is 1 because we're not allocating any complex data structures, and +/// just interacting with the memory directly. +const ALIGNMENT: usize = 1; + /// Allocates a buffer of `len` bytes on the WASM memory. #[no_mangle] pub fn allocate(len: i32) -> i32 { - let bytes = vec![0u8; len as usize]; - let ptr = bytes.as_ptr(); - mem::forget(bytes); - ptr as i32 + unsafe { + let layout = Layout::from_size_align_unchecked(len as usize, ALIGNMENT); + let ptr = alloc(layout); + ptr as _ + } } /// Frees a previously allocated buffer on the WASM memory. #[no_mangle] pub fn free_mem(ptr: i32, len: i32) { - let ptr = ptr as *mut u8; - let len = len as usize; unsafe { - Vec::from_raw_parts(ptr, len, len); + let layout = Layout::from_size_align_unchecked(len as usize, ALIGNMENT); + dealloc(ptr as _, layout); } } @@ -53,11 +62,9 @@ pub fn seed(args: i32, len: i32) -> i64 { hash.update(b"SEED"); let seed = hash.finalize().to_vec(); - let ptr = seed.as_ptr() as u32; - let len = seed.len() as u32; - mem::forget(seed); - utils::compose(true, ptr, len) + let (ptr, len) = utils::allocated_copy(seed); + utils::compose(true, ptr as _, len as _) } /// Computes the total balance of the given notes. diff --git a/src/utils.rs b/src/utils.rs index 39f0252..c2c313e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,10 +6,10 @@ //! Misc utilities required by the library implementation. -use crate::{tx, MAX_INPUT_NOTES, MAX_LEN, RNG_SEED}; +use crate::{ffi, tx, MAX_INPUT_NOTES, MAX_LEN, RNG_SEED}; use alloc::vec::Vec; -use core::mem; +use core::ptr; use dusk_bytes::DeserializableSlice; use dusk_jubjub::JubJubScalar; @@ -93,14 +93,9 @@ pub fn into_ptr(response: T) -> i64 where T: Serialize, { - let response = serde_json::to_string(&response).unwrap_or_default(); - let ptr = response.as_ptr() as u32; - let len = response.len() as u32; - let result = compose(true, ptr, len); - - mem::forget(response); - - result + let response = serde_json::to_string(&response).unwrap_or_default().leak(); + let (ptr, len) = allocated_copy(response); + compose(true, ptr as _, len as _) } /// Returns the provided bytes as a pointer @@ -113,13 +108,25 @@ where Err(_) => return fail(), }; - let ptr = bytes.as_ptr() as u32; - let len = bytes.len() as u32; + let (ptr, len) = allocated_copy(bytes); - mem::forget(bytes); compose(true, ptr, len) } +/// Allocated a new buffer, copies the provided bytes to it, and returns the +/// pointer and length of the new buffer. +pub fn allocated_copy>(bytes: B) -> (u32, u32) { + unsafe { + let bytes = bytes.as_ref(); + let len = bytes.len(); + + let ptr = ffi::allocate(bytes.len() as _); + ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as _, len); + + (ptr as _, len as _) + } +} + /// Creates a secure RNG directly a seed. pub fn rng(seed: [u8; 32]) -> ChaCha12Rng { ChaCha12Rng::from_seed(seed) diff --git a/tests/wallet.rs b/tests/wallet.rs index 9cf60fe..f785782 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -19,7 +19,7 @@ use phoenix_core::Crossover; use rusk_abi::ContractId; use serde::{Deserialize, Serialize}; use serde_json::json; -use wasmer::{imports, Instance, Module, Store, Value}; +use wasmtime::{Engine, Instance, Module, Store, Val}; #[test] fn seed_works() { @@ -356,7 +356,7 @@ mod node { } pub struct Wallet { - pub store: Store, + pub store: Store<()>, pub module: Module, pub instance: Instance, } @@ -386,21 +386,19 @@ impl<'a> CallResult<'a> { self.wallet .instance - .exports - .get_memory("memory") - .unwrap() - .view(&self.wallet.store) - .read(self.val as u64, &mut bytes) + .get_memory(&mut self.wallet.store, "memory") + .expect("There should be one memory") + .read(&mut self.wallet.store, self.val as usize, &mut bytes) .unwrap(); self.wallet .instance - .exports - .get_function("free_mem") - .unwrap() + .get_func(&mut self.wallet.store, "free_mem") + .expect("free_mem should exist") .call( &mut self.wallet.store, - &[Value::I32(self.val as i32), Value::I32(self.aux as i32)], + &[Val::I32(self.val as i32), Val::I32(self.aux as i32)], + &mut [], ) .unwrap(); @@ -429,35 +427,39 @@ impl Wallet { T: Serialize, { let bytes = serde_json::to_string(&args).unwrap(); - let len = Value::I32(bytes.len() as i32); - let ptr = self + + let len_params = [Val::I32(bytes.len() as i32)]; + let mut ptr_results = [Val::I32(0)]; + + let allocate = self .instance - .exports - .get_function("allocate") - .unwrap() - .call(&mut self.store, &[len.clone()]) - .unwrap()[0] - .unwrap_i32(); + .get_func(&mut self.store, "allocate") + .expect("allocate should exist"); - self.instance - .exports - .get_memory("memory") - .unwrap() - .view(&self.store) - .write(ptr as u64, bytes.as_bytes()) + allocate + .call(&mut self.store, &len_params, &mut ptr_results) .unwrap(); - let ptr = Value::I32(ptr); - let result = self - .instance - .exports - .get_function(f) - .unwrap() - .call(&mut self.store, &[ptr, len]) - .unwrap()[0] - .unwrap_i64(); + self.instance + .get_memory(&mut self.store, "memory") + .expect("There should be one memory") + .write( + &mut self.store, + ptr_results[0].unwrap_i32() as usize, + bytes.as_bytes(), + ) + .expect("Writing to memory should succeed"); + + let params = [ptr_results[0].clone(), len_params[0].clone()]; + let mut results = [Val::I64(0)]; - CallResult::new(self, result) + self.instance + .get_func(&mut self.store, f) + .expect("allocate should exist") + .call(&mut self.store, ¶ms, &mut results) + .unwrap(); + + CallResult::new(self, results[0].unwrap_i64()) } } @@ -465,13 +467,14 @@ impl Default for Wallet { fn default() -> Self { const WALLET: &[u8] = include_bytes!("../assets/dusk_wallet_core.wasm"); - let mut store = Store::default(); + let engine = Engine::default(); + let mut store = Store::new(&engine, ()); + let module = - Module::new(&store, WALLET).expect("failed to create wasm module"); + Module::new(&engine, WALLET).expect("failed to create wasm module"); - let import_object = imports! {}; - let instance = Instance::new(&mut store, &module, &import_object) - .expect("failed to instanciate the wasm module"); + let instance = Instance::new(&mut store, &module, &[]) + .expect("failed to instantiate the wasm module"); Self { store,