darkfi_sdk/wasm/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 darkfi_serial::Encodable;
20
21use crate::{
22 crypto::MerkleNode,
23 error::{ContractError, GenericResult},
24 pasta::pallas,
25 wasm::db::DbHandle,
26};
27
28/// Add given elements into an on-chain Merkle tree. Used for inclusion proofs.
29///
30/// * `db_info` is a handle for a database where the Merkle tree is stored.
31/// * `db_roots` is a handle for a database where all the new Merkle roots are stored.
32/// * `root_key` is the serialized key pointing to the latest Merkle root in `db_info`
33/// * `tree_key` is the serialized key pointing to the Merkle tree in `db_info`.
34/// * `elements` are the items we want to add to the Merkle tree.
35///
36/// There are 2 databases:
37///
38/// * `db_info` stores general metadata or info.
39/// * `db_roots` stores a log of all the merkle roots.
40///
41/// Inside `db_info` we store:
42///
43/// * The \[latest root hash:32\] under `root_key`.
44/// * The incremental merkle tree under `tree_key`.
45///
46/// Inside `db_roots` we store:
47///
48/// * All \[merkle root:32\]s as keys. The value is the current \[tx_hash:32\]\[call_idx:1\].
49/// If no new values are added, then the root key is updated to the current (tx_hash, call_idx).
50pub fn merkle_add(
51 db_info: DbHandle,
52 db_roots: DbHandle,
53 root_key: &[u8],
54 tree_key: &[u8],
55 elements: &[MerkleNode],
56) -> GenericResult<()> {
57 merkle_add_internal(db_info, db_roots, root_key, tree_key, elements, false)
58}
59
60/// Add given elements into a tx-local Merkle tree. Used for inclusion proofs.
61///
62/// * `db_info` is a handle for a database where the Merkle tree is stored.
63/// * `db_roots` is a handle for a database where all the new Merkle roots are stored.
64/// * `root_key` is the serialized key pointing to the latest Merkle root in `db_info`
65/// * `tree_key` is the serialized key pointing to the Merkle tree in `db_info`.
66/// * `elements` are the items we want to add to the Merkle tree.
67///
68/// There are 2 databases:
69///
70/// * `db_info` stores general metadata or info.
71/// * `db_roots` stores a log of all the merkle roots.
72///
73/// Inside `db_info` we store:
74///
75/// * The \[latest root hash:32\] under `root_key`.
76/// * The incremental merkle tree under `tree_key`.
77///
78/// Inside `db_roots` we store:
79///
80/// * All \[merkle root:32\]s as keys. The value is the current \[tx_hash:32\]\[call_idx:1\].
81/// If no new values are added, then the root key is updated to the current (tx_hash, call_idx).
82pub fn merkle_add_local(
83 db_info: DbHandle,
84 db_roots: DbHandle,
85 root_key: &[u8],
86 tree_key: &[u8],
87 elements: &[MerkleNode],
88) -> GenericResult<()> {
89 merkle_add_internal(db_info, db_roots, root_key, tree_key, elements, true)
90}
91
92/// Internal function for `merkle_add` which branches to either on-chain or
93/// transaction-local.
94pub fn merkle_add_internal(
95 db_info: DbHandle,
96 db_roots: DbHandle,
97 root_key: &[u8],
98 tree_key: &[u8],
99 elements: &[MerkleNode],
100 local: bool,
101) -> GenericResult<()> {
102 let mut buf = vec![];
103 let mut len = 0;
104 len += db_info.encode(&mut buf)?;
105 len += db_roots.encode(&mut buf)?;
106 len += root_key.encode(&mut buf)?;
107 len += tree_key.encode(&mut buf)?;
108 len += elements.encode(&mut buf)?;
109
110 let ret = unsafe {
111 if local {
112 merkle_add_local_(buf.as_ptr(), len as u32)
113 } else {
114 merkle_add_(buf.as_ptr(), len as u32)
115 }
116 };
117
118 if ret < 0 {
119 return Err(ContractError::from(ret))
120 }
121
122 match ret {
123 0 => Ok(()),
124 _ => unreachable!(),
125 }
126}
127
128/// Add given elements into a sparse Merkle tree. Used for exclusion proofs.
129///
130/// * `db_info` is a handle for a database where the latest root is stored.
131/// * `db_smt` is a handle for a database where all the actual tree is stored.
132/// * `db_roots` is a handle for a database where all the new roots are stored.
133/// * `root_key` is the serialized key pointing to the latest Merkle root in `db_info`
134/// * `elements` are the items we want to add to the tree.
135///
136/// There are 2 databases:
137///
138/// * `db_info` stores general metadata or info.
139/// * `db_roots` stores a log of all the merkle roots.
140///
141/// Inside `db_info` we store:
142///
143/// * The \[latest root hash:32\] under `root_key`.
144///
145/// Inside `db_roots` we store:
146///
147/// * All \[merkle root:32\]s as keys. The value is the current \[tx_hash:32\]\[call_idx:1\].
148/// If no new values are added, then the root key is updated to the current (tx_hash, call_idx).
149pub fn sparse_merkle_insert_batch(
150 db_info: DbHandle,
151 db_smt: DbHandle,
152 db_roots: DbHandle,
153 root_key: &[u8],
154 elements: &[pallas::Base],
155) -> GenericResult<()> {
156 let mut buf = vec![];
157 let mut len = 0;
158 len += db_info.encode(&mut buf)?;
159 len += db_smt.encode(&mut buf)?;
160 len += db_roots.encode(&mut buf)?;
161 len += root_key.encode(&mut buf)?;
162 len += elements.encode(&mut buf)?;
163
164 match unsafe { sparse_merkle_insert_batch_(buf.as_ptr(), len as u32) } {
165 0 => Ok(()),
166 -1 => Err(ContractError::CallerAccessDenied),
167 -2 => Err(ContractError::DbSetFailed),
168 _ => unreachable!(),
169 }
170}
171
172extern "C" {
173 fn merkle_add_(ptr: *const u8, len: u32) -> i64;
174 fn merkle_add_local_(ptr: *const u8, len: u32) -> i64;
175 fn sparse_merkle_insert_batch_(ptr: *const u8, len: u32) -> i64;
176}