Просмотр исходного кода

wasm: wagie data utility functions

x 3 лет назад
Родитель
Сommit
6cc6bb12b3

+ 18 - 2
example/smart-contract/src/lib.rs

@@ -6,9 +6,9 @@ use darkfi_sdk::{
     msg,
     pasta::pallas,
     tx::FuncCall,
-    util::set_return_data,
+    util::{set_return_data, put_object_bytes, get_object_bytes, get_object_size},
 };
-use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable};
+use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable, WriteExt, ReadExt};
 
 /// Available functions for this contract.
 /// We identify them with the first byte passed in through the payload.
@@ -125,6 +125,19 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
 // Through here, you can branch out into different functions inside
 // this library.
 fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
+    msg!("process_instruction():");
+    msg!("    ix: {:x?}", ix);
+    msg!("    cid: {:x?}", cid);
+
+    //let bytes = [0xde, 0xad, 0xbe, 0xef];
+    let bytes = [0x3a, 0x14, 0x15, 0x92, 0x63, 0x35];
+    let obj = put_object_bytes(&bytes);
+    let obj_size = get_object_size(obj as u32);
+    msg!("    obj_size: {}", obj_size);
+    let mut buf = vec![0u8; obj_size as usize];
+    get_object_bytes(&mut buf, obj as u32);
+    msg!("    buf (bytes): {:x?}", &buf);
+
     match Function::from(ix[0]) {
         Function::Foo => {
             let tx_data = &ix[1..];
@@ -158,12 +171,14 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
         }
     }
 
+    msg!("process_instruction() [END]");
     Ok(())
 }
 
 fn process_update(_cid: ContractId, update_data: &[u8]) -> ContractResult {
     msg!("Make 1 update!");
 
+    /*
     match Function::from(update_data[0]) {
         Function::Foo => {
             msg!("fooupp");
@@ -177,6 +192,7 @@ fn process_update(_cid: ContractId, update_data: &[u8]) -> ContractResult {
         }
         _ => unreachable!(),
     }
+    */
 
     msg!("process_update() finished");
     Ok(())

+ 7 - 4
src/runtime/import/db.rs

@@ -283,7 +283,7 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
 /// ```
 ///     value = db_get(db_handle, key);
 /// ```
-pub(crate) fn db_get(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+pub(crate) fn db_get(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
     let env = ctx.data();
     match env.contract_section {
         ContractSection::Deploy | ContractSection::Exec | ContractSection::Update => {
@@ -343,12 +343,15 @@ pub(crate) fn db_get(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
                 }
             };
 
-            if ret.is_none() {
+            let Some(return_data) = ret else {
                 log::debug!("returned empty vec");
                 return -3
-            }
+            };
 
-            0
+            // Copy Vec<u8> to the VM
+            let mut objects = env.objects.borrow_mut();
+            objects.push(return_data);
+            (objects.len() - 1) as i64
         }
         _ => -1,
     }

+ 90 - 0
src/runtime/import/util.rs

@@ -17,9 +17,11 @@
  */
 
 use log::{debug, error};
+use std::io::Cursor;
 use wasmer::{FunctionEnvMut, WasmPtr};
 
 use crate::runtime::vm_runtime::{ContractSection, Env};
+use darkfi_serial::ReadExt;
 
 /// Host function for logging strings.
 /// This is injected into the runtime with wasmer's `imports!` macro.
@@ -63,3 +65,91 @@ pub(crate) fn set_return_data(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u
         _ => darkfi_sdk::error::CALLER_ACCESS_DENIED,
     }
 }
+
+pub(crate) fn put_object_bytes(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
+    let env = ctx.data();
+    let memory_view = env.memory_view(&ctx);
+    let db = &env.blockchain.sled_db;
+    let contracts = &env.blockchain.contracts;
+    let contract_id = &env.contract_id;
+
+    //debug!(target: "wasm_runtime::diagnostic", "diagnostic:");
+    //let pages = memory_view.size().0;
+    //debug!(target: "wasm_runtime::diagnostic", "    pages: {}", pages);
+
+    let Ok(slice) = ptr.slice(&memory_view, len) else {
+        error!(target: "wasm_runtime::diagnostic", "Failed to make slice from ptr");
+        return -2
+    };
+
+    let mut buf = vec![0_u8; len as usize];
+    if let Err(e) = slice.read_slice(&mut buf) {
+        error!(target: "wasm_runtime::diagnostic", "Failed to read from memory slice: {}", e);
+        return -2
+    };
+
+    // There would be a serious problem if this is zero.
+    // The number of pages is calculated as a quantity X + 1 where X >= 0
+    //assert!(pages > 0);
+
+    //debug!(target: "wasm_runtime::diagnostic", "    memory: {:02x?}", &buf[0..32]);
+    //debug!(target: "wasm_runtime::diagnostic", "            {:x?}", &buf[32..64]);
+
+    //debug!(target: "wasm_runtime::diagnostic", "    ptr location: {}", ptr.offset());
+
+    let mut objects = env.objects.borrow_mut();
+    objects.push(buf);
+    let obj_idx = objects.len() - 1;
+
+    obj_idx as i64
+}
+
+pub(crate) fn get_object_bytes(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, idx: u32) -> i64 {
+    // Get the slice, where we will read the size of the buffer
+
+    let env = ctx.data();
+    let memory_view = env.memory_view(&ctx);
+
+    // Get the object from env
+
+    let objects = env.objects.borrow();
+    if idx as usize >= objects.len() {
+        error!(target: "wasm_runtime::get_object_bytes", "Tried to access object out of bounds");
+        return -5
+    }
+    let obj = &objects[idx as usize];
+
+    // Read N bytes from the object and write onto the ptr.
+
+    // We need to re-read the slice, since in the first run, we just read n
+    let Ok(slice) = ptr.slice(&memory_view, obj.len() as u32) else {
+        error!(target: "wasm_runtime::get_object_bytes", "Failed to make slice from ptr");
+        return -2
+    };
+
+    // Put the result in the VM
+    if let Err(e) = slice.write_slice(&obj) {
+        error!(target: "wasm_runtime::get_object_bytes", "Failed to write to memory slice: {}", e);
+        return -4
+    };
+
+    0
+}
+
+pub(crate) fn get_object_size(ctx: FunctionEnvMut<Env>, idx: u32) -> i64 {
+    // Get the slice, where we will read the size of the buffer
+
+    let env = ctx.data();
+    let memory_view = env.memory_view(&ctx);
+
+    // Get the object from env
+
+    let objects = env.objects.borrow();
+    if idx as usize >= objects.len() {
+        error!(target: "wasm_runtime::get_object_bytes", "Tried to access object out of bounds");
+        return -5
+    }
+
+    let obj = &objects[idx as usize];
+    obj.len() as i64
+}

+ 23 - 0
src/runtime/vm_runtime.rs

@@ -87,6 +87,8 @@ pub struct Env {
     pub logs: RefCell<Vec<String>>,
     /// Direct memory access to the VM
     pub memory: Option<Memory>,
+    /// Object store for transferring memory from the host to VM
+    pub objects: RefCell<Vec<Vec<u8>>>,
 }
 
 impl Env {
@@ -161,6 +163,7 @@ impl Runtime {
                 contract_return_data: Cell::new(None),
                 logs,
                 memory: None,
+                objects: RefCell::new(vec![]),
             },
         );
 
@@ -201,6 +204,24 @@ impl Runtime {
                     &ctx,
                     import::db::db_set,
                 ),
+
+                "put_object_bytes_" => Function::new_typed_with_env(
+                    &mut store,
+                    &ctx,
+                    import::util::put_object_bytes,
+                ),
+
+                "get_object_bytes_" => Function::new_typed_with_env(
+                    &mut store,
+                    &ctx,
+                    import::util::get_object_bytes,
+                ),
+
+                "get_object_size_" => Function::new_typed_with_env(
+                    &mut store,
+                    &ctx,
+                    import::util::get_object_size,
+                ),
             }
         };
 
@@ -220,6 +241,8 @@ impl Runtime {
         env_mut.contract_section = section;
         assert!(env_mut.contract_return_data.take().is_none());
         env_mut.contract_return_data.set(None);
+        // Clear the logs
+        let _ = env_mut.logs.take();
 
         // Serialize the payload for the format the wasm runtime is expecting.
         let payload = Self::serialize_payload(&env_mut.contract_id, payload);

+ 10 - 4
src/sdk/src/db.rs

@@ -3,6 +3,7 @@ use darkfi_serial::Encodable;
 use super::{
     crypto::ContractId,
     error::{ContractError, GenericResult},
+    util::{get_object_bytes, get_object_size},
 };
 
 type DbHandle = u32;
@@ -71,13 +72,13 @@ pub fn db_lookup(contract_id: ContractId, db_name: &str) -> GenericResult<DbHand
 /// ```
 pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Option<Vec<u8>>> {
     #[cfg(target_arch = "wasm32")]
-    unsafe {
+    {
         let mut len = 0;
         let mut buf = vec![];
         len += db_handle.encode(&mut buf)?;
         len += key.to_vec().encode(&mut buf)?;
 
-        let ret = db_get_(buf.as_ptr(), len as u32);
+        let ret = unsafe { db_get_(buf.as_ptr(), len as u32) };
 
         if ret < 0 {
             match ret {
@@ -88,7 +89,12 @@ pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Option<Vec<u8>>>
             }
         }
 
-        Ok(Some(vec![]))
+        let obj = ret as u32;
+        let obj_size = get_object_size(obj);
+        let mut buf = vec![0u8; obj_size as usize];
+        get_object_bytes(&mut buf, obj);
+
+        Ok(Some(buf))
     }
 
     #[cfg(not(target_arch = "wasm32"))]
@@ -126,6 +132,6 @@ pub fn db_set(db_handle: DbHandle, key: &[u8], value: &[u8]) -> GenericResult<()
 extern "C" {
     fn db_init_(ptr: *const u8, len: u32) -> i32;
     fn db_lookup_(ptr: *const u8, len: u32) -> i32;
-    fn db_get_(ptr: *const u8, len: u32) -> i32;
+    fn db_get_(ptr: *const u8, len: u32) -> i64;
     fn db_set_(ptr: *const u8, len: u32) -> i32;
 }

+ 33 - 0
src/sdk/src/util.rs

@@ -13,7 +13,40 @@ pub fn set_return_data(data: &[u8]) -> Result<(), ContractError> {
     unimplemented!();
 }
 
+pub fn put_object_bytes(data: &[u8]) -> i64 {
+    #[cfg(target_arch = "wasm32")]
+    unsafe {
+        return put_object_bytes_(data.as_ptr(), data.len() as u32)
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    unimplemented!();
+}
+
+pub fn get_object_bytes(data: &mut [u8], object_index: u32) -> i64 {
+    #[cfg(target_arch = "wasm32")]
+    {
+        unsafe { return get_object_bytes_(data.as_mut_ptr(), object_index as u32) }
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    unimplemented!();
+}
+
+pub fn get_object_size(object_index: u32) -> i64 {
+    #[cfg(target_arch = "wasm32")]
+    unsafe {
+        return get_object_size_(object_index as u32)
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    unimplemented!();
+}
+
 #[cfg(target_arch = "wasm32")]
 extern "C" {
     fn set_return_data_(ptr: *const u8, len: u32) -> i64;
+    fn put_object_bytes_(ptr: *const u8, len: u32) -> i64;
+    fn get_object_bytes_(ptr: *mut u8, len: u32) -> i64;
+    fn get_object_size_(len: u32) -> i64;
 }