darkfi/runtime/import/db/
db_contains_key.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::Decodable;
20use tracing::error;
21use wasmer::{FunctionEnvMut, WasmPtr};
22
23use crate::runtime::{
24    import::{acl::acl_allow, util::wasm_mem_read},
25    vm_runtime::{ContractSection, Env},
26};
27
28/// Check if an on-chain database contains a given key.
29///
30/// Returns `1` if the key is found.
31/// Returns `0` if the key is not found and there are no errors.
32/// Otherwise, returns an error code.
33///
34/// ## Permissions
35/// * `ContractSection::Deploy`
36/// * `ContractSection::Metadata`
37/// * `ContractSection::Exec`
38pub(crate) fn db_contains_key(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
39    db_contains_key_internal(ctx, ptr, ptr_len, false)
40}
41
42/// Check if a tx-local database contains a given key.
43///
44/// Returns `1` if the key is found.
45/// Returns `0` if the key is not found and there are no errors.
46/// Otherwise, returns an error code.
47///
48/// ## Permissions
49/// * `ContractSection::Deploy`
50/// * `ContractSection::Metadata`
51/// * `ContractSection::Exec`
52pub(crate) fn db_contains_key_local(
53    ctx: FunctionEnvMut<Env>,
54    ptr: WasmPtr<u8>,
55    ptr_len: u32,
56) -> i64 {
57    db_contains_key_internal(ctx, ptr, ptr_len, true)
58}
59
60/// Internal `db_contains_key` function which branches to either on-chain or
61/// tx-local.
62///
63/// ## Permissions
64/// * `ContractSection::Deploy`
65/// * `ContractSection::Metadata`
66/// * `ContractSection::Exec`
67fn db_contains_key_internal(
68    mut ctx: FunctionEnvMut<Env>,
69    ptr: WasmPtr<u8>,
70    ptr_len: u32,
71    local: bool,
72) -> i64 {
73    let lt = if local { "db_contains_key_local" } else { "db_contains_key" };
74    let (env, mut store) = ctx.data_and_store_mut();
75    let cid = env.contract_id;
76
77    // Enforce function ACL
78    if let Err(e) =
79        acl_allow(env, &[ContractSection::Deploy, ContractSection::Metadata, ContractSection::Exec])
80    {
81        error!(
82            target: "runtime::db::{lt}",
83            "[WASM] [{cid}] {lt}(): Called in unauthorized section: {e}",
84        );
85        return darkfi_sdk::error::CALLER_ACCESS_DENIED
86    }
87
88    // Subtract used gas.
89    // Reading is free.
90    env.subtract_gas(&mut store, 1);
91
92    // Get the WASM memory reader
93    let mut buf_reader = match wasm_mem_read(env, &store, ptr, ptr_len) {
94        Ok(v) => v,
95        Err(e) => {
96            error!(
97                target: "runtime::db::{lt}",
98                "[WASM] [{cid}] {lt}(): Failed to read wasm memory: {e}",
99            );
100            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
101        }
102    };
103
104    // Decode DbHandle index
105    let db_handle_index: u32 = match Decodable::decode(&mut buf_reader) {
106        Ok(v) => v,
107        Err(e) => {
108            error!(
109                target: "runtime::db::{lt}",
110                "[WASM] [{cid}] {lt}(): Failed to decode DbHandle: {e}",
111            );
112            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
113        }
114    };
115
116    let db_handle_index = db_handle_index as usize;
117
118    // Decode key
119    let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
120        Ok(v) => v,
121        Err(e) => {
122            error!(
123                target: "runtime::db::{lt}",
124                "[WASM] [{cid}] {lt}(): Failed to decode key vec: {e}",
125            );
126            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
127        }
128    };
129
130    // Make sure we've read the entire buffer
131    if buf_reader.position() != ptr_len as u64 {
132        error!(
133            target: "runtime::db::{lt}",
134            "[WASM] [{cid}] {lt}(): Trailing bytes in argument stream",
135        );
136        return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
137    }
138
139    // Fetch requested db handles
140    let db_handles = if local { env.local_db_handles.borrow() } else { env.db_handles.borrow() };
141
142    // Ensure DbHandle index is within bounds
143    if db_handles.len() <= db_handle_index {
144        error!(
145            target: "runtime::db::{lt}",
146            "[WASM] [{cid}] {lt}(): Requested DbHandle out of bounds",
147        );
148        return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
149    }
150
151    // Retrieve DbHandle using the index
152    let db_handle = &db_handles[db_handle_index];
153
154    // Lookup key parameter in the appropriate db
155    if local {
156        let db = env.tx_local.lock();
157        let Some(db_cid) = db.get(&db_handle.contract_id) else {
158            error!(
159                target: "runtime::db::{lt}",
160                "[WASM] [{cid}] {lt}(): Could not find db for {}",
161                db_handle.contract_id,
162            );
163            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
164        };
165
166        let Some(tree) = db_cid.get(&db_handle.tree) else {
167            error!(
168                target: "runtime::db::{lt}",
169                "[WASM] [{cid}] {lt}(): Could not find db tree for {}",
170                db_handle.contract_id,
171            );
172            return darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
173        };
174
175        // 0=false, 1=true. Convert bool to i64.
176        return i64::from(tree.contains_key(&key))
177    }
178
179    // On-chain db
180    match env.blockchain.lock().unwrap().overlay.lock().unwrap().contains_key(&db_handle.tree, &key)
181    {
182        Ok(v) => i64::from(v), // <- 0=false, 1=true. Convert bool to i64.
183        Err(e) => {
184            error!(
185                target: "runtime::db::{lt}",
186                "[WASM] [{cid}] {lt}(): sled.tree.contains_key failed: {e}",
187            );
188            darkfi_sdk::error::DB_CONTAINS_KEY_FAILED
189        }
190    }
191}