parazyd 3 лет назад
Родитель
Сommit
60934a20d4

+ 10 - 8
example/smart-contract/src/lib.rs

@@ -1,6 +1,6 @@
 use darkfi_sdk::{
     crypto::ContractId,
-    db::{db_begin_tx, db_end_tx, db_get, db_init, db_lookup, db_set},
+    db::{db_get, db_init, db_lookup, db_set},
     define_contract,
     error::ContractResult,
     msg,
@@ -72,8 +72,6 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
     let wagies_handle = db_init(cid, "wagies")?;
     db_set(wagies_handle, &serialize(&"jason_gulag".to_string()), &serialize(&110))?;
 
-    //let db_handle = db_lookup("wagies")?;
-
     Ok(())
 }
 
@@ -135,7 +133,7 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
 // This is the main entrypoint function where the payload is fed.
 // Through here, you can branch out into different functions inside
 // this library.
-fn process_instruction(_cid: ContractId, ix: &[u8]) -> ContractResult {
+fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
     match Function::from(ix[0]) {
         Function::Foo => {
             let tx_data = &ix[1..];
@@ -153,10 +151,14 @@ fn process_instruction(_cid: ContractId, ix: &[u8]) -> ContractResult {
             msg!("update is set!");
 
             // Example: try to get a value from the db
-            let db_handle = db_lookup("wagies")?;
-            // FIXME: this is just empty right now
-            let age_data = db_get(db_handle, "jason_gulag".as_bytes())?;
-            msg!("wagie age data: {:?}", age_data);
+            let db_handle = db_lookup(cid, "wagies")?;
+
+            if let Some(age_data) = db_get(db_handle, &serialize(&"jason_gulag".to_string()))? {
+                let age_data: u32 = deserialize(&age_data)?;
+                msg!("wagie age data: {}", age_data);
+            } else {
+                msg!("didn't find wagie age data");
+            }
         }
         Function::Bar => {
             let tx_data = &ix[1..];

+ 4 - 1
example/smart-contract/tests/runtime.rs

@@ -52,12 +52,15 @@ fn run_contract() -> Result<()> {
     // ================================================================
     let wasm_bytes = std::fs::read("contract.wasm")?;
     let contract_id = ContractId::new(pallas::Base::from(1));
-    let mut runtime = Runtime::new(&wasm_bytes, blockchain, contract_id)?;
+    let mut runtime = Runtime::new(&wasm_bytes, blockchain.clone(), contract_id)?;
 
     // Deploy function to initialize the smart contract state.
     // Here we pass an empty payload, but it's possible to feed in arbitrary data.
     runtime.deploy(&[])?;
 
+    // This is another call so we instantiate a new runtime.
+    let mut runtime = Runtime::new(&wasm_bytes, blockchain, contract_id)?;
+
     // =============================================
     // Build some kind of payload to show an example
     // =============================================

+ 1 - 0
src/blockchain/blockstore.rs

@@ -196,6 +196,7 @@ impl BlockStore {
 /// The `BlockOrderStore` is a `sled` tree storing the order of the
 /// blockchain's slots, where the key is the slot uid, and the value is
 /// the block's headers' hash. [`BlockStore`] can be queried with this hash.
+#[derive(Clone)]
 pub struct BlockOrderStore(sled::Tree);
 
 impl BlockOrderStore {

+ 35 - 8
src/blockchain/contractstore.rs

@@ -29,12 +29,29 @@ pub struct ContractStore(sled::Tree);
 
 const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
 
+// =================
+// TODO: Drop tree
+// =================
+
 impl ContractStore {
     pub fn new(db: &sled::Db) -> Result<Self> {
         let tree = db.open_tree(SLED_CONTRACTS_TREE)?;
         Ok(Self(tree))
     }
 
+    /// Database layout:
+    /// ```plaintext
+    /// Tree: _contracts
+    /// key:   ContractId
+    /// value: blake3(ContractId || tree_name)
+    /// ```
+    ///
+    /// `value` when init-ed represents a Contract's state tree:
+    /// ```plaintext
+    /// Tree: blake3(ContractId || tree_name)
+    /// key: &[u8]
+    /// value: &[u8]
+    /// ```
     pub fn init(
         &self,
         db: &sled::Db,
@@ -43,21 +60,30 @@ impl ContractStore {
     ) -> Result<sled::Tree> {
         let contract_id_bytes = serialize(contract_id);
 
-        // If the db was never initialized, it should not be in here.
-        if self.0.contains_key(&contract_id_bytes)? {
-            return Err(ContractAlreadyInitialized)
-        }
+        let mut state_pointers: Vec<[u8; 32]> = if self.0.contains_key(&contract_id_bytes)? {
+            let bytes = self.0.get(&contract_id_bytes)?.unwrap();
+            deserialize(&bytes)?
+        } else {
+            vec![]
+        };
 
         let mut hasher = blake3::Hasher::new();
         hasher.update(&contract_id_bytes);
         hasher.update(&tree_name.as_bytes());
         let ptr = hasher.finalize();
+        let ptr = ptr.as_bytes();
+
+        // If the db was never initialized, it should not be in here.
+        if state_pointers.contains(ptr) {
+            return Err(ContractAlreadyInitialized)
+        }
 
         // Now we add it so it's marked as initialized
-        self.0.insert(&contract_id_bytes, ptr.as_bytes())?;
+        state_pointers.push(*ptr);
+        self.0.insert(&contract_id_bytes, serialize(&state_pointers))?;
 
         // We open the tree and return its handle
-        let tree = db.open_tree(ptr.as_bytes())?;
+        let tree = db.open_tree(ptr)?;
         Ok(tree)
     }
 
@@ -82,15 +108,16 @@ impl ContractStore {
         hasher.update(&contract_id_bytes);
         hasher.update(&tree_name.as_bytes());
         let ptr = hasher.finalize();
+        let ptr = ptr.as_bytes();
 
         // We assume the tree has been created already, so it should be listed in this array.
         // If not, that's an error.
-        if !state_pointers.contains(ptr.as_bytes()) {
+        if !state_pointers.contains(ptr) {
             return Err(ContractStateNotFound)
         }
 
         // We open the tree and return its handle
-        let tree = db.open_tree(ptr.as_bytes())?;
+        let tree = db.open_tree(ptr)?;
         Ok(tree)
     }
 }

+ 1 - 0
src/blockchain/mod.rs

@@ -41,6 +41,7 @@ pub mod contractstore;
 pub use contractstore::ContractStore;
 
 /// Structure holding all sled trees that define the concept of Blockchain.
+#[derive(Clone)]
 pub struct Blockchain {
     /// Main pointer to the sled db connection
     pub sled_db: sled::Db,

+ 156 - 35
src/runtime/import/db.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::crypto::ContractId;
-use darkfi_serial::{deserialize, Decodable};
+use darkfi_serial::Decodable;
 use log::error;
 use std::io::Cursor;
 use wasmer::{FunctionEnvMut, WasmPtr};
@@ -38,9 +38,22 @@ impl DbHandle {
         Self { contract_id, tree }
     }
 
+    pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
+        if let Some(v) = self.tree.get(key)? {
+            return Ok(Some(v.to_vec()))
+        };
+
+        Ok(None)
+    }
+
     pub fn apply_batch(&self, batch: sled::Batch) -> Result<()> {
         Ok(self.tree.apply_batch(batch)?)
     }
+
+    pub fn flush(&self) -> Result<()> {
+        let _ = self.tree.flush()?;
+        Ok(())
+    }
 }
 
 /// Only deploy() can call this. Creates a new database instance for this contract.
@@ -89,22 +102,23 @@ pub(crate) fn db_init(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i
 
             // TODO: Ensure we've read the entire buffer above.
 
-            if &cid != contract_id {
-                error!(target: "wasm_runtime::db_init", "Unauthorized ContractId for db_init");
-                return -1
-            }
-
-            let tree_handle = match contracts.init(db, contract_id, &db_name) {
+            let tree_handle = match contracts.init(db, &cid, &db_name) {
                 Ok(v) => v,
                 Err(e) => {
-                    error!(target: "wasm_runtime::db_init", "Failed to init db: {}", e);
+                    error!(target: "wasm_runtime:db_lookup", "Failed to init db: {}", e);
                     return -2
                 }
             };
 
+            // TODO: Make sure we don't duplicate the DbHandle in the vec.
+            //       It should behave like an ordered set.
+            // In `lookup()` we also create a `sled::Batch`. This is done for
+            // some simplicity reasons, and also for possible future changes.
+            // However, we make sure that unauthorized writes are not available
+            // from other functions that interface with the databases.
             let mut db_handles = env.db_handles.borrow_mut();
             let mut db_batches = env.db_batches.borrow_mut();
-            db_handles.push(DbHandle::new(*contract_id, tree_handle));
+            db_handles.push(DbHandle::new(cid, tree_handle));
             db_batches.push(sled::Batch::default());
             return (db_handles.len() - 1) as i32
         }
@@ -125,34 +139,65 @@ pub(crate) fn db_lookup(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) ->
     let env = ctx.data();
     match env.contract_section {
         ContractSection::Deploy | ContractSection::Exec | ContractSection::Update => {
-            let env = ctx.data();
             let memory_view = env.memory_view(&ctx);
+            let db = &env.blockchain.sled_db;
+            let contracts = &env.blockchain.contracts;
+
+            let Ok(mem_slice) = ptr.slice(&memory_view, len) else {
+                error!(target: "wasm_runtime::db_lookup", "Failed to make slice from ptr");
+                return -2
+            };
 
-            match ptr.read_utf8_string(&memory_view, len) {
-                Ok(db_name) => {
-                    // db_name = blake3_hash(contract_id, db_name)
-                    return 110
+            let mut buf = vec![0_u8; len as usize];
+            if let Err(e) = mem_slice.read_slice(&mut buf) {
+                error!(target: "wasm_runtime::db_lookup", "Failed to read from memory slice: {}", e);
+                return -2
+            };
+
+            let mut buf_reader = Cursor::new(buf);
+
+            let cid: ContractId = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::db_lookup", "Failed to decode ContractId: {}", e);
+                    return -2
                 }
-                Err(_) => {
-                    error!(target: "wasm_runtime::drk_log", "Failed to read UTF-8 string from VM memory");
+            };
+
+            let db_name: String = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::db_lookup", "Failed to decode db_name: {}", e);
                     return -2
                 }
-            }
-        }
-        _ => -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(ctx: FunctionEnvMut<Env>) -> i32 {
-    let env = ctx.data();
-    match env.contract_section {
-        ContractSection::Exec => 0,
-        _ => -1,
+            // TODO: Ensure we've read the entire buffer above.
+
+            let tree_handle = match contracts.lookup(db, &cid, &db_name) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime:db_lookup", "Failed to lookup db: {}", e);
+                    return -2
+                }
+            };
+
+            // TODO: Make sure we don't duplicate the DbHandle in the vec.
+            //       It should behave like an ordered set.
+            // In `lookup()` we also create a `sled::Batch`. This is done for
+            // some simplicity reasons, and also for possible future changes.
+            // However, we make sure that unauthorized writes are not available
+            // from other functions that interface with the databases.
+            let mut db_handles = env.db_handles.borrow_mut();
+            let mut db_batches = env.db_batches.borrow_mut();
+            db_handles.push(DbHandle::new(cid, tree_handle));
+            db_batches.push(sled::Batch::default());
+            return (db_handles.len() - 1) as i32
+        }
+        _ => {
+            error!(target: "wasm_runtime::db_lookup", "db_lookup called in unauthorized section");
+            return -1
+        }
     }
 }
 
@@ -174,7 +219,7 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
 
             let mut buf = vec![0_u8; len as usize];
             if let Err(e) = mem_slice.read_slice(&mut buf) {
-                error!(target: "wasm_runtime:db_set", "Failed to read from memory slice");
+                error!(target: "wasm_runtime:db_set", "Failed to read from memory slice: {}", e);
                 return -2
             };
 
@@ -184,7 +229,7 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
             let db_handle: u32 = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Err(e) => {
-                    error!(target: "wasm_runtime::db_set", "Failed to decode DbHandle");
+                    error!(target: "wasm_runtime::db_set", "Failed to decode DbHandle: {}", e);
                     return -2
                 }
             };
@@ -193,7 +238,7 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
             let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Err(e) => {
-                    error!(target: "wasm_runtime::db_set", "Failed to decode key vec");
+                    error!(target: "wasm_runtime::db_set", "Failed to decode key vec: {}", e);
                     return -2
                 }
             };
@@ -201,7 +246,7 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
             let value: Vec<u8> = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Err(e) => {
-                    error!(target: "wasm_runtime::db_set", "Failed to decode value vec");
+                    error!(target: "wasm_runtime::db_set", "Failed to decode value vec: {}", e);
                     return -2
                 }
             };
@@ -232,3 +277,79 @@ pub(crate) fn db_set(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i3
         _ => -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(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+    let env = ctx.data();
+    match env.contract_section {
+        ContractSection::Deploy | ContractSection::Exec | ContractSection::Update => {
+            let memory_view = env.memory_view(&ctx);
+            let db = &env.blockchain.sled_db;
+            let contracts = &env.blockchain.contracts;
+
+            let Ok(mem_slice) = ptr.slice(&memory_view, len) else {
+                error!(target: "wasm_runtime::db_get", "Failed to make slice from ptr");
+                return -2
+            };
+
+            let mut buf = vec![0_u8; len as usize];
+            if let Err(e) = mem_slice.read_slice(&mut buf) {
+                error!(target: "wasm_runtime::db_get", "Failed to read from memory slice: {}", e);
+                return -2
+            };
+
+            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: "wasm_runtime::db_get", "Failed to decode DbHandle: {}", e);
+                    return -2
+                }
+            };
+            let db_handle = db_handle as usize;
+
+            let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::db_get", "Failed to decode key from vec: {}", e);
+                    return -2
+                }
+            };
+
+            // TODO: Ensure we've read the entire buffer above.
+
+            let db_handles = env.db_handles.borrow();
+            let db_batches = env.db_batches.borrow();
+
+            if db_handles.len() <= db_handle || db_batches.len() <= db_handle {
+                error!(target: "wasm_runtime::db_get", "Requested DbHandle that is out of bounds");
+                return -2
+            }
+
+            let handle_idx = db_handle;
+            let db_handle = &db_handles[handle_idx];
+
+            let ret = match db_handle.get(&key) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::db_get", "Internal error getting from tree");
+                    return -2
+                }
+            };
+
+            if ret.is_none() {
+                log::debug!("returned empty vec");
+                return -3
+            }
+
+            0
+        }
+        _ => -1,
+    }
+}

+ 20 - 3
src/runtime/vm_runtime.rs

@@ -279,7 +279,19 @@ impl Runtime {
     /// The permissions for this are handled by the `ContractId` in the sled db API so we
     /// assume that the contract is only able to do write operations on its own sled trees.
     pub fn deploy(&mut self, payload: &[u8]) -> Result<()> {
+        debug!("deploy: {:?}", payload);
         let _ = self.call(ContractSection::Deploy, payload)?;
+
+        // If the above didn't fail, we write the batches.
+        // TODO: Make all the writes atomic in a transaction over all trees.
+        let env_mut = self.ctx.as_mut(&mut self.store);
+        for (idx, db) in env_mut.db_handles.get_mut().iter().enumerate() {
+            let batch = env_mut.db_batches.borrow()[idx].clone();
+            db.apply_batch(batch)?;
+            db.flush()?;
+            drop(db);
+        }
+
         Ok(())
     }
 
@@ -288,6 +300,7 @@ impl Runtime {
     /// execute it if found. A payload is also passed as an instruction that can
     /// be used inside the vm by the runtime.
     pub fn exec(&mut self, payload: &[u8]) -> Result<Vec<u8>> {
+        debug!("exec: {:?}", payload);
         self.call(ContractSection::Exec, payload)
     }
 
@@ -297,6 +310,7 @@ impl Runtime {
     /// it if found. The function does not take an arbitrary payload, but just takes
     /// a state update from `env` and passes it into the wasm runtime.
     pub fn apply(&mut self, update: &[u8]) -> Result<()> {
+        debug!("apply: {:?}", update);
         let _ = self.call(ContractSection::Update, update)?;
 
         // If the above didn't fail, we write the batches.
@@ -305,6 +319,8 @@ impl Runtime {
         for (idx, db) in env_mut.db_handles.get_mut().iter().enumerate() {
             let batch = env_mut.db_batches.borrow()[idx].clone();
             db.apply_batch(batch)?;
+            db.flush()?;
+            drop(db);
         }
 
         Ok(())
@@ -335,15 +351,16 @@ impl Runtime {
     }
 
     /// Set the memory page size
-    fn set_memory_page_size(&mut self, pages: u32) -> Result<()> {
+    fn set_memory_page_size(&mut self, pages: u32) -> Result<Pages> {
         // Grab memory by value
         let memory = self.take_memory();
         // Modify the memory
-        memory.grow(&mut self.store, Pages(pages))?;
+        let ret = memory.grow(&mut self.store, Pages(pages))?;
         // Replace the memory back again
         self.ctx.as_mut(&mut self.store).memory = Some(memory);
-        Ok(())
+        Ok(ret)
     }
+
     /// Take Memory by value. Needed to modify the Memory object
     /// Will panic if memory isn't set.
     fn take_memory(&mut self) -> Memory {

+ 36 - 59
src/sdk/src/db.rs

@@ -39,23 +39,29 @@ pub fn db_init(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle
     unimplemented!()
 }
 
-pub fn db_lookup(db_name: &str) -> GenericResult<DbHandle> {
+pub fn db_lookup(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
     #[cfg(target_arch = "wasm32")]
     unsafe {
-        return match db_lookup_(db_name.as_ptr(), db_name.len() as u32) {
-            handle => {
-                if handle < 0 {
-                    unreachable!();
-                }
-                Ok(handle as u32)
+        let mut len = 0;
+        let mut buf = vec![];
+        len += contract_id.encode(&mut buf)?;
+        len += db_name.to_string().encode(&mut buf)?;
+
+        let ret = db_lookup_(buf.as_ptr(), len as u32);
+
+        if ret < 0 {
+            match ret {
+                -1 => return Err(ContractError::CallerAccessDenied),
+                -2 => return Err(ContractError::DbLookupFailed),
+                _ => unimplemented!(),
             }
-            -1 => Err(ContractError::CallerAccessDenied),
-            -2 => Err(ContractError::DbNotFound),
         }
+
+        return Ok(ret as u32)
     }
 
     #[cfg(not(target_arch = "wasm32"))]
-    todo!("{}", db_name);
+    unimplemented!()
 }
 
 /// Everyone can call this. Will read a key from the key-value store.
@@ -63,18 +69,30 @@ pub fn db_lookup(db_name: &str) -> GenericResult<DbHandle> {
 /// ```
 ///     value = db_get(db_handle, key);
 /// ```
-pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Vec<u8>> {
+pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Option<Vec<u8>>> {
     #[cfg(target_arch = "wasm32")]
     unsafe {
-        return match db_get_() {
-            0 => Ok(Vec::new()),
-            -1 => Err(ContractError::CallerAccessDenied),
-            _ => unreachable!(),
+        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);
+
+        if ret < 0 {
+            match ret {
+                -1 => return Err(ContractError::CallerAccessDenied),
+                -2 => return Err(ContractError::DbGetFailed),
+                -3 => return Ok(None),
+                _ => unimplemented!(),
+            }
         }
+
+        Ok(Some(vec![]))
     }
 
     #[cfg(not(target_arch = "wasm32"))]
-    todo!("db_get");
+    unimplemented!()
 }
 
 /// Only update() can call this. Set a value within the transaction.
@@ -101,54 +119,13 @@ pub fn db_set(db_handle: DbHandle, key: &[u8], value: &[u8]) -> GenericResult<()
     }
 
     #[cfg(not(target_arch = "wasm32"))]
-    todo!("db_set");
-}
-
-/// Only update() can call this. Starts an atomic transaction.
-///
-/// ```
-///     tx_handle = db_begin_tx();
-/// ```
-pub fn db_begin_tx() -> GenericResult<TxHandle> {
-    #[cfg(target_arch = "wasm32")]
-    unsafe {
-        return match db_begin_tx_() {
-            0 => Ok(4),
-            -1 => Err(ContractError::CallerAccessDenied),
-            _ => unreachable!(),
-        }
-    }
-
-    #[cfg(not(target_arch = "wasm32"))]
-    todo!("db_begin_tx");
-}
-
-/// 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.
-    #[cfg(target_arch = "wasm32")]
-    unsafe {
-        return match db_end_tx_() {
-            0 => Ok(()),
-            -1 => Err(ContractError::CallerAccessDenied),
-            _ => unreachable!(),
-        }
-    }
-
-    #[cfg(not(target_arch = "wasm32"))]
-    todo!("db_end_tx");
+    unimplemented!()
 }
 
 #[cfg(target_arch = "wasm32")]
 extern "C" {
     fn db_init_(ptr: *const u8, len: u32) -> i32;
     fn db_lookup_(ptr: *const u8, len: u32) -> i32;
-    fn db_get_() -> i32;
-    fn db_begin_tx_() -> i32;
+    fn db_get_(ptr: *const u8, len: u32) -> i32;
     fn db_set_(ptr: *const u8, len: u32) -> i32;
-    fn db_end_tx_() -> i32;
 }

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

@@ -59,6 +59,12 @@ pub enum ContractError {
 
     #[error("Db set failed")]
     DbSetFailed,
+
+    #[error("Db lookup failed")]
+    DbLookupFailed,
+
+    #[error("Db get failed")]
+    DbGetFailed,
 }
 
 /// Builtin return values occupy the upper 32 bits
@@ -80,6 +86,8 @@ pub const DB_INIT_FAILED: u64 = to_builtin!(8);
 pub const CALLER_ACCESS_DENIED: u64 = to_builtin!(9);
 pub const DB_NOT_FOUND: u64 = to_builtin!(10);
 pub const DB_SET_FAILED: u64 = to_builtin!(11);
+pub const DB_LOOKUP_FAILED: u64 = to_builtin!(12);
+pub const DB_GET_FAILED: u64 = to_builtin!(13);
 
 impl From<ContractError> for u64 {
     fn from(err: ContractError) -> Self {
@@ -94,6 +102,8 @@ impl From<ContractError> for u64 {
             ContractError::CallerAccessDenied => CALLER_ACCESS_DENIED,
             ContractError::DbNotFound => DB_NOT_FOUND,
             ContractError::DbSetFailed => DB_SET_FAILED,
+            ContractError::DbLookupFailed => DB_LOOKUP_FAILED,
+            ContractError::DbGetFailed => DB_GET_FAILED,
             ContractError::Custom(error) => {
                 if error == 0 {
                     CUSTOM_ZERO
@@ -119,6 +129,8 @@ impl From<u64> for ContractError {
             CALLER_ACCESS_DENIED => Self::CallerAccessDenied,
             DB_NOT_FOUND => Self::DbNotFound,
             DB_SET_FAILED => Self::DbSetFailed,
+            DB_LOOKUP_FAILED => Self::DbLookupFailed,
+            DB_GET_FAILED => Self::DbGetFailed,
             _ => Self::Custom(error as u32),
         }
     }