use std::io::Cursor;
use darkfi_sdk::{
crypto::{MerkleNode, MerkleTree},
hex::AsHex,
wasm,
};
use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
use log::{debug, error};
use wasmer::{FunctionEnvMut, WasmPtr};
use super::acl::acl_allow;
use crate::runtime::vm_runtime::{ContractSection, Env};
pub(crate) fn merkle_add(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
let (env, mut store) = ctx.data_and_store_mut();
let cid = env.contract_id;
if let Err(e) = acl_allow(env, &[ContractSection::Update]) {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Called in unauthorized section: {}", cid, e,
);
return darkfi_sdk::error::CALLER_ACCESS_DENIED
}
env.subtract_gas(&mut store, 1);
env.subtract_gas(&mut store, 33 );
let memory_view = env.memory_view(&store);
let Ok(mem_slice) = ptr.slice(&memory_view, len) else {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to make slice from ptr", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
};
let mut buf = vec![0_u8; len as usize];
if let Err(e) = mem_slice.read_slice(&mut buf) {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to read from memory slice: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
};
let mut buf_reader = Cursor::new(buf);
let db_info_index: u32 = match Decodable::decode(&mut buf_reader) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to decode db_info DbHandle: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let db_info_index = db_info_index as usize;
let db_roots_index: u32 = match Decodable::decode(&mut buf_reader) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to decode db_roots DbHandle: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let db_roots_index = db_roots_index as usize;
let db_handles = env.db_handles.borrow();
let n_dbs = db_handles.len();
if n_dbs <= db_info_index || n_dbs <= db_roots_index {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Requested DbHandle that is out of bounds", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
let db_info = &db_handles[db_info_index];
let db_roots = &db_handles[db_roots_index];
if db_info.contract_id != env.contract_id || db_roots.contract_id != env.contract_id {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Unauthorized to write to DbHandle", cid,
);
return darkfi_sdk::error::CALLER_ACCESS_DENIED
}
let root_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to decode key vec: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let tree_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to decode key vec: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let coins: Vec<MerkleNode> = match Decodable::decode(&mut buf_reader) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Failed to decode MerkleNode: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
if buf_reader.position() != (len as u64) {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Mismatch between given length, and cursor length", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
let lock = env.blockchain.lock().unwrap();
let mut overlay = lock.overlay.lock().unwrap();
let ret = match overlay.get(&db_info.tree, &tree_key) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Internal error getting from tree: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let Some(return_data) = ret else {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Return data is empty", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
};
debug!(
target: "runtime::merkle::merkle_add",
"Serialized tree: {} bytes",
return_data.len()
);
debug!(
target: "runtime::merkle::merkle_add",
" {}",
return_data.hex()
);
let mut decoder = Cursor::new(&return_data);
let set_size: u32 = match Decodable::decode(&mut decoder) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Unable to read set size: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let mut tree: MerkleTree = match Decodable::decode(&mut decoder) {
Ok(v) => v,
Err(e) => {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Unable to deserialize Merkle tree: {}", cid, e,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
};
let coins_len = coins.len();
for coin in coins {
tree.append(coin);
}
let mut tree_data = Vec::new();
if tree_data.write_u32(set_size + coins_len as u32).is_err() ||
tree.encode(&mut tree_data).is_err()
{
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Couldn't reserialize modified tree", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
if overlay.insert(&db_info.tree, &tree_key, &tree_data).is_err() {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Couldn't insert to db_info tree", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
let Some(latest_root) = tree.root(0) else {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Unable to read the root of tree", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
};
debug!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Appending Merkle root to db: {:?}", cid, latest_root,
);
let latest_root_data = serialize(&latest_root);
assert_eq!(latest_root_data.len(), 32);
let mut value_data = Vec::with_capacity(32 + 1);
env.tx_hash.inner().encode(&mut value_data).expect("Unable to serialize tx_hash");
env.call_idx.encode(&mut value_data).expect("Unable to serialize call_idx");
assert_eq!(value_data.len(), 32 + 1);
if overlay.insert(&db_roots.tree, &latest_root_data, &value_data).is_err() {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Couldn't insert to db_roots tree", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
debug!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Replacing latest Merkle root pointer", cid,
);
if overlay.insert(&db_info.tree, &root_key, &latest_root_data).is_err() {
error!(
target: "runtime::merkle::merkle_add",
"[WASM] [{}] merkle_add(): Couldn't insert latest root to db_info tree", cid,
);
return darkfi_sdk::error::INTERNAL_ERROR
}
drop(overlay);
drop(lock);
drop(db_handles);
let spent_gas = coins_len * 32;
env.subtract_gas(&mut store, spent_gas as u64);
wasm::entrypoint::SUCCESS
}