darkfi_sdk/wasm/
db.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 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 on-chain database instance for the given contract.
30/// A contract is only able to create a db for itself.
31///
32/// Returns a `DbHandle` which provides methods for reading and writing.
33///
34/// ## Permissions
35/// * `ContractSection::Deploy`
36pub fn db_init(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
37    let mut len = 0;
38    let mut buf = vec![];
39    len += contract_id.encode(&mut buf)?;
40    len += db_name.to_string().encode(&mut buf)?;
41
42    let ret = unsafe { db_init_(buf.as_ptr(), len as u32) };
43
44    if ret < 0 {
45        return Err(ContractError::from(ret))
46    }
47
48    Ok(ret as u32)
49}
50
51/// Open an existing on-chain database instance for the given contract.
52/// A contract is able to read any on-chain database.
53///
54/// Returns a `DbHandle` which is used with methods for reading and writing.
55///
56/// ## Permissions
57/// * `ContractSection::Deploy`
58/// * `ContractSection::Metadata`
59/// * `ContractSection::Exec`
60/// * `ContractSection::Update`
61pub fn db_lookup(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
62    db_lookup_internal(contract_id, db_name, false)
63}
64
65/// Open a tx-local database instance for the given contract.
66/// A contract is able to read any tx-local database.
67///
68/// If the calling contract is opening its own db, the db will be created
69/// and initialized in-memory.
70///
71/// Returns a `DbHandle` which is used with methods for reading and writing.
72///
73/// ## Permissions
74/// * `ContractSection::Deploy`
75/// * `ContractSection::Metadata`
76/// * `ContractSection::Exec`
77/// * `ContractSection::Update`
78pub fn db_lookup_local(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
79    db_lookup_internal(contract_id, db_name, true)
80}
81
82/// Internal function for `db_lookup` which branches to either on-chain or
83/// transaction-local.
84fn db_lookup_internal(
85    contract_id: ContractId,
86    db_name: &str,
87    local: bool,
88) -> GenericResult<DbHandle> {
89    let mut len = 0;
90    let mut buf = vec![];
91    len += contract_id.encode(&mut buf)?;
92    len += db_name.to_string().encode(&mut buf)?;
93
94    let ret = unsafe {
95        if local {
96            db_lookup_local_(buf.as_ptr(), len as u32)
97        } else {
98            db_lookup_(buf.as_ptr(), len as u32)
99        }
100    };
101
102    if ret < 0 {
103        return Err(ContractError::from(ret))
104    }
105
106    Ok(ret as u32)
107}
108
109/// Read a key from the on-chain key-value store given a `DbHandle` and `key`.
110///
111/// Returns the `Vec<u8>` value if the key exists, otherwise `None`.
112///
113/// ## Permissions
114/// * `ContractSection::Deploy`
115/// * `ContractSection::Metadata`
116/// * `ContractSection::Exec`
117pub fn db_get(db_handle: DbHandle, key: &[u8]) -> GenericResult<Option<Vec<u8>>> {
118    db_get_internal(db_handle, key, false)
119}
120
121/// Read a key from the tx-local key-value store given a `DbHandle` and `key`.
122///
123/// Returns the `Vec<u8>` value if the key exists, otherwise `None`.
124///
125/// ## Permissions
126/// * `ContractSection::Deploy`
127/// * `ContractSection::Metadata`
128/// * `ContractSection::Exec`
129pub fn db_get_local(db_handle: DbHandle, key: &[u8]) -> GenericResult<Option<Vec<u8>>> {
130    db_get_internal(db_handle, key, true)
131}
132
133/// Internal function for `db_get` which branches to either on-chain or
134/// transaction-local.
135fn db_get_internal(db_handle: DbHandle, key: &[u8], local: bool) -> GenericResult<Option<Vec<u8>>> {
136    let mut len = 0;
137    let mut buf = vec![];
138    len += db_handle.encode(&mut buf)?;
139    len += key.encode(&mut buf)?;
140
141    let ret = unsafe {
142        if local {
143            db_get_local_(buf.as_ptr(), len as u32)
144        } else {
145            db_get_(buf.as_ptr(), len as u32)
146        }
147    };
148
149    wasm::util::parse_ret(ret)
150}
151
152/// Check if a key is contained in the on-chain key-value store given a
153/// `DbHandle` and `key`.
154///
155/// Returns a boolean value.
156///
157/// ## Permissions
158/// * `ContractSection::Deploy`
159/// * `ContractSection::Metadata`
160/// * `ContractSection::Exec`
161pub fn db_contains_key(db_handle: DbHandle, key: &[u8]) -> GenericResult<bool> {
162    db_contains_key_internal(db_handle, key, false)
163}
164
165/// Check if a key is contained in the tx-local key-value store given a
166/// `DbHandle` and `key`.
167///
168/// Returns a boolean value.
169///
170/// ## Permissions
171/// * `ContractSection::Deploy`
172/// * `ContractSection::Metadata`
173/// * `ContractSection::Exec`
174pub fn db_contains_key_local(db_handle: DbHandle, key: &[u8]) -> GenericResult<bool> {
175    db_contains_key_internal(db_handle, key, true)
176}
177
178/// Internal function for `db_contains_key` which branches to either on-chain
179/// or transaction-local.
180fn db_contains_key_internal(db_handle: DbHandle, key: &[u8], local: bool) -> GenericResult<bool> {
181    let mut len = 0;
182    let mut buf = vec![];
183    len += db_handle.encode(&mut buf)?;
184    len += key.encode(&mut buf)?;
185
186    let ret = unsafe {
187        if local {
188            db_contains_key_local_(buf.as_ptr(), len as u32)
189        } else {
190            db_contains_key_(buf.as_ptr(), len as u32)
191        }
192    };
193
194    if ret < 0 {
195        return Err(ContractError::from(ret))
196    }
197
198    match ret {
199        0 => Ok(false),
200        1 => Ok(true),
201        _ => unreachable!(),
202    }
203}
204
205/// Set a key and value in the on-chain database for the given `DbHandle`.
206///
207/// Returns `Ok` on success.
208///
209/// ## Permissions
210/// * `ContractSection::Deploy`
211/// * `ContractSection::Update`
212pub fn db_set(db_handle: DbHandle, key: &[u8], value: &[u8]) -> GenericResult<()> {
213    db_set_internal(db_handle, key, value, false)
214}
215
216/// Set a key and value in the tx-local database for the given `DbHandle`.
217///
218/// Returns `Ok` on success.
219///
220/// ## Permissions
221/// * `ContractSection::Deploy`
222/// * `ContractSection::Update`
223pub fn db_set_local(db_handle: DbHandle, key: &[u8], value: &[u8]) -> GenericResult<()> {
224    db_set_internal(db_handle, key, value, true)
225}
226
227/// Internal function for `db_set` which branches to either on-chain or
228/// transaction-local.
229fn db_set_internal(
230    db_handle: DbHandle,
231    key: &[u8],
232    value: &[u8],
233    local: bool,
234) -> GenericResult<()> {
235    let mut len = 0;
236    let mut buf = vec![];
237    len += db_handle.encode(&mut buf)?;
238    len += key.encode(&mut buf)?;
239    len += value.encode(&mut buf)?;
240
241    let ret = unsafe {
242        if local {
243            db_set_local_(buf.as_ptr(), len as u32)
244        } else {
245            db_set_(buf.as_ptr(), len as u32)
246        }
247    };
248
249    if ret != wasm::entrypoint::SUCCESS {
250        return Err(ContractError::from(ret))
251    }
252
253    Ok(())
254}
255
256/// Remove a key from the on-chain database given a `DbHandle` and `key`.
257///
258/// Returns `Ok` on success.
259///
260/// ## Permissions
261/// * `ContractSection::Deploy`
262/// * `ContractSection::Update`
263pub fn db_del(db_handle: DbHandle, key: &[u8]) -> GenericResult<()> {
264    db_del_internal(db_handle, key, false)
265}
266
267/// Remove a key from the tx-local database given a `DbHandle` and `key`.
268///
269/// Returns `Ok` on success.
270///
271/// ## Permissions
272/// * `ContractSection::Deploy`
273/// * `ContractSection::Update`
274pub fn db_del_local(db_handle: DbHandle, key: &[u8]) -> GenericResult<()> {
275    db_del_internal(db_handle, key, true)
276}
277
278/// Internal function for `db_del` which branches to either on-chain or
279/// transaction-local.
280fn db_del_internal(db_handle: DbHandle, key: &[u8], local: bool) -> GenericResult<()> {
281    let mut len = 0;
282    let mut buf = vec![];
283    len += db_handle.encode(&mut buf)?;
284    len += key.encode(&mut buf)?;
285
286    let ret = unsafe {
287        if local {
288            db_del_local_(buf.as_ptr(), len as u32)
289        } else {
290            db_del_(buf.as_ptr(), len as u32)
291        }
292    };
293
294    if ret != wasm::entrypoint::SUCCESS {
295        return Err(ContractError::from(ret))
296    }
297
298    Ok(())
299}
300
301/// Given a zkas circuit, create a VerifyingKey and insert them both
302/// into the on-chain db.
303///
304/// Returns `Ok` on success, otherwise returns an error code.
305///
306/// ## Permissions
307/// * `ContractSection::Deploy`
308pub fn zkas_db_set(bincode: &[u8]) -> GenericResult<()> {
309    let mut len = 0;
310    let mut buf = vec![];
311    len += bincode.encode(&mut buf)?;
312
313    let ret = unsafe { zkas_db_set_(buf.as_ptr(), len as u32) };
314
315    if ret != wasm::entrypoint::SUCCESS {
316        return Err(ContractError::from(ret))
317    }
318
319    Ok(())
320}
321
322extern "C" {
323    fn db_init_(ptr: *const u8, len: u32) -> i64;
324
325    fn db_lookup_(ptr: *const u8, len: u32) -> i64;
326    fn db_lookup_local_(ptr: *const u8, len: u32) -> i64;
327
328    fn db_get_(ptr: *const u8, len: u32) -> i64;
329    fn db_get_local_(ptr: *const u8, len: u32) -> i64;
330
331    fn db_contains_key_(ptr: *const u8, len: u32) -> i64;
332    fn db_contains_key_local_(ptr: *const u8, len: u32) -> i64;
333
334    fn db_set_(ptr: *const u8, len: u32) -> i64;
335    fn db_set_local_(ptr: *const u8, len: u32) -> i64;
336
337    fn db_del_(ptr: *const u8, len: u32) -> i64;
338    fn db_del_local_(ptr: *const u8, len: u32) -> i64;
339
340    fn zkas_db_set_(ptr: *const u8, len: u32) -> i64;
341}