darkfi/runtime/import/
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 std::io::Cursor;
20
21use darkfi_sdk::{
22    crypto::contract_id::{
23        ContractId, SMART_CONTRACT_MONOTREE_DB_NAME, SMART_CONTRACT_ZKAS_DB_NAME,
24    },
25    wasm,
26};
27use darkfi_serial::{deserialize, serialize, Decodable};
28use tracing::{debug, error, info};
29use wasmer::{FunctionEnvMut, WasmPtr};
30
31use super::acl::acl_allow;
32use crate::{
33    runtime::vm_runtime::{ContractSection, Env},
34    zk::{empty_witnesses, VerifyingKey, ZkCircuit},
35    zkas::ZkBinary,
36};
37
38/// Internal wasm runtime API for sled trees
39#[derive(PartialEq)]
40pub struct DbHandle {
41    pub contract_id: ContractId,
42    pub tree: [u8; 32],
43}
44
45impl DbHandle {
46    pub fn new(contract_id: ContractId, tree: [u8; 32]) -> Self {
47        Self { contract_id, tree }
48    }
49}
50
51/// Create a new database instance for the calling contract.
52///
53/// This function expects to receive a pointer from which a `ContractId`
54/// and the `db_name` will be read.
55///
56/// This function should **only** be allowed in `ContractSection::Deploy`, as that
57/// is called when a contract is being (re)deployed and databases have to be created.
58///
59/// Permissions: deploy
60pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
61    let (env, mut store) = ctx.data_and_store_mut();
62    let cid = env.contract_id;
63
64    // Enforce function ACL
65    if let Err(e) = acl_allow(env, &[ContractSection::Deploy]) {
66        error!(
67            target: "runtime::db::db_init",
68            "[WASM] [{cid}] db_init(): Called in unauthorized section: {e}"
69        );
70        return darkfi_sdk::error::CALLER_ACCESS_DENIED
71    }
72
73    // Subtract used gas.
74    // TODO: There should probably be an additional fee to open a new sled tree.
75    env.subtract_gas(&mut store, 1);
76
77    // This takes lock of the blockchain overlay reference in the wasm env
78    let contracts = &env.blockchain.lock().unwrap().contracts;
79
80    // Create a mem slice of the wasm VM memory
81    let memory_view = env.memory_view(&store);
82    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
83        error!(
84            target: "runtime::db::db_init",
85            "[WASM] [{cid}] db_init(): Failed to make slice from ptr"
86        );
87        return darkfi_sdk::error::DB_INIT_FAILED
88    };
89
90    // Allocate a buffer and copy all the data from the pointer into the buffer
91    let mut buf = vec![0_u8; ptr_len as usize];
92    if let Err(e) = mem_slice.read_slice(&mut buf) {
93        error!(
94            target: "runtime::db::db_init",
95            "[WASM] [{cid}] db_init(): Failed to read memory slice: {e}"
96        );
97        return darkfi_sdk::error::DB_INIT_FAILED
98    };
99
100    // Once the data is copied, we'll attempt to deserialize it into the objects
101    // we're expecting.
102    let mut buf_reader = Cursor::new(buf);
103    let read_cid: ContractId = match Decodable::decode(&mut buf_reader) {
104        Ok(v) => v,
105        Err(e) => {
106            error!(
107                target: "runtime::db::db_init",
108                "[WASM] [{cid}] db_init(): Failed decoding ContractId: {e}"
109            );
110            return darkfi_sdk::error::DB_INIT_FAILED
111        }
112    };
113
114    let read_db_name: String = match Decodable::decode(&mut buf_reader) {
115        Ok(v) => v,
116        Err(e) => {
117            error!(
118                target: "runtime::db::db_init",
119                "[WASM] [{cid}] db_init(): Failed decoding db_name: {e}"
120            );
121            return darkfi_sdk::error::DB_INIT_FAILED
122        }
123    };
124
125    // Make sure we've read the entire buffer
126    if buf_reader.position() != ptr_len as u64 {
127        error!(
128            target: "runtime::db::db_init",
129            "[WASM] [{cid}] db_init(): Trailing bytes in argument stream"
130        );
131        return darkfi_sdk::error::DB_INIT_FAILED
132    }
133
134    // We cannot allow initializing the special zkas db:
135    if read_db_name == SMART_CONTRACT_ZKAS_DB_NAME {
136        error!(
137            target: "runtime::db::db_init",
138            "[WASM] [{cid}] db_init(): Attempted to init zkas db"
139        );
140        return darkfi_sdk::error::CALLER_ACCESS_DENIED
141    }
142
143    // Nor can we allow initializing the special monotree db:
144    if read_db_name == SMART_CONTRACT_MONOTREE_DB_NAME {
145        error!(
146            target: "runtime::db::db_init",
147            "[WASM] [{cid}] db_init(): Attempted to init monotree db"
148        );
149        return darkfi_sdk::error::CALLER_ACCESS_DENIED
150    }
151
152    // Nor can we allow another contract to initialize a db for someone else:
153    if cid != read_cid {
154        error!(
155            target: "runtime::db::db_init",
156            "[WASM] [{cid}] db_init(): Unauthorized ContractId"
157        );
158        return darkfi_sdk::error::CALLER_ACCESS_DENIED
159    }
160
161    // Now try to initialize the tree. If this returns an error,
162    // it usually means that this DB was already initialized.
163    // An alternative error might happen if something in sled fails,
164    // for this we should take care to stop the node or do something to
165    // be able to gracefully recover.
166    // (src/blockchain/contract_store.rs holds this init() function)
167    let tree_handle = match contracts.init(&read_cid, &read_db_name) {
168        Ok(v) => v,
169        Err(e) => {
170            error!(
171                target: "runtime::db::db_init",
172                "[WASM] [{cid}] db_init(): Failed to init db: {e}"
173            );
174            return darkfi_sdk::error::DB_INIT_FAILED
175        }
176    };
177
178    // Create the DbHandle
179    let db_handle = DbHandle::new(read_cid, tree_handle);
180    let mut db_handles = env.db_handles.borrow_mut();
181
182    // Make sure we don't duplicate the DbHandle in the vec.
183    // It's not really an issue, but it's better to be pedantic.
184    if db_handles.contains(&db_handle) {
185        error!(
186            target: "runtime::db::db_init",
187            "[WASM] [{cid}] db_init(): DbHandle initialized twice during execution"
188        );
189        return darkfi_sdk::error::DB_INIT_FAILED
190    }
191
192    // This tries to cast into u32
193    match db_handles.len().try_into() {
194        Ok(db_handle_idx) => {
195            db_handles.push(db_handle);
196            // Return the db handle index
197            db_handle_idx
198        }
199        Err(_) => {
200            error!(
201                target: "runtime::db::db_init",
202                "[WASM] [{cid}] db_init(): Too many open DbHandles"
203            );
204            darkfi_sdk::error::DB_INIT_FAILED
205        }
206    }
207}
208
209/// Lookup a database handle from its name.
210/// If it exists, push it to the Vector of db_handles.
211///
212/// Returns the index of the DbHandle in the db_handles Vector on success.
213/// Otherwise, returns an error value.
214///
215/// This function can be called from any [`ContractSection`].
216///
217/// Permissions: deploy, metadata, exec, update
218pub(crate) fn db_lookup(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
219    let (env, mut store) = ctx.data_and_store_mut();
220    let cid = env.contract_id;
221
222    // Enforce function ACL
223    if let Err(e) = acl_allow(
224        env,
225        &[
226            ContractSection::Deploy,
227            ContractSection::Metadata,
228            ContractSection::Exec,
229            ContractSection::Update,
230        ],
231    ) {
232        error!(
233            target: "runtime::db::db_lookup",
234            "[WASM] [{cid}] db_lookup() called in unauthorized section: {e}"
235        );
236        return darkfi_sdk::error::CALLER_ACCESS_DENIED
237    }
238
239    // Subtract used gas. Opening an existing db should be free (i.e. 1 gas unit).
240    env.subtract_gas(&mut store, 1);
241
242    // Read memory location that contains the ContractId and DB name
243    let memory_view = env.memory_view(&store);
244    let contracts = &env.blockchain.lock().unwrap().contracts;
245
246    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
247        error!(
248            target: "runtime::db::db_lookup",
249            "[WASM] [{cid}] db_lookup(): Failed to make slice from ptr."
250        );
251        return darkfi_sdk::error::DB_LOOKUP_FAILED
252    };
253
254    let mut buf = vec![0_u8; ptr_len as usize];
255    if let Err(e) = mem_slice.read_slice(&mut buf) {
256        error!(
257            target: "runtime::db::db_lookup",
258            "[WASM] [{cid}] db_lookup(): Failed to read from memory slice: {e}"
259        );
260        return darkfi_sdk::error::DB_LOOKUP_FAILED
261    };
262
263    // Wrap the buffer into a Cursor for stream reading
264    let mut buf_reader = Cursor::new(buf);
265
266    // Decode ContractId from memory
267    let cid: ContractId = match Decodable::decode(&mut buf_reader) {
268        Ok(v) => v,
269        Err(e) => {
270            error!(
271                target: "runtime::db::db_lookup",
272                "[WASM] [{cid}] db_lookup(): Failed to decode ContractId: {e}"
273            );
274            return darkfi_sdk::error::DB_LOOKUP_FAILED
275        }
276    };
277
278    // Decode DB name from memory
279    let db_name: String = match Decodable::decode(&mut buf_reader) {
280        Ok(v) => v,
281        Err(e) => {
282            error!(
283                target: "runtime::db::db_lookup",
284                "[WASM] [{cid}] db_lookup(): Failed to decode db_name: {e}"
285            );
286            return darkfi_sdk::error::DB_LOOKUP_FAILED
287        }
288    };
289
290    // Make sure we've read the entire buffer
291    if buf_reader.position() != ptr_len as u64 {
292        error!(
293            target: "runtime::db::db_lookup",
294            "[WASM] [{cid}] db_lookup(), Trailing bytes in argument stream"
295        );
296        return darkfi_sdk::error::DB_LOOKUP_FAILED
297    }
298
299    if db_name == SMART_CONTRACT_ZKAS_DB_NAME {
300        error!(
301            target: "runtime::db::db_lookup",
302            "[WASM] [{cid}] db_lookup(): Attempted to lookup zkas db"
303        );
304        return darkfi_sdk::error::CALLER_ACCESS_DENIED
305    }
306
307    if db_name == SMART_CONTRACT_MONOTREE_DB_NAME {
308        error!(
309            target: "runtime::db::db_lookup",
310            "[WASM] [{cid}] db_lookup(): Attempted to lookup monotree db"
311        );
312        return darkfi_sdk::error::CALLER_ACCESS_DENIED
313    }
314
315    // Lookup contract state
316    let tree_handle = match contracts.lookup(&cid, &db_name) {
317        Ok(v) => v,
318        Err(_) => return darkfi_sdk::error::DB_LOOKUP_FAILED,
319    };
320
321    // Create the DbHandle
322    let db_handle = DbHandle::new(cid, tree_handle);
323    let mut db_handles = env.db_handles.borrow_mut();
324
325    // Make sure we don't duplicate the DbHandle in the vec
326    if let Some(index) = db_handles.iter().position(|x| x == &db_handle) {
327        return index as i64
328    }
329
330    // Push the new DbHandle to the Vec of opened DbHandles
331    match db_handles.len().try_into() {
332        Ok(db_handle_idx) => {
333            db_handles.push(db_handle);
334            db_handle_idx
335        }
336        Err(_) => {
337            error!(
338                target: "runtime::db::db_lookup",
339                "[WASM] [{cid}] db_lookup(): Too many open DbHandles"
340            );
341            darkfi_sdk::error::DB_LOOKUP_FAILED
342        }
343    }
344}
345
346/// Set a value within the transaction.
347///
348/// * `ptr` must contain the DbHandle index and the key-value pair.
349/// * The DbHandle must match the ContractId.
350///
351/// This function can be called only from the Deploy or Update [`ContractSection`].
352/// Returns `SUCCESS` on success, otherwise returns an error value.
353///
354/// Permissions: deploy, update
355pub(crate) fn db_set(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
356    let (env, mut store) = ctx.data_and_store_mut();
357    let cid = env.contract_id;
358
359    if let Err(e) = acl_allow(env, &[ContractSection::Deploy, ContractSection::Update]) {
360        error!(
361            target: "runtime::db::db_set",
362            "[WASM] [{cid}] db_set(): Called in unauthorized section: {e}"
363        );
364        return darkfi_sdk::error::CALLER_ACCESS_DENIED
365    }
366
367    // Subtract used gas. Here we count the bytes written into the database.
368    // TODO: We might want to count only the difference in size if we're replacing
369    // data and the new data is larger.
370    env.subtract_gas(&mut store, ptr_len as u64);
371
372    // Ensure that it is possible to read from the memory that this function needs
373    let memory_view = env.memory_view(&store);
374    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
375        error!(
376            target: "runtime::db::db_set",
377            "[WASM] [{cid}] db_set(): Failed to make slice from ptr"
378        );
379        return darkfi_sdk::error::DB_SET_FAILED
380    };
381
382    let mut buf = vec![0_u8; ptr_len as usize];
383    if let Err(e) = mem_slice.read_slice(&mut buf) {
384        error!(
385            target: "runtime::db::db_set",
386            "[WASM] [{cid}] db_set(): Failed to read from memory slice: {e}"
387        );
388        return darkfi_sdk::error::DB_SET_FAILED
389    };
390
391    let mut buf_reader = Cursor::new(buf);
392
393    // Decode DbHandle index
394    let db_handle_index: u32 = match Decodable::decode(&mut buf_reader) {
395        Ok(v) => v,
396        Err(e) => {
397            error!(
398                target: "runtime::db::db_set",
399                "[WASM] [{cid}] db_set(): Failed to decode DbHandle: {e}"
400            );
401            return darkfi_sdk::error::DB_SET_FAILED
402        }
403    };
404
405    let db_handle_index = db_handle_index as usize;
406
407    // Decode key and value
408    let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
409        Ok(v) => v,
410        Err(e) => {
411            error!(
412                target: "runtime::db::db_set",
413                "[WASM] [{cid}] db_set(): Failed to decode key vec: {e}"
414            );
415            return darkfi_sdk::error::DB_SET_FAILED
416        }
417    };
418
419    let value: Vec<u8> = match Decodable::decode(&mut buf_reader) {
420        Ok(v) => v,
421        Err(e) => {
422            error!(
423                target: "runtime::db::db_set",
424                "[WASM] [{cid}] db_set(): Failed to decode value vec: {e}"
425            );
426            return darkfi_sdk::error::DB_SET_FAILED
427        }
428    };
429
430    // Make sure we've read the entire buffer
431    if buf_reader.position() != ptr_len as u64 {
432        error!(
433            target: "runtime::db::db_set",
434            "[WASM] [{cid}] db_set(): Trailing bytes in argument stream"
435        );
436        return darkfi_sdk::error::DB_SET_FAILED
437    }
438
439    let db_handles = env.db_handles.borrow();
440
441    // Check DbHandle index is within bounds
442    if db_handles.len() <= db_handle_index {
443        error!(
444            target: "runtime::db::db_set",
445            "[WASM] [{cid}] db_set(): Requested DbHandle that is out of bounds"
446        );
447        return darkfi_sdk::error::DB_SET_FAILED
448    }
449
450    // Retrive DbHandle using the index
451    let db_handle = &db_handles[db_handle_index];
452
453    // Validate that the DbHandle matches the contract ID
454    if db_handle.contract_id != env.contract_id {
455        error!(
456            target: "runtime::db::db_set",
457            "[WASM] [{cid}] db_set(): Unauthorized to write to DbHandle"
458        );
459        return darkfi_sdk::error::CALLER_ACCESS_DENIED
460    }
461
462    // Insert key-value pair into the database corresponding to this contract
463    if env
464        .blockchain
465        .lock()
466        .unwrap()
467        .overlay
468        .lock()
469        .unwrap()
470        .insert(&db_handle.tree, &key, &value)
471        .is_err()
472    {
473        error!(
474            target: "runtime::db::db_set",
475            "[WASM] [{cid}] db_set(): Couldn't insert to db_handle tree"
476        );
477        return darkfi_sdk::error::DB_SET_FAILED
478    }
479
480    wasm::entrypoint::SUCCESS
481}
482
483/// Remove a key from the database.
484///
485/// This function can be called only from the Deploy or Update [`ContractSection`].
486/// Returns `SUCCESS` on success, otherwise returns an error value.
487///
488/// Permissions: deploy, update
489pub(crate) fn db_del(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
490    let (env, mut store) = ctx.data_and_store_mut();
491    let cid = env.contract_id;
492
493    if let Err(e) = acl_allow(env, &[ContractSection::Deploy, ContractSection::Update]) {
494        error!(
495            target: "runtime::db::db_del",
496            "[WASM] [{cid}] db_del(): Called in unauthorized section: {e}"
497        );
498        return darkfi_sdk::error::CALLER_ACCESS_DENIED
499    }
500
501    // Subtract used gas. We make deletion free.
502    env.subtract_gas(&mut store, 1);
503
504    // Ensure that it is possible to read from the memory that this function needs
505    let memory_view = env.memory_view(&store);
506
507    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
508        error!(
509            target: "runtime::db::db_del",
510            "[WASM] [{cid}] db_del(): Failed to make slice from ptr"
511        );
512        return darkfi_sdk::error::DB_DEL_FAILED
513    };
514
515    let mut buf = vec![0_u8; ptr_len as usize];
516    if let Err(e) = mem_slice.read_slice(&mut buf) {
517        error!(
518            target: "runtime::db::db_del",
519            "[WASM] [{cid}] db_del(): Failed to read from memory slice: {e}"
520        );
521        return darkfi_sdk::error::DB_DEL_FAILED
522    };
523
524    let mut buf_reader = Cursor::new(buf);
525
526    // Decode DbHandle index
527    let db_handle_index: u32 = match Decodable::decode(&mut buf_reader) {
528        Ok(v) => v,
529        Err(e) => {
530            error!(
531                target: "runtime::db::db_del",
532                "[WASM] [{cid}] db_del(): Failed to decode DbHandle: {e}"
533            );
534            return darkfi_sdk::error::DB_DEL_FAILED
535        }
536    };
537    let db_handle_index = db_handle_index as usize;
538
539    // Decode key corresponding to the value that will be deleted
540    let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
541        Ok(v) => v,
542        Err(e) => {
543            error!(
544                target: "runtime::db::db_del",
545                "[WASM] [{cid}] db_del(): Failed to decode key vec: {e}"
546            );
547            return darkfi_sdk::error::DB_DEL_FAILED
548        }
549    };
550
551    // Make sure we've read the entire buffer
552    if buf_reader.position() != ptr_len as u64 {
553        error!(
554            target: "runtime::db::db_del",
555            "[WASM] [{cid}] db_del(): Trailing bytes in argument stream"
556        );
557        return darkfi_sdk::error::DB_DEL_FAILED
558    }
559
560    let db_handles = env.db_handles.borrow();
561
562    if db_handles.len() <= db_handle_index {
563        error!(
564            target: "runtime::db::db_del",
565            "[WASM] [{cid}] db_del(): Requested DbHandle that is out of bounds"
566        );
567        return darkfi_sdk::error::DB_DEL_FAILED
568    }
569
570    // Retrive DbHandle using the index
571    let db_handle = &db_handles[db_handle_index];
572
573    // Validate that the DbHandle matches the contract ID
574    if db_handle.contract_id != cid {
575        error!(
576            target: "runtime::db::db_del",
577            "[WASM] [{cid}] db_del(): Unauthorized to write to DbHandle"
578        );
579        return darkfi_sdk::error::CALLER_ACCESS_DENIED
580    }
581
582    // Remove key-value pair from the database corresponding to this contract
583    if env.blockchain.lock().unwrap().overlay.lock().unwrap().remove(&db_handle.tree, &key).is_err()
584    {
585        error!(
586            target: "runtime::db::db_del",
587            "[WASM] [{cid}] db_del(): Couldn't remove key from db_handle tree"
588        );
589        return darkfi_sdk::error::DB_DEL_FAILED
590    }
591
592    wasm::entrypoint::SUCCESS
593}
594
595/// Reads a value by key from the key-value store.
596///
597/// This function can be called from the Deploy, Exec, or Metadata [`ContractSection`].
598///
599/// On success, returns the length of the `objects` Vector in the environment.
600/// Otherwise, returns an error code.
601///
602/// Permissions: deploy, metadata, exec
603pub(crate) fn db_get(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
604    let (env, mut store) = ctx.data_and_store_mut();
605    let cid = env.contract_id;
606
607    if let Err(e) =
608        acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
609    {
610        error!(
611            target: "runtime::db::db_get",
612            "[WASM] [{cid}] db_get(): Called in unauthorized section: {e}"
613        );
614        return darkfi_sdk::error::CALLER_ACCESS_DENIED
615    }
616
617    // Subtract used gas. Reading is free.
618    env.subtract_gas(&mut store, 1);
619
620    // Ensure that it is possible to read memory
621    let memory_view = env.memory_view(&store);
622    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
623        error!(
624            target: "runtime::db::db_get",
625            "[WASM] [{cid}] db_get(): Failed to make slice from ptr"
626        );
627        return darkfi_sdk::error::DB_GET_FAILED
628    };
629
630    let mut buf = vec![0_u8; ptr_len as usize];
631    if let Err(e) = mem_slice.read_slice(&mut buf) {
632        error!(
633            target: "runtime::db::db_get",
634            "[WASM] [{cid}] db_get(): Failed to read from memory slice: {e}"
635        );
636        return darkfi_sdk::error::DB_GET_FAILED
637    };
638
639    let mut buf_reader = Cursor::new(buf);
640
641    // Decode DbHandle index
642    let db_handle_index: u32 = match Decodable::decode(&mut buf_reader) {
643        Ok(v) => v,
644        Err(e) => {
645            error!(
646                target: "runtime::db::db_get",
647                "[WASM] [{cid}] db_get(): Failed to decode DbHandle: {e}"
648            );
649            return darkfi_sdk::error::DB_GET_FAILED
650        }
651    };
652
653    let db_handle_index = db_handle_index as usize;
654
655    // Decode key for key-value pair that we wish to retrieve
656    let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
657        Ok(v) => v,
658        Err(e) => {
659            error!(
660                target: "runtime::db::db_get",
661                "[WASM] [{cid}] db_get(): Failed to decode key from vec: {e}"
662            );
663            return darkfi_sdk::error::DB_GET_FAILED
664        }
665    };
666
667    // Make sure there are no trailing bytes in the buffer. This means we've used all data that was
668    // supplied.
669    if buf_reader.position() != ptr_len as u64 {
670        error!(
671            target: "runtime::db::db_get",
672            "[WASM] [{cid}] db_get(): Trailing bytes in argument stream"
673        );
674        return darkfi_sdk::error::DB_GET_FAILED
675    }
676
677    let db_handles = env.db_handles.borrow();
678
679    // Ensure that the index is within bounds
680    if db_handles.len() <= db_handle_index {
681        error!(
682            target: "runtime::db::db_get",
683            "[WASM] [{cid}] db_get(): Requested DbHandle that is out of bounds"
684        );
685        return darkfi_sdk::error::DB_GET_FAILED
686    }
687
688    // Get DbHandle using db_handle_index
689    let db_handle = &db_handles[db_handle_index];
690
691    // Retrieve data using the `key`
692    let ret =
693        match env.blockchain.lock().unwrap().overlay.lock().unwrap().get(&db_handle.tree, &key) {
694            Ok(v) => v,
695            Err(e) => {
696                error!(
697                    target: "runtime::db::db_get",
698                    "[WASM] [{cid}] db_get(): Internal error getting from tree: {e}"
699                );
700                return darkfi_sdk::error::DB_GET_FAILED
701            }
702        };
703    drop(db_handles);
704
705    // Return special error if the data is empty
706    let Some(return_data) = ret else {
707        debug!(
708            target: "runtime::db::db_get",
709            "[WASM] [{cid}] db_get(): Return data is empty"
710        );
711        return darkfi_sdk::error::DB_GET_EMPTY
712    };
713
714    if return_data.len() > u32::MAX as usize {
715        return darkfi_sdk::error::DATA_TOO_LARGE
716    }
717
718    // Subtract used gas. Here we count the length of the data read from db.
719    env.subtract_gas(&mut store, return_data.len() as u64);
720
721    // Copy the data (Vec<u8>) to the VM by pushing it to the objects Vector.
722    let mut objects = env.objects.borrow_mut();
723    if objects.len() == u32::MAX as usize {
724        return darkfi_sdk::error::DATA_TOO_LARGE
725    }
726
727    // Return the length of the objects Vector.
728    // This is the location of the data that was retrieved and pushed
729    objects.push(return_data.to_vec());
730    (objects.len() - 1) as i64
731}
732
733/// Check if a database contains a given key.
734///
735/// Returns `1` if the key is found.
736/// Returns `0` if the key is not found and there are no errors.
737/// Otherwise, returns an error code.
738///
739/// Permissions: deploy, metadata, exec
740pub(crate) fn db_contains_key(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
741    let (env, mut store) = ctx.data_and_store_mut();
742    let cid = env.contract_id;
743
744    if let Err(e) =
745        acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
746    {
747        error!(
748            target: "runtime::db::db_contains_key",
749            "[WASM] [{cid}] db_contains_key(): Called in unauthorized section: {e}"
750        );
751        return darkfi_sdk::error::CALLER_ACCESS_DENIED
752    }
753
754    // Subtract used gas. Reading is free.
755    env.subtract_gas(&mut store, 1);
756
757    // Ensure memory is readable
758    let memory_view = env.memory_view(&store);
759    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
760        error!(
761            target: "runtime::db::db_contains_key",
762            "[WASM] [{cid}] db_contains_key(): Failed to make slice from ptr"
763        );
764        return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
765    };
766
767    let mut buf = vec![0_u8; ptr_len as usize];
768    if let Err(e) = mem_slice.read_slice(&mut buf) {
769        error!(
770            target: "runtime::db::db_contains_key",
771            "[WASM] [{cid}] db_contains_key(): Failed to read from memory slice: {e}"
772        );
773        return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
774    };
775
776    let mut buf_reader = Cursor::new(buf);
777
778    // Decode DbHandle index
779    let db_handle_index: u32 = match Decodable::decode(&mut buf_reader) {
780        Ok(v) => v,
781        Err(e) => {
782            error!(
783                target: "runtime::db::db_contains_key",
784                "[WASM] [{cid}] db_contains_key(): Failed to decode DbHandle: {e}"
785            );
786            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
787        }
788    };
789
790    let db_handle_index = db_handle_index as usize;
791
792    // Decode key
793    let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
794        Ok(v) => v,
795        Err(e) => {
796            error!(
797                target: "runtime::db::db_contains_key",
798                "[WASM] [{cid}] db_contains_key(): Failed to decode key vec: {e}"
799            );
800            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
801        }
802    };
803
804    // Make sure there are no trailing bytes in the buffer.
805    // This means we've used all data that was supplied.
806    if buf_reader.position() != ptr_len as u64 {
807        error!(
808            target: "runtime::db::db_contains_key",
809            "[WASM] [{cid}] db_contains_key(): Trailing bytes in argument stream"
810        );
811        return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
812    }
813
814    let db_handles = env.db_handles.borrow();
815
816    // Ensure DbHandle index is within bounds
817    if db_handles.len() <= db_handle_index {
818        error!(
819            target: "runtime::db::db_contains_key",
820            "[WASM] [{cid}] db_contains_key(): Requested DbHandle that is out of bounds"
821        );
822        return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
823    }
824
825    // Retrieve DbHandle using the index
826    let db_handle = &db_handles[db_handle_index];
827
828    // Lookup key parameter in the database
829    match env.blockchain.lock().unwrap().overlay.lock().unwrap().contains_key(&db_handle.tree, &key)
830    {
831        Ok(v) => i64::from(v), // <- 0=false, 1=true. Convert bool to i64.
832        Err(e) => {
833            error!(
834                target: "runtime::db::db_contains_key",
835                "[WASM] [{cid}] db_contains_key(): sled.tree.contains_key failed: {e}"
836            );
837            darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
838        }
839    }
840}
841
842/// Given a zkas circuit, create a VerifyingKey and insert them both into the db.
843///
844/// This function can only be called from the Deploy [`ContractSection`].
845/// Returns `SUCCESS` on success, otherwise returns an error code.
846///
847/// Permissions: deploy
848pub(crate) fn zkas_db_set(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
849    let (env, mut store) = ctx.data_and_store_mut();
850    let cid = env.contract_id;
851
852    if let Err(e) = acl_allow(env, &[ContractSection::Deploy]) {
853        error!(
854            target: "runtime::db::zkas_db_set",
855            "[WASM] [{cid}] zkas_db_set(): Called in unauthorized section: {e}"
856        );
857        return darkfi_sdk::error::CALLER_ACCESS_DENIED
858    }
859
860    let memory_view = env.memory_view(&store);
861
862    // Ensure that the memory is readable
863    let Ok(mem_slice) = ptr.slice(&memory_view, ptr_len) else {
864        error!(
865            target: "runtime::db::zkas_db_set",
866            "[WASM] [{cid}] zkas_db_set(): Failed to make slice from ptr"
867        );
868        return darkfi_sdk::error::DB_SET_FAILED
869    };
870
871    let mut buf = vec![0u8; ptr_len as usize];
872    if let Err(e) = mem_slice.read_slice(&mut buf) {
873        error!(
874            target: "runtime::db::zkas_db_set",
875            "[WASM] [{cid}] zkas_db_set(): Failed to read from memory slice: {e}"
876        );
877        return darkfi_sdk::error::DB_SET_FAILED
878    };
879
880    // Deserialize the ZkBinary bytes from the buffer
881    let zkbin_bytes: Vec<u8> = match deserialize(&buf) {
882        Ok(zkbin) => zkbin,
883        Err(e) => {
884            error!(
885                target: "runtime::db::zkas_db_set",
886                "[WASM] [{cid}] zkas_db_set(): Could not deserialize bytes from buffer: {e}"
887            );
888            return darkfi_sdk::error::DB_SET_FAILED
889        }
890    };
891
892    // Validate the bytes by decoding them into the ZkBinary format
893    let zkbin = match ZkBinary::decode(&zkbin_bytes, false) {
894        Ok(zkbin) => zkbin,
895        Err(e) => {
896            error!(
897                target: "runtime::db::zkas_db_set",
898                "[WASM] [{cid}] zkas_db_set(): Invalid zkas bincode passed to function: {e}"
899            );
900            return darkfi_sdk::error::DB_SET_FAILED
901        }
902    };
903
904    // Subtract used gas. We count 100 gas per opcode, witness, and literal.
905    // This is likely bad.
906    // TODO: This should be better-priced.
907    let gas_cost =
908        (zkbin.literals.len() + zkbin.witnesses.len() + zkbin.opcodes.len()) as u64 * 100;
909    env.subtract_gas(&mut store, gas_cost);
910
911    // Because of `Runtime::Deploy`, we should be sure that the zkas db is index zero.
912    let db_handles = env.db_handles.borrow();
913    let db_handle = &db_handles[0];
914    // Redundant check
915    if db_handle.contract_id != cid {
916        error!(
917            target: "runtime::db::zkas_db_set",
918            "[WASM] [{cid}] zkas_db_set(): Internal error, zkas db at index 0 incorrect"
919        );
920        return darkfi_sdk::error::DB_SET_FAILED
921    }
922
923    // Check if there is existing bincode and compare it. Return DB_SUCCESS if
924    // they're the same. The assumption should be that VerifyingKey was generated
925    // already so we can skip things after this guard.
926    match env
927        .blockchain
928        .lock()
929        .unwrap()
930        .overlay
931        .lock()
932        .unwrap()
933        .get(&db_handle.tree, &serialize(&zkbin.namespace))
934    {
935        Ok(v) => {
936            if let Some(bytes) = v {
937                // We allow a panic here because this db should never be corrupted in this way.
938                let (existing_zkbin, _): (Vec<u8>, Vec<u8>) =
939                    deserialize(&bytes).expect("deserialize tuple");
940
941                if existing_zkbin == zkbin_bytes {
942                    debug!(
943                        target: "runtime::db::zkas_db_set",
944                        "[WASM] [{cid}] zkas_db_set(): Existing zkas bincode is the same. Skipping."
945                    );
946                    return wasm::entrypoint::SUCCESS
947                }
948            }
949        }
950        Err(e) => {
951            error!(
952                target: "runtime::db::zkas_db_set",
953                "[WASM] [{cid}] zkas_db_set(): Internal error getting from tree: {e}"
954            );
955            return darkfi_sdk::error::DB_SET_FAILED
956        }
957    };
958
959    // We didn't find any existing bincode, so let's create a new VerifyingKey and write it all.
960    info!(
961        target: "runtime::db::zkas_db_set",
962        "[WASM] [{cid}] zkas_db_set(): Creating VerifyingKey for {} zkas circuit",
963        zkbin.namespace,
964    );
965
966    let witnesses = match empty_witnesses(&zkbin) {
967        Ok(w) => w,
968        Err(e) => {
969            error!(
970                target: "runtime::db::zkas_db_set",
971                "[WASM] [{cid}] zkas_db_set(): Failed to create empty witnesses: {e}"
972            );
973            return darkfi_sdk::error::DB_SET_FAILED
974        }
975    };
976
977    // Construct the circuit and build the VerifyingKey
978    let circuit = ZkCircuit::new(witnesses, &zkbin);
979    let vk = VerifyingKey::build(zkbin.k, &circuit);
980    let mut vk_buf = vec![];
981    if let Err(e) = vk.write(&mut vk_buf) {
982        error!(
983            target: "runtime::db::zkas_db_set",
984            "[WASM] [{cid}] zkas_db_set(): Failed to serialize VerifyingKey: {e}"
985        );
986        return darkfi_sdk::error::DB_SET_FAILED
987    }
988
989    // Insert the key-value pair into the database.
990    let key = serialize(&zkbin.namespace);
991    let value = serialize(&(zkbin_bytes, vk_buf));
992    if env
993        .blockchain
994        .lock()
995        .unwrap()
996        .overlay
997        .lock()
998        .unwrap()
999        .insert(&db_handle.tree, &key, &value)
1000        .is_err()
1001    {
1002        error!(
1003            target: "runtime::db::zkas_db_set",
1004            "[WASM] [{cid}] zkas_db_set(): Couldn't insert to db_handle tree"
1005        );
1006        return darkfi_sdk::error::DB_SET_FAILED
1007    }
1008    drop(db_handles);
1009
1010    // Subtract used gas. Here we count the bytes written into the db.
1011    env.subtract_gas(&mut store, (key.len() + value.len()) as u64);
1012
1013    wasm::entrypoint::SUCCESS
1014}