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

implement dao::exec() update, and add missing db_del() to runtime

x 3 лет назад
Родитель
Сommit
4c056ceb86
6 измененных файлов с 131 добавлено и 9 удалено
  1. 5 2
      src/contract/dao/src/entrypoint.rs
  2. 1 1
      src/lib.rs
  3. 89 6
      src/runtime/import/db.rs
  4. 6 0
      src/runtime/vm_runtime.rs
  5. 24 0
      src/sdk/src/db.rs
  6. 6 0
      src/sdk/src/error.rs

+ 5 - 2
src/contract/dao/src/entrypoint.rs

@@ -23,7 +23,9 @@ use darkfi_sdk::{
         contract_id::{DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
         ContractId, MerkleNode, MerkleTree, PublicKey,
     },
-    db::{db_contains_key, db_get, db_init, db_lookup, db_set, SMART_CONTRACT_ZKAS_DB_NAME},
+    db::{
+        db_contains_key, db_del, db_get, db_init, db_lookup, db_set, SMART_CONTRACT_ZKAS_DB_NAME,
+    },
     error::{ContractError, ContractResult},
     merkle::merkle_add,
     msg,
@@ -394,8 +396,9 @@ fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
         DaoFunction::Exec => {
             let update: DaoExecUpdate = deserialize(&ix[1..])?;
 
-            // TODO: Implement db_del
             // Remove proposal from db
+            let proposal_vote_db = db_lookup(cid, DAO_PROPOSAL_VOTES_TREE)?;
+            db_del(proposal_vote_db, &serialize(&update.proposal))?;
 
             Ok(())
         }

+ 1 - 1
src/lib.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-//#![feature(let_else)]
+#![feature(cursor_remaining)]
 
 pub mod error;
 pub use error::{ClientFailed, ClientResult, Error, Result, VerifyFailed, VerifyResult};

+ 89 - 6
src/runtime/import/db.rs

@@ -21,7 +21,7 @@ use std::io::Cursor;
 use darkfi_sdk::{
     crypto::ContractId,
     db::{
-        CALLER_ACCESS_DENIED, DB_CONTAINS_KEY_FAILED, DB_GET_FAILED, DB_INIT_FAILED,
+        CALLER_ACCESS_DENIED, DB_CONTAINS_KEY_FAILED, DB_DEL_FAILED, DB_GET_FAILED, DB_INIT_FAILED,
         DB_LOOKUP_FAILED, DB_SET_FAILED, DB_SUCCESS,
     },
 };
@@ -106,7 +106,10 @@ pub(crate) fn db_init(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i
                 }
             };
 
-            // TODO: Ensure we've read the entire buffer above.
+            if !buf_reader.is_empty() {
+                error!(target: "runtime::db::db_init()", "Trailing bytes in argument stream");
+                return DB_DEL_FAILED
+            }
 
             if &cid != contract_id {
                 error!(target: "runtime::db::db_init()", "Unauthorized ContractId for db_init");
@@ -181,7 +184,10 @@ pub(crate) fn db_lookup(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) ->
                 }
             };
 
-            // TODO: Ensure we've read the entire buffer above.
+            if !buf_reader.is_empty() {
+                error!(target: "runtime::db::db_lookup()", "Trailing bytes in argument stream");
+                return DB_LOOKUP_FAILED
+            }
 
             let tree_handle = match contracts.lookup(db, &cid, &db_name) {
                 Ok(v) => v,
@@ -256,7 +262,10 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
                 }
             };
 
-            // TODO: Ensure we've read the entire buffer above.
+            if !buf_reader.is_empty() {
+                error!(target: "runtime::db::db_set()", "Trailing bytes in argument stream");
+                return DB_DEL_FAILED
+            }
 
             let db_handles = env.db_handles.borrow();
             let mut db_batches = env.db_batches.borrow_mut();
@@ -283,6 +292,74 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
     }
 }
 
+/// Only update() can call this. Remove a key from the database.
+pub(crate) fn db_del(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Deploy | ContractSection::Update => {
+            let memory_view = env.memory_view(&ctx);
+
+            let Ok(mem_slice) = ptr.slice(&memory_view, len) else {
+                error!(target: "runtime::db::db_del()", "Failed to make slice from ptr");
+                return DB_DEL_FAILED
+            };
+
+            let mut buf = vec![0_u8; len as usize];
+            if let Err(e) = mem_slice.read_slice(&mut buf) {
+                error!(target: "runtime::db::db_del()", "Failed to read from memory slice: {}", e);
+                return DB_DEL_FAILED
+            };
+
+            let mut buf_reader = Cursor::new(buf);
+
+            // FIXME: There's a type DbHandle=u32, but this should maybe be renamed
+            let db_handle: u32 = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "runtime::db::db_del()", "Failed to decode DbHandle: {}", e);
+                    return DB_DEL_FAILED
+                }
+            };
+            let db_handle = db_handle as usize;
+
+            let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "runtime::db::db_del()", "Failed to decode key vec: {}", e);
+                    return DB_DEL_FAILED
+                }
+            };
+
+            if !buf_reader.is_empty() {
+                error!(target: "runtime::db::db_del()", "Trailing bytes in argument stream");
+                return DB_DEL_FAILED
+            }
+
+            let db_handles = env.db_handles.borrow();
+            let mut db_batches = env.db_batches.borrow_mut();
+
+            if db_handles.len() <= db_handle || db_batches.len() <= db_handle {
+                error!(target: "runtime::db::db_del()", "Requested DbHandle that is out of bounds");
+                return DB_DEL_FAILED
+            }
+
+            let handle_idx = db_handle;
+            let db_handle = &db_handles[handle_idx];
+            let db_batch = &mut db_batches[handle_idx];
+
+            if db_handle.contract_id != env.contract_id {
+                error!(target: "runtime::db::db_del()", "Unauthorized to write to DbHandle");
+                return CALLER_ACCESS_DENIED
+            }
+
+            db_batch.remove(key);
+
+            DB_SUCCESS
+        }
+        _ => CALLER_ACCESS_DENIED,
+    }
+}
+
 /// Everyone can call this. Will read a key from the key-value store.
 pub(crate) fn db_get(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
     let env = ctx.data();
@@ -321,7 +398,10 @@ pub(crate) fn db_get(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i6
                 }
             };
 
-            // TODO: Ensure we've read the entire buffer above.
+            if !buf_reader.is_empty() {
+                error!(target: "runtime::db::db_get()", "Trailing bytes in argument stream");
+                return DB_GET_FAILED.into()
+            }
 
             let db_handles = env.db_handles.borrow();
 
@@ -396,7 +476,10 @@ pub(crate) fn db_contains_key(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u
                 }
             };
 
-            // TODO: Ensure we've read the entire buffer above.
+            if !buf_reader.is_empty() {
+                error!(target: "runtime::db::db_contains_key()", "Trailing bytes in argument stream");
+                return DB_CONTAINS_KEY_FAILED
+            }
 
             let db_handles = env.db_handles.borrow();
 

+ 6 - 0
src/runtime/vm_runtime.rs

@@ -214,6 +214,12 @@ impl Runtime {
                     import::db::db_set,
                 ),
 
+                "db_del_" => Function::new_typed_with_env(
+                    &mut store,
+                    &ctx,
+                    import::db::db_del,
+                ),
+
                 "put_object_bytes_" => Function::new_typed_with_env(
                     &mut store,
                     &ctx,

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

@@ -36,6 +36,7 @@ pub const DB_LOOKUP_FAILED: i32 = -3;
 pub const DB_GET_FAILED: i32 = -4;
 pub const DB_CONTAINS_KEY_FAILED: i32 = -5;
 pub const DB_SET_FAILED: i32 = -6;
+pub const DB_DEL_FAILED: i32 = -7;
 
 /// Only deploy() can call this. Creates a new database instance for this contract.
 ///
@@ -162,10 +163,33 @@ pub fn db_set(db_handle: DbHandle, key: &[u8], value: &[u8]) -> GenericResult<()
     }
 }
 
+/// Only update() can call this. Removes a key from the db.
+///
+/// ```
+///     db_del(tx_handle, key);
+/// ```
+pub fn db_del(db_handle: DbHandle, key: &[u8]) -> GenericResult<()> {
+    // Check entry for tx_handle is not None
+    unsafe {
+        let mut len = 0;
+        let mut buf = vec![];
+        len += db_handle.encode(&mut buf)?;
+        len += key.to_vec().encode(&mut buf)?;
+
+        match db_del_(buf.as_ptr(), len as u32) {
+            CALLER_ACCESS_DENIED => Err(ContractError::CallerAccessDenied),
+            DB_DEL_FAILED => Err(ContractError::DbDelFailed),
+            DB_SUCCESS => Ok(()),
+            _ => unreachable!(),
+        }
+    }
+}
+
 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) -> i64;
     fn db_contains_key_(ptr: *const u8, len: u32) -> i32;
     fn db_set_(ptr: *const u8, len: u32) -> i32;
+    fn db_del_(ptr: *const u8, len: u32) -> i32;
 }

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

@@ -60,6 +60,9 @@ pub enum ContractError {
     #[error("Db set failed")]
     DbSetFailed,
 
+    #[error("Db del failed")]
+    DbDelFailed,
+
     #[error("Db lookup failed")]
     DbLookupFailed,
 
@@ -95,6 +98,7 @@ pub const DB_LOOKUP_FAILED: i64 = to_builtin!(12);
 pub const DB_GET_FAILED: i64 = to_builtin!(13);
 pub const DB_CONTAINS_KEY_FAILED: i64 = to_builtin!(14);
 pub const INVALID_FUNCTION: i64 = to_builtin!(15);
+pub const DB_DEL_FAILED: i64 = to_builtin!(16);
 
 impl From<ContractError> for i64 {
     fn from(err: ContractError) -> Self {
@@ -113,6 +117,7 @@ impl From<ContractError> for i64 {
             ContractError::DbGetFailed => DB_GET_FAILED,
             ContractError::DbContainsKeyFailed => DB_CONTAINS_KEY_FAILED,
             ContractError::InvalidFunction => INVALID_FUNCTION,
+            ContractError::DbDelFailed => DB_DEL_FAILED,
             ContractError::Custom(error) => {
                 if error == 0 {
                     CUSTOM_ZERO
@@ -142,6 +147,7 @@ impl From<i64> for ContractError {
             DB_GET_FAILED => Self::DbGetFailed,
             DB_CONTAINS_KEY_FAILED => Self::DbContainsKeyFailed,
             INVALID_FUNCTION => Self::InvalidFunction,
+            DB_DEL_FAILED => Self::DbDelFailed,
             _ => Self::Custom(error as u32),
         }
     }