darkfi/runtime/import/
merkle.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::{MerkleNode, MerkleTree},
23    hex::AsHex,
24    wasm,
25};
26use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
27use tracing::{debug, error};
28use wasmer::{FunctionEnvMut, WasmPtr};
29
30use super::acl::acl_allow;
31use crate::runtime::vm_runtime::{ContractSection, Env};
32
33/// Adds data to merkle tree. The tree, database connection, and new data to add is
34/// read from `ptr` at offset specified by `len`.
35/// Returns `0` on success; otherwise, returns an error-code corresponding to a
36/// [`darkfi_sdk::error::ContractError`] (defined in the SDK).
37/// See also the method `merkle_add` in `sdk/src/merkle.rs`.
38///
39/// Permissions: update
40pub(crate) fn merkle_add(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
41    let (env, mut store) = ctx.data_and_store_mut();
42    let cid = env.contract_id;
43
44    // Enforce function ACL
45    if let Err(e) = acl_allow(env, &[ContractSection::Update]) {
46        error!(
47            target: "runtime::merkle::merkle_add",
48            "[WASM] [{cid}] merkle_add(): Called in unauthorized section: {e}"
49        );
50        return darkfi_sdk::error::CALLER_ACCESS_DENIED
51    }
52
53    // Subtract used gas.
54    // This makes calling the function which returns early have some (small) cost.
55    env.subtract_gas(&mut store, 1);
56
57    // Subtract written bytes as gas
58    env.subtract_gas(&mut store, 33 /* value_data.len() as u64 */);
59
60    let memory_view = env.memory_view(&store);
61    let Ok(mem_slice) = ptr.slice(&memory_view, len) else {
62        error!(
63            target: "runtime::merkle::merkle_add",
64            "[WASM] [{cid}] merkle_add(): Failed to make slice from ptr"
65        );
66        return darkfi_sdk::error::INTERNAL_ERROR
67    };
68
69    let mut buf = vec![0_u8; len as usize];
70    if let Err(e) = mem_slice.read_slice(&mut buf) {
71        error!(
72            target: "runtime::merkle::merkle_add",
73            "[WASM] [{cid}] merkle_add(): Failed to read from memory slice: {e}"
74        );
75        return darkfi_sdk::error::INTERNAL_ERROR
76    };
77
78    // The buffer should deserialize into:
79    // - db_info
80    // - db_roots
81    // - root_key (as Vec<u8>) (key being the name of the sled key in info_db where the latest root is)
82    // - tree_key (as Vec<u8>) (key being the name of the sled key in info_db where the Merkle tree is)
83    // - coins (as Vec<MerkleNode>) (the coins being added into the Merkle tree)
84    let mut buf_reader = Cursor::new(buf);
85    // FIXME: There's a type DbHandle=u32, but this should maybe be renamed
86    let db_info_index: u32 = match Decodable::decode(&mut buf_reader) {
87        Ok(v) => v,
88        Err(e) => {
89            error!(
90                target: "runtime::merkle::merkle_add",
91                "[WASM] [{cid}] merkle_add(): Failed to decode db_info DbHandle: {e}"
92            );
93            return darkfi_sdk::error::INTERNAL_ERROR
94        }
95    };
96    let db_info_index = db_info_index as usize;
97
98    let db_roots_index: u32 = match Decodable::decode(&mut buf_reader) {
99        Ok(v) => v,
100        Err(e) => {
101            error!(
102                target: "runtime::merkle::merkle_add",
103                "[WASM] [{cid}] merkle_add(): Failed to decode db_roots DbHandle: {e}"
104            );
105            return darkfi_sdk::error::INTERNAL_ERROR
106        }
107    };
108    let db_roots_index = db_roots_index as usize;
109
110    let db_handles = env.db_handles.borrow();
111    let n_dbs = db_handles.len();
112
113    if n_dbs <= db_info_index || n_dbs <= db_roots_index {
114        error!(
115            target: "runtime::merkle::merkle_add",
116            "[WASM] [{cid}] merkle_add(): Requested DbHandle that is out of bounds"
117        );
118        return darkfi_sdk::error::INTERNAL_ERROR
119    }
120    let db_info = &db_handles[db_info_index];
121    let db_roots = &db_handles[db_roots_index];
122
123    // Make sure that the contract owns the dbs it wants to write to
124    if db_info.contract_id != env.contract_id || db_roots.contract_id != env.contract_id {
125        error!(
126            target: "runtime::merkle::merkle_add",
127            "[WASM] [{cid}] merkle_add(): Unauthorized to write to DbHandle"
128        );
129        return darkfi_sdk::error::CALLER_ACCESS_DENIED
130    }
131
132    // This `key` represents the sled key in info where the latest root is
133    let root_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
134        Ok(v) => v,
135        Err(e) => {
136            error!(
137                target: "runtime::merkle::merkle_add",
138                "[WASM] [{cid}] merkle_add(): Failed to decode key vec: {e}"
139            );
140            return darkfi_sdk::error::INTERNAL_ERROR
141        }
142    };
143
144    // This `key` represents the sled key in info where the Merkle tree is
145    let tree_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
146        Ok(v) => v,
147        Err(e) => {
148            error!(
149                target: "runtime::merkle::merkle_add",
150                "[WASM] [{cid}] merkle_add(): Failed to decode key vec: {e}"
151            );
152            return darkfi_sdk::error::INTERNAL_ERROR
153        }
154    };
155
156    // This `coin` represents the leaf we're adding to the Merkle tree
157    let coins: Vec<MerkleNode> = match Decodable::decode(&mut buf_reader) {
158        Ok(v) => v,
159        Err(e) => {
160            error!(
161                target: "runtime::merkle::merkle_add",
162                "[WASM] [{cid}] merkle_add(): Failed to decode MerkleNode: {e}"
163            );
164            return darkfi_sdk::error::INTERNAL_ERROR
165        }
166    };
167
168    // Make sure we've read the entire buffer
169    if buf_reader.position() != (len as u64) {
170        error!(
171            target: "runtime::merkle::merkle_add",
172            "[WASM] [{cid}] merkle_add(): Mismatch between given length, and cursor length"
173        );
174        return darkfi_sdk::error::INTERNAL_ERROR
175    }
176
177    // Locking should happen for the entire duration of this fn. This is unsafe otherwise.
178    let lock = env.blockchain.lock().unwrap();
179    let mut overlay = lock.overlay.lock().unwrap();
180    // Read the current tree
181    let ret = match overlay.get(&db_info.tree, &tree_key) {
182        Ok(v) => v,
183        Err(e) => {
184            error!(
185                target: "runtime::merkle::merkle_add",
186                "[WASM] [{cid}] merkle_add(): Internal error getting from tree: {e}"
187            );
188            return darkfi_sdk::error::INTERNAL_ERROR
189        }
190    };
191
192    let Some(return_data) = ret else {
193        error!(
194            target: "runtime::merkle::merkle_add",
195            "[WASM] [{cid}] merkle_add(): Return data is empty"
196        );
197        return darkfi_sdk::error::INTERNAL_ERROR
198    };
199
200    debug!(
201        target: "runtime::merkle::merkle_add",
202        "Serialized tree: {} bytes",
203        return_data.len()
204    );
205    debug!(
206        target: "runtime::merkle::merkle_add",
207        "                 {}",
208        return_data.hex()
209    );
210
211    let mut decoder = Cursor::new(&return_data);
212    let set_size: u32 = match Decodable::decode(&mut decoder) {
213        Ok(v) => v,
214        Err(e) => {
215            error!(
216                target: "runtime::merkle::merkle_add",
217                "[WASM] [{cid}] merkle_add(): Unable to read set size: {e}"
218            );
219            return darkfi_sdk::error::INTERNAL_ERROR
220        }
221    };
222
223    let mut tree: MerkleTree = match Decodable::decode(&mut decoder) {
224        Ok(v) => v,
225        Err(e) => {
226            error!(
227                target: "runtime::merkle::merkle_add",
228                "[WASM] [{cid}] merkle_add(): Unable to deserialize Merkle tree: {e}"
229            );
230            return darkfi_sdk::error::INTERNAL_ERROR
231        }
232    };
233
234    // Here we add the new coins into the tree.
235    let coins_len = coins.len();
236    for coin in coins {
237        tree.append(coin);
238    }
239
240    // And we serialize the tree back to bytes
241    let mut tree_data = Vec::new();
242    if tree_data.write_u32(set_size + coins_len as u32).is_err() ||
243        tree.encode(&mut tree_data).is_err()
244    {
245        error!(
246            target: "runtime::merkle::merkle_add",
247            "[WASM] [{cid}] merkle_add(): Couldn't reserialize modified tree"
248        );
249        return darkfi_sdk::error::INTERNAL_ERROR
250    }
251
252    // Apply changes to overlay
253    if overlay.insert(&db_info.tree, &tree_key, &tree_data).is_err() {
254        error!(
255            target: "runtime::merkle::merkle_add",
256            "[WASM] [{cid}] merkle_add(): Couldn't insert to db_info tree"
257        );
258        return darkfi_sdk::error::INTERNAL_ERROR
259    }
260
261    // Here we add the Merkle root to our set of roots
262    // Since each update to the tree is atomic, we only need to add the last root.
263    let Some(latest_root) = tree.root(0) else {
264        error!(
265            target: "runtime::merkle::merkle_add",
266            "[WASM] [{cid}] merkle_add(): Unable to read the root of tree"
267        );
268        return darkfi_sdk::error::INTERNAL_ERROR
269    };
270
271    debug!(
272        target: "runtime::merkle::merkle_add",
273        "[WASM] [{cid}] merkle_add(): Appending Merkle root to db: {latest_root:?}"
274    );
275    let latest_root_data = serialize(&latest_root);
276    assert_eq!(latest_root_data.len(), 32);
277
278    let mut value_data = Vec::with_capacity(32 + 1);
279    env.tx_hash.inner().encode(&mut value_data).expect("Unable to serialize tx_hash");
280    env.call_idx.encode(&mut value_data).expect("Unable to serialize call_idx");
281    assert_eq!(value_data.len(), 32 + 1);
282
283    if overlay.insert(&db_roots.tree, &latest_root_data, &value_data).is_err() {
284        error!(
285            target: "runtime::merkle::merkle_add",
286            "[WASM] [{cid}] merkle_add(): Couldn't insert to db_roots tree"
287        );
288        return darkfi_sdk::error::INTERNAL_ERROR
289    }
290
291    // Write a pointer to the latest known root
292    debug!(
293        target: "runtime::merkle::merkle_add",
294        "[WASM] [{cid}] merkle_add(): Replacing latest Merkle root pointer"
295    );
296
297    if overlay.insert(&db_info.tree, &root_key, &latest_root_data).is_err() {
298        error!(
299            target: "runtime::merkle::merkle_add",
300            "[WASM] [{cid}] merkle_add(): Couldn't insert latest root to db_info tree"
301        );
302        return darkfi_sdk::error::INTERNAL_ERROR
303    }
304
305    // Subtract used gas.
306    // Here we count:
307    // * The size of the Merkle tree we deserialized from the db.
308    // * The size of the Merkle tree we serialized into the db.
309    // * The size of the new Merkle roots we wrote into the db.
310    drop(overlay);
311    drop(lock);
312    drop(db_handles);
313    let spent_gas = coins_len * 32;
314    env.subtract_gas(&mut store, spent_gas as u64);
315
316    wasm::entrypoint::SUCCESS
317}