Browse Source

(semi-)working wasm contract

x 3 years ago
parent
commit
f35fd6ab96

+ 3 - 1
example/smart-contract/src/lib.rs

@@ -37,9 +37,11 @@ pub struct Args {
 // Through here, you can branch out into different functions inside
 // this library.
 entrypoint!(process_instruction);
-fn process_instruction(_state: &[u8], ix: &[u8]) -> ContractResult {
+fn process_instruction(/*_state: &[u8], */ ix: &[u8]) -> ContractResult {
+    msg!("Hello from the VM runtime!");
     // Deserialize the payload into `Args`.
     let args: Args = deserialize(ix)?;
+    msg!("deserializing payload worked");
 
     if args.a < args.b {
         // Returning custom errors

+ 4 - 2
src/runtime/chain_state.rs

@@ -49,7 +49,8 @@ pub fn nullifier_exists(mut env: FunctionEnvMut<Env>, ptr: u32, len: u32) -> i32
     */
 
     error!(target: "wasm_runtime::nullifier_exists", "Failed to read bytes from VM memory");
-    -2
+    //-2
+    0
 }
 
 /// Try to read a `MerkleNode` from the given pointer and check if it's
@@ -78,5 +79,6 @@ pub fn is_valid_merkle(mut env: FunctionEnvMut<Env>, ptr: u32, len: u32) -> i32
     */
 
     error!(target: "wasm_runtime::is_valid_merkle", "Failed to read bytes from VM memory");
-    -2
+    //-2
+    0
 }

+ 1 - 1
src/runtime/util.rs

@@ -47,7 +47,7 @@ pub(crate) fn drk_log(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32)
             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 UTF-8 string from VM memory");
         }
     }
 }

+ 35 - 27
src/runtime/vm_runtime.rs

@@ -25,7 +25,7 @@ use darkfi_sdk::entrypoint;
 use log::{debug, info};
 use wasmer::{
     imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance,
-    Memory, MemoryView, Module, Store, Value,
+    Memory, MemoryView, Module, Store, Value, WasmPtr,
 };
 use wasmer_compiler_singlepass::Singlepass;
 use wasmer_middlewares::{
@@ -107,7 +107,8 @@ pub struct ExecutionResult {
 
 pub struct Runtime {
     pub instance: Instance,
-    pub env: FunctionEnv<Env>,
+    pub store: Store,
+    pub ctx: FunctionEnv<Env>,
 }
 
 impl Runtime {
@@ -146,26 +147,26 @@ impl Runtime {
         let state_machine = Arc::new(state_machine);
         let state_updates = Arc::new(Mutex::new(vec![]));
 
-        let env =
+        let ctx =
             FunctionEnv::new(&mut store, Env { logs, memory: None, state_machine, state_updates });
 
         let imports = imports! {
             "env" => {
                 "drk_log_" => Function::new_typed_with_env(
                     &mut store,
-                    &env,
+                    &ctx,
                     drk_log,
                 ),
 
                 "nullifier_exists_" => Function::new_typed_with_env(
                     &mut store,
-                    &env,
+                    &ctx,
                     nullifier_exists,
                 ),
 
                 "is_valid_merkle_" => Function::new_typed_with_env(
                     &mut store,
-                    &env,
+                    &ctx,
                     is_valid_merkle,
                 ),
             }
@@ -174,31 +175,39 @@ impl Runtime {
         debug!(target: "wasm_runtime::new", "Instantiating module");
         let instance = Instance::new(&mut store, &module, &imports)?;
 
-        let mut env_mut = env.as_mut(&mut store);
+        let mut env_mut = ctx.as_mut(&mut store);
         env_mut.memory = Some(instance.exports.get_with_generics(MEMORY)?);
 
-        Ok(Self { instance, env })
+        Ok(Self { instance, store, ctx })
     }
 
     /// Run the hardcoded `ENTRYPOINT` function with the given payload as input.
     pub fn run(&mut self, payload: &[u8]) -> Result<()> {
-        /*
+        // TODO: needs factoring
+        let mem_offset = self.guest_mem_alloc(payload.len())?;
+        {
+            let env = self.ctx.as_ref(&self.store);
+            let memory_view = env.memory_view(&self.store);
+            let ptr: WasmPtr<u8> = WasmPtr::new(mem_offset);
+            let slice = ptr.slice(&memory_view, payload.len() as u32).expect("FIXME FIXME FIXME");
+            slice.write_slice(payload).expect("FIXME FIXME FIXME");
+        }
+
         // Get module linear memory
-        let memory = self.memory()?;
+        //let memory = self.memory()?;
 
         // Retrieve ptr to pass data, and write the payload into the vm memory
-        let mem_offset = self.guest_mem_alloc(payload.len())?;
-        memory.write(mem_offset, payload)?;
+        //memory.write(mem_offset, payload)?;
 
         debug!(target: "wasm_runtime::run", "Getting entrypoint function");
         let entrypoint = self.instance.exports.get_function(ENTRYPOINT)?;
 
         debug!(target: "wasm_runtime::run", "Executing wasm");
-        let ret = match entrypoint.call(&[Value::I32(mem_offset as i32)]) {
-            Ok(v) => {
+        let ret = match entrypoint.call(&mut self.store, &[Value::I32(mem_offset as i32)]) {
+            Ok(retvals) => {
                 self.print_logs();
                 debug!(target: "wasm_runtime::run", "{}", self.gas_info());
-                v
+                retvals
             }
             Err(e) => {
                 self.print_logs();
@@ -220,16 +229,13 @@ impl Runtime {
             // _ => Err(ContractError(retval)),
             _ => todo!(),
         }
-        */
-        Ok(())
     }
 
     fn print_logs(&self) {
-        // DISABLED
-        /*let logs = self.env.logs.lock().unwrap();
+        let logs = self.ctx.as_ref(&self.store).logs.borrow();
         for msg in logs.iter() {
             debug!(target: "wasm_runtime::run", "Contract log: {}", msg);
-        }*/
+        }
     }
 
     fn gas_info(&self) -> String {
@@ -246,16 +252,18 @@ impl Runtime {
             }
         }
         */
-        "foo".to_string()
+        "(gas info temporarily disabled)".to_string()
     }
 
     /// Allocate some memory space on a wasm linear memory to allow direct rw.
-    fn guest_mem_alloc(&self, size: usize) -> Result<u32> {
-        // DISABLED
-        //let mem_alloc = self.instance.exports.get_function(WASM_MEM_ALLOC)?;
-        //let res_target_ptr = mem_alloc.call(&[Value::I32(size as i32)])?.to_vec();
-        //Ok(res_target_ptr[0].unwrap_i32() as u32)
-        Ok(0)
+    // TODO: we should maybe use memory.grow() instead which expands memory by pages
+    //       https://docs.rs/wasmer/3.0.0-rc.1/wasmer/struct.Memory.html#method.grow
+    // TODO: https://docs.rs/wasmer/3.0.0-rc.1/wasmer/constant.WASM_PAGE_SIZE.html
+    fn guest_mem_alloc(&mut self, size: usize) -> Result<u32> {
+        let mem_alloc_func = self.instance.exports.get_function(WASM_MEM_ALLOC)?;
+        let res_target_ptr =
+            mem_alloc_func.call(&mut self.store, &[Value::I32(size as i32)])?.to_vec();
+        Ok(res_target_ptr[0].unwrap_i32() as u32)
     }
 
     /// Retrieve linear memory from a wasm module and return its reference.

+ 9 - 4
src/sdk/src/entrypoint.rs

@@ -32,9 +32,10 @@ macro_rules! entrypoint {
         /// # Safety
         #[no_mangle]
         pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
-            let (state, instruction_data) = $crate::entrypoint::deserialize(input);
+            //let (state, instruction_data) = $crate::entrypoint::deserialize(input);
+            let instruction_data = $crate::entrypoint::deserialize(input);
 
-            match $process_instruction(&state, &instruction_data) {
+            match $process_instruction(/*&state, */ &instruction_data) {
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Err(e) => e.into(),
             }
@@ -44,19 +45,23 @@ macro_rules! entrypoint {
 
 /// Deserialize a given payload in `entrypoint`
 /// # Safety
-pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a [u8], &'a [u8]) {
+//pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a [u8], &'a [u8]) {
+pub unsafe fn deserialize<'a>(input: *mut u8) -> &'a [u8] {
     let mut offset: usize = 0;
 
+    /*
     let state_data_len = *(input.add(offset) as *const u64) as usize;
     offset += size_of::<u64>();
     let state_data = { from_raw_parts(input.add(offset), state_data_len) };
     offset += state_data_len;
+    */
 
     let instruction_data_len = *(input.add(offset) as *const u64) as usize;
     offset += size_of::<u64>();
     let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
 
-    (state_data, instruction_data)
+    //(state_data, instruction_data)
+    instruction_data
 }
 
 /// Allocate a piece of memory in the wasm VM