darkfi_sdk/wasm/
db.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use darkfi_serial::Encodable;
20
21use crate::{
22    crypto::ContractId,
23    error::{ContractError, GenericResult},
24    wasm,
25};
26
27pub type DbHandle = u32;
28
29/// Create a new database instance for the given contract.
30/// This should be called in the `init_contract()` section to create any databases
31/// that the contract might need or use.
32///
33/// Returns a `DbHandle` which provides methods for reading and writing.
34pub fn db_init(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
35    unsafe {
36        let mut len = 0;
37        let mut buf = vec![];
38        len += contract_id.encode(&mut buf)?;
39        len += db_name.to_string().encode(&mut buf)?;
40
41        let ret = db_init_(buf.as_ptr(), len as u32);
42
43        if ret < 0 {
44            return Err(ContractError::from(ret))
45        }
46
47        Ok(ret as u32)
48    }
49}
50
51/// Everyone can call this. Assumes that the database already went through `db_init()`.
52pub fn db_lookup(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
53    unsafe {
54        let mut len = 0;
55        let mut buf = vec![];
56        len += contract_id.encode(&mut buf)?;
57        len += db_name.to_string().encode(&mut buf)?;
58
59        let ret = db_lookup_(buf.as_ptr(), len as u32);
60
61        if ret < 0 {
62            return Err(ContractError::from(ret))
63        }
64
65        Ok(ret as u32)
66    }
67}
68
69/// Everyone can call this. Will read a key from the key-value store.
70///
71/// ```
72/// value = db_get(db_handle, key);
73/// ```
74pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Option<Vec<u8>>> {
75    let mut len = 0;
76    let mut buf = vec![];
77    len += db_handle.encode(&mut buf)?;
78    len += key.to_vec().encode(&mut buf)?;
79
80    let ret = unsafe { db_get_(buf.as_ptr(), len as u32) };
81    wasm::util::parse_ret(ret)
82}
83
84/// Everyone can call this. Checks if a key is contained in the key-value store.
85///
86/// ```
87/// if db_contains_key(db_handle, key) {
88///     println!("true");
89/// }
90/// ```
91pub fn db_contains_key(db_handle: DbHandle, key: &[u8]) -> GenericResult<bool> {
92    let mut len = 0;
93    let mut buf = vec![];
94    len += db_handle.encode(&mut buf)?;
95    len += key.to_vec().encode(&mut buf)?;
96
97    let ret = unsafe { db_contains_key_(buf.as_ptr(), len as u32) };
98
99    if ret < 0 {
100        return Err(ContractError::from(ret))
101    }
102
103    match ret {
104        0 => Ok(false),
105        1 => Ok(true),
106        _ => unreachable!(),
107    }
108}
109
110/// Only update() can call this. Set a value within the transaction.
111///
112/// ```
113/// db_set(tx_handle, key, value);
114/// ```
115pub fn db_set(db_handle: DbHandle, key: &[u8], value: &[u8]) -> GenericResult<()> {
116    // Check entry for tx_handle is not None
117    unsafe {
118        let mut len = 0;
119        let mut buf = vec![];
120        len += db_handle.encode(&mut buf)?;
121        len += key.to_vec().encode(&mut buf)?;
122        len += value.to_vec().encode(&mut buf)?;
123
124        let ret = db_set_(buf.as_ptr(), len as u32);
125
126        if ret != wasm::entrypoint::SUCCESS {
127            return Err(ContractError::from(ret))
128        }
129
130        Ok(())
131    }
132}
133
134/// Only update() can call this. Removes a key from the db.
135///
136/// ```
137///     db_del(tx_handle, key);
138/// ```
139pub fn db_del(db_handle: DbHandle, key: &[u8]) -> GenericResult<()> {
140    // Check entry for tx_handle is not None
141    unsafe {
142        let mut len = 0;
143        let mut buf = vec![];
144        len += db_handle.encode(&mut buf)?;
145        len += key.to_vec().encode(&mut buf)?;
146
147        let ret = db_del_(buf.as_ptr(), len as u32);
148
149        if ret != wasm::entrypoint::SUCCESS {
150            return Err(ContractError::from(ret))
151        }
152
153        Ok(())
154    }
155}
156
157/// Only deploy() can call this.
158pub fn zkas_db_set(bincode: &[u8]) -> GenericResult<()> {
159    unsafe {
160        let mut len = 0;
161        let mut buf = vec![];
162        len += bincode.to_vec().encode(&mut buf)?;
163
164        let ret = zkas_db_set_(buf.as_ptr(), len as u32);
165
166        if ret != wasm::entrypoint::SUCCESS {
167            return Err(ContractError::from(ret))
168        }
169
170        Ok(())
171    }
172}
173
174extern "C" {
175    fn db_init_(ptr: *const u8, len: u32) -> i64;
176    fn db_lookup_(ptr: *const u8, len: u32) -> i64;
177    fn db_get_(ptr: *const u8, len: u32) -> i64;
178    fn db_contains_key_(ptr: *const u8, len: u32) -> i64;
179    fn db_set_(ptr: *const u8, len: u32) -> i64;
180    fn db_del_(ptr: *const u8, len: u32) -> i64;
181
182    fn zkas_db_set_(ptr: *const u8, len: u32) -> i64;
183}