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