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

wasm: add skeleton db functionality

x 3 лет назад
Родитель
Сommit
ef7dfb0ba3
4 измененных файлов с 190 добавлено и 0 удалено
  1. 93 0
      src/runtime/import/db.rs
  2. 81 0
      src/sdk/src/db.rs
  3. 13 0
      src/sdk/src/error.rs
  4. 3 0
      src/sdk/src/lib.rs

+ 93 - 0
src/runtime/import/db.rs

@@ -0,0 +1,93 @@
+use darkfi_sdk::crypto::{MerkleNode, Nullifier};
+use log::{debug, error};
+use wasmer::{AsStoreRef, FunctionEnvMut, WasmPtr};
+
+use crate::{
+    node::state::ProgramState,
+    runtime::{
+        memory::MemoryManipulation,
+        vm_runtime::{ContractSection, Env},
+    },
+};
+
+/// Only deploy() can call this. Creates a new database instance for this contract.
+///
+/// ```
+///     type DbHandle = u32;
+///     db_init(db_name) -> DbHandle
+/// ```
+pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Deploy => {
+            let env = ctx.data();
+            let memory_view = env.memory_view(&ctx);
+
+            match ptr.read_utf8_string(&memory_view, len) {
+                Ok(db_name) => {
+                    // TODO:
+                    // * db_name = blake3_hash(contract_id, db_name)
+                    // * create db_name sled database
+                }
+                Err(_) => {
+                    error!(target: "wasm_runtime::drk_log", "Failed to read UTF-8 string from VM memory");
+                    return -2;
+                }
+            }
+            0
+        }
+        _ => -1,
+    }
+}
+
+/// Everyone can call this. Will read a key from the key-value store.
+///
+/// ```
+///     value = db_get(db_handle, key);
+/// ```
+pub(crate) fn db_get(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Update => 0,
+        _ => -1,
+    }
+}
+
+/// Only update() can call this. Starts an atomic transaction.
+///
+/// ```
+///     tx_handle = db_begin_tx();
+/// ```
+pub(crate) fn db_begin_tx(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Update => 0,
+        _ => -1,
+    }
+}
+
+/// Only update() can call this. Set a value within the transaction.
+///
+/// ```
+///     db_set(tx_handle, key, value);
+/// ```
+pub(crate) fn db_set(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Update => 0,
+        _ => -1,
+    }
+}
+
+/// Only update() can call this. This writes the atomic tx to the database.
+///
+/// ```
+///     db_end_tx(db_handle, tx_handle);
+/// ```
+pub(crate) fn db_end_tx(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Update => 0,
+        _ => -1,
+    }
+}

+ 81 - 0
src/sdk/src/db.rs

@@ -0,0 +1,81 @@
+use super::{
+    crypto::{MerkleNode, Nullifier},
+    error::{ContractError, GenericResult},
+};
+
+type DbHandle = u32;
+type TxHandle = u32;
+
+/// Only deploy() can call this. Creates a new database instance for this contract.
+///
+/// ```
+///     type DbHandle = u32;
+///     db_init(db_name) -> DbHandle
+/// ```
+pub fn db_init(db_name: &str) -> GenericResult<DbHandle> {
+    // FIXME: how do I return the u32 db handle from db_init?
+    // I also want the status (whether an error occurred or success).
+    #[cfg(target_arch = "wasm32")]
+    unsafe {
+        return match db_init_(message.as_ptr(), message.len() as u32) {
+            0 => Ok(110),
+            -1 => Err(ContractError::CallerAccessDenied),
+            -2 => Err(ContractError::DbInitFailed)
+        }
+    }
+
+    #[cfg(not(target_arch = "wasm32"))]
+    todo!("{}", db_name);
+}
+
+/// Everyone can call this. Will read a key from the key-value store.
+///
+/// ```
+///     value = db_get(db_handle, key);
+/// ```
+pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Vec<u8>> {
+    Ok(Vec::new())
+}
+
+/// Only update() can call this. Starts an atomic transaction.
+///
+/// ```
+///     tx_handle = db_begin_tx();
+/// ```
+pub fn db_begin_tx() -> GenericResult<TxHandle> {
+    Ok(4)
+}
+
+/// Only update() can call this. Set a value within the transaction.
+///
+/// ```
+///     db_set(tx_handle, key, value);
+/// ```
+pub fn db_set(tx_handle: TxHandle, key: &[u8], value: Vec<u8>) -> GenericResult<()> {
+    // Check entry for tx_handle is not None
+    Ok(())
+}
+
+/// Only update() can call this. This writes the atomic tx to the database.
+///
+/// ```
+///     db_end_tx(db_handle, tx_handle);
+/// ```
+pub fn db_end_tx(db_handle: DbHandle, tx_handle: TxHandle) -> GenericResult<()> {
+    // Don't forget to set the entry for the tx in the table to empty.
+    Ok(())
+}
+
+#[cfg(target_arch = "wasm32")]
+extern "C" {
+    fn get_update_() -> i32;
+    fn set_update_(ptr: *const u8, len: u32) -> i32;
+    fn nullifier_exists_(ptr: *const u8, len: u32) -> i32;
+    fn is_valid_merkle_(ptr: *const u8, len: u32) -> i32;
+
+    fn db_init_(ptr: *const u8, len: usize) -> i32;
+    fn db_get_() -> i32;
+    fn db_begin_tx_() -> i32;
+    fn db_set_() -> i32;
+    fn db_end_tx_() -> i32;
+}

+ 13 - 0
src/sdk/src/error.rs

@@ -18,6 +18,7 @@
 
 use std::result::Result as ResultGeneric;
 
+pub type GenericResult<T> = ResultGeneric<T, ContractError>;
 pub type ContractResult = ResultGeneric<(), ContractError>;
 
 /// Error codes available in the contract.
@@ -46,6 +47,12 @@ pub enum ContractError {
 
     #[error("Update already set")]
     UpdateAlreadySet,
+
+    #[error("Db init failed")]
+    DbInitFailed,
+
+    #[error("Caller access was denied")]
+    CallerAccessDenied,
 }
 
 /// Builtin return values occupy the upper 32 bits
@@ -63,6 +70,8 @@ pub const IO_ERROR: u64 = to_builtin!(4);
 pub const NULLIFIER_EXIST_CHECK: u64 = to_builtin!(5);
 pub const VALID_MERKLE_CHECK: u64 = to_builtin!(6);
 pub const UPDATE_ALREADY_SET: u64 = to_builtin!(7);
+pub const DB_INIT_FAILED: u64 = to_builtin!(8);
+pub const CALLER_ACCESS_DENIED: u64 = to_builtin!(9);
 
 impl From<ContractError> for u64 {
     fn from(err: ContractError) -> Self {
@@ -73,6 +82,8 @@ impl From<ContractError> for u64 {
             ContractError::NullifierExistCheck => NULLIFIER_EXIST_CHECK,
             ContractError::ValidMerkleCheck => VALID_MERKLE_CHECK,
             ContractError::UpdateAlreadySet => UPDATE_ALREADY_SET,
+            ContractError::DbInitFailed => DB_INIT_FAILED,
+            ContractError::CallerAccessDenied => CALLER_ACCESS_DENIED,
             ContractError::Custom(error) => {
                 if error == 0 {
                     CUSTOM_ZERO
@@ -94,6 +105,8 @@ impl From<u64> for ContractError {
             NULLIFIER_EXIST_CHECK => Self::NullifierExistCheck,
             VALID_MERKLE_CHECK => Self::ValidMerkleCheck,
             UPDATE_ALREADY_SET => Self::UpdateAlreadySet,
+            DB_INIT_FAILED => Self::DbInitFailed,
+            CALLER_ACCESS_DENIED => Self::CallerAccessDenied,
             _ => Self::Custom(error as u32),
         }
     }

+ 3 - 0
src/sdk/src/lib.rs

@@ -19,6 +19,9 @@
 pub use incrementalmerkletree;
 pub use pasta_curves as pasta;
 
+/// Database functions
+pub mod db;
+
 /// Entrypoint used for the wasm binaries
 pub mod entrypoint;