1use std::io::Cursor;
20
21use darkfi_sdk::{
22 crypto::{pasta_prelude::Field, MerkleNode, MerkleTree},
23 hex::AsHex,
24 pasta::pallas,
25 wasm,
26};
27use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
28use tracing::{debug, error};
29use wasmer::{FunctionEnvMut, WasmPtr};
30
31use crate::runtime::{
32 import::{acl::acl_allow, util::wasm_mem_read},
33 vm_runtime::{ContractSection, Env},
34};
35
36pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
48 merkle_add_internal(ctx, ptr, ptr_len, false)
49}
50
51pub(crate) fn merkle_add_local(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, ptr_len: u32) -> i64 {
63 merkle_add_internal(ctx, ptr, ptr_len, true)
64}
65
66pub(crate) fn merkle_add_internal(
69 mut ctx: FunctionEnvMut<Env>,
70 ptr: WasmPtr<u8>,
71 ptr_len: u32,
72 local: bool,
73) -> i64 {
74 let lt = if local { "merkle_add_local" } else { "merkle_add" };
75 let (env, mut store) = ctx.data_and_store_mut();
76 let cid = env.contract_id;
77
78 if let Err(e) = acl_allow(env, &[ContractSection::Update]) {
80 error!(
81 target: "runtime::merkle::{lt}",
82 "[WASM] [{cid}] {lt}(): Called in unauthorized section: {e}",
83 );
84 return darkfi_sdk::error::CALLER_ACCESS_DENIED
85 }
86
87 env.subtract_gas(&mut store, 34);
89
90 let mut buf_reader = match wasm_mem_read(env, &store, ptr, ptr_len) {
92 Ok(v) => v,
93 Err(e) => {
94 error!(
95 target: "runtime::merkle::{lt}",
96 "[WASM] [{cid}] {lt}(): Failed to read wasm memory: {e}",
97 );
98 return darkfi_sdk::error::INTERNAL_ERROR
99 }
100 };
101
102 let db_info_index: u32 = match Decodable::decode(&mut buf_reader) {
110 Ok(v) => v,
111 Err(e) => {
112 error!(
113 target: "runtime::merkle::{lt}",
114 "[WASM] [{cid}] {lt}(): Failed to decode db_info DbHandle: {e}",
115 );
116 return darkfi_sdk::error::INTERNAL_ERROR
117 }
118 };
119
120 let db_roots_index: u32 = match Decodable::decode(&mut buf_reader) {
121 Ok(v) => v,
122 Err(e) => {
123 error!(
124 target: "runtime::merkle::{lt}",
125 "[WASM] [{cid}] {lt}(): Failed to decode db_roots DbHandle: {e}",
126 );
127 return darkfi_sdk::error::INTERNAL_ERROR
128 }
129 };
130
131 let db_info_index = db_info_index as usize;
133 let db_roots_index = db_roots_index as usize;
134 let db_handles = if local { env.local_db_handles.borrow() } else { env.db_handles.borrow() };
135 let n_dbs = db_handles.len();
136
137 if n_dbs <= db_info_index || n_dbs <= db_roots_index {
138 error!(
139 target: "runtime::merkle::{lt}",
140 "[WASM] [{cid}] {lt}(): Requested DbHandle that is out of bounds",
141 );
142 return darkfi_sdk::error::INTERNAL_ERROR
143 }
144
145 let db_info = &db_handles[db_info_index];
146 let db_roots = &db_handles[db_roots_index];
147
148 if db_info.contract_id != cid || db_roots.contract_id != cid {
150 error!(
151 target: "runtime::merkle::{lt}",
152 "[WASM] [{cid}] {lt}(): Unauthorized write to DbHandle",
153 );
154 return darkfi_sdk::error::CALLER_ACCESS_DENIED
155 }
156
157 let root_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
159 Ok(v) => v,
160 Err(e) => {
161 error!(
162 target: "runtime::merkle::{lt}",
163 "[WASM] [{cid}] {lt}(): Failed to decode root_key Vec: {e}",
164 );
165 return darkfi_sdk::error::INTERNAL_ERROR
166 }
167 };
168
169 let tree_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
171 Ok(v) => v,
172 Err(e) => {
173 error!(
174 target: "runtime::merkle::{lt}",
175 "[WASM] [{cid}] {lt}(): Failed to decode tree_key Vec: {e}",
176 );
177 return darkfi_sdk::error::INTERNAL_ERROR
178 }
179 };
180
181 let coins: Vec<MerkleNode> = match Decodable::decode(&mut buf_reader) {
183 Ok(v) => v,
184 Err(e) => {
185 error!(
186 target: "runtime::merkle::{lt}",
187 "[WASM] [{cid}] {lt}(): Failed to decode Vec<MerkleNode>: {e}",
188 );
189 return darkfi_sdk::error::INTERNAL_ERROR
190 }
191 };
192
193 if buf_reader.position() != ptr_len as u64 {
195 error!(
196 target: "runtime::merkle::{lt}",
197 "[WASM] [{cid}] {lt}(): Trailing bytes in argument stream",
198 );
199 return darkfi_sdk::error::INTERNAL_ERROR
200 }
201
202 let blockchain = env.blockchain.lock().unwrap();
205 let mut overlay = blockchain.overlay.lock().unwrap();
206 let mut tx_local_db = env.tx_local.lock();
207
208 let tree_bytes = if local {
210 let Some(db_cid) = tx_local_db.get_mut(&db_info.contract_id) else {
211 error!(
212 target: "runtime::db::{lt}",
213 "[WASM] [{cid}] {lt}(): Could not find db for {}",
214 db_info.contract_id,
215 );
216 return darkfi_sdk::error::INTERNAL_ERROR
217 };
218
219 let tree = db_cid.entry(db_info.tree).or_default();
221
222 match tree.get(&tree_key) {
223 Some(v) => Some(v).cloned(),
224 None => {
225 let mut merkle_tree = MerkleTree::new(1);
228 merkle_tree.append(MerkleNode::from(pallas::Base::ZERO));
229 let mut merkle_tree_data = vec![];
230 merkle_tree_data.write_u32(0).unwrap();
231 merkle_tree.encode(&mut merkle_tree_data).unwrap();
232 Some(merkle_tree_data)
233 }
234 }
235 } else {
236 match overlay.get(&db_info.tree, &tree_key) {
237 Ok(v) => v.map(|iv| iv.to_vec()),
238 Err(e) => {
239 error!(
240 target: "runtime::merkle::{lt}",
241 "[WASM] [{cid}] {lt}(): Error getting from sled tree: {e}",
242 );
243 return darkfi_sdk::error::INTERNAL_ERROR
244 }
245 }
246 };
247
248 let Some(tree_bytes) = tree_bytes else {
249 error!(
250 target: "runtime::merkle::{lt}",
251 "[WASM] [{cid}] {lt}(): Merkle tree k/v is empty",
252 );
253 return darkfi_sdk::error::INTERNAL_ERROR
254 };
255
256 debug!(target: "runtime::merkle::{lt}", "Serialized tree: {} bytes", tree_bytes.len());
258 debug!(target: "runtime::merkle::{lt}", "{}", tree_bytes.hex());
259
260 let mut decoder = Cursor::new(&tree_bytes);
261 let set_size: u32 = match Decodable::decode(&mut decoder) {
262 Ok(v) => v,
263 Err(e) => {
264 error!(
265 target: "runtime::merkle::{lt}",
266 "[WASM] [{cid}] {lt}(): Unable to decode set size: {e}",
267 );
268 return darkfi_sdk::error::INTERNAL_ERROR
269 }
270 };
271
272 let mut merkle_tree: MerkleTree = match Decodable::decode(&mut decoder) {
273 Ok(v) => v,
274 Err(e) => {
275 error!(
276 target: "runtime::merkle::{lt}",
277 "[WASM] [{cid}] {lt}(): Unable to deserialize Merkle tree: {e}",
278 );
279 return darkfi_sdk::error::INTERNAL_ERROR
280 }
281 };
282
283 let coins_len = coins.len();
285 for coin in coins {
286 merkle_tree.append(coin);
287 }
288
289 let mut merkle_tree_data = vec![];
291 if merkle_tree_data.write_u32(set_size + coins_len as u32).is_err() ||
292 merkle_tree.encode(&mut merkle_tree_data).is_err()
293 {
294 error!(
295 target: "runtime::merkle::{lt}",
296 "[WASM] [{cid}] {lt}(): Could not serialize modified Merkle tree",
297 );
298 return darkfi_sdk::error::INTERNAL_ERROR
299 }
300
301 if local {
303 let db_cid = tx_local_db.get_mut(&db_info.contract_id).unwrap();
306 let tree = db_cid.get_mut(&db_info.tree).unwrap();
307 tree.insert(tree_key, merkle_tree_data);
308 } else if let Err(e) = overlay.insert(&db_info.tree, &tree_key, &merkle_tree_data) {
309 error!(
310 target: "runtime::merkle::{lt}",
311 "[WASM] [{cid}] {lt}(): Could not insert tree to db_info: {e}",
312 );
313 return darkfi_sdk::error::INTERNAL_ERROR
314 }
315
316 let Some(latest_root) = merkle_tree.root(0) else {
320 error!(
321 target: "runtime::merkle::{lt}",
322 "[WASM] [{cid}] {lt}(): Unable to read Merkle tree root",
323 );
324 return darkfi_sdk::error::INTERNAL_ERROR
325 };
326
327 debug!(
328 target: "runtime::merkle::{lt}",
329 "[WASM] [{cid}] {lt}(): Appending Merkle root to db: {latest_root:?}",
330 );
331
332 let latest_root_data = serialize(&latest_root);
333 assert_eq!(latest_root_data.len(), 32);
334
335 let mut value_data = Vec::with_capacity(32 + 1);
336 env.tx_hash.inner().encode(&mut value_data).expect("Unable to serialize tx_hash");
337 env.call_idx.encode(&mut value_data).expect("Unable to serialize call_idx");
338 assert_eq!(value_data.len(), 32 + 1);
339
340 if local {
341 let db_cid = tx_local_db.get_mut(&db_info.contract_id).unwrap();
344
345 let info_tree = db_cid.get_mut(&db_info.tree).unwrap();
346 info_tree.insert(root_key, latest_root_data.clone());
347
348 let roots_tree = db_cid.entry(db_roots.tree).or_default();
349 roots_tree.insert(latest_root_data, value_data);
350 } else {
351 if let Err(e) = overlay.insert(&db_roots.tree, &latest_root_data, &value_data) {
352 error!(
353 target: "runtime::merkle::{lt}",
354 "[WASM] [{cid}] {lt}(): Could not insert to db_roots tree: {e}",
355 );
356 return darkfi_sdk::error::INTERNAL_ERROR
357 }
358
359 if let Err(e) = overlay.insert(&db_info.tree, &root_key, &latest_root_data) {
360 error!(
361 target: "runtime::merkle::{lt}",
362 "[WASM] [{cid}] {lt}(): Could not insert latest root to db_info: {e}",
363 );
364 return darkfi_sdk::error::INTERNAL_ERROR
365 }
366 }
367
368 drop(tx_local_db);
370 drop(overlay);
371 drop(blockchain);
372 drop(db_handles);
373 let spent_gas = coins_len * 32;
374 env.subtract_gas(&mut store, spent_gas as u64);
375
376 wasm::entrypoint::SUCCESS
377}