mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
runtime/merkle & smt: implements DB_roots format documented in the book arch/dao page. We store all merkle roots together with information about exactly when that root occurred. To store when the root occurred, we use an absolute location of (block_height, tx_idx, call_idx). Right now tx_idx and call_idx are hardcoded to 0 since the env doesn't yet have access to this info.
This commit is contained in:
@@ -35,8 +35,8 @@ use crate::{
|
||||
},
|
||||
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_DB_VERSION, MONEY_CONTRACT_INFO_TREE,
|
||||
MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_TOKEN_FREEZE_TREE,
|
||||
MONEY_CONTRACT_TOTAL_FEES_PAID,
|
||||
MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE,
|
||||
MONEY_CONTRACT_TOKEN_FREEZE_TREE, MONEY_CONTRACT_TOTAL_FEES_PAID,
|
||||
};
|
||||
|
||||
/// `Money::Fee` functions
|
||||
@@ -123,12 +123,18 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
|
||||
zkas_db_set(&token_mint_v1_bincode[..])?;
|
||||
zkas_db_set(&token_frz_v1_bincode[..])?;
|
||||
|
||||
// Set up a database tree to hold Merkle roots of all coins
|
||||
// Set up a database tree to hold Merkle roots of all coin trees
|
||||
// k=MerkleNode, v=[]
|
||||
if db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE).is_err() {
|
||||
db_init(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
}
|
||||
|
||||
// Set up a database tree to hold Merkle roots of all nullifier trees
|
||||
// k=MerkleNode, v=[]
|
||||
if db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE).is_err() {
|
||||
db_init(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
}
|
||||
|
||||
// Set up a database tree to hold all coins ever seen
|
||||
// k=Coin, v=[]
|
||||
if db_lookup(cid, MONEY_CONTRACT_COINS_TREE).is_err() {
|
||||
|
||||
@@ -41,7 +41,9 @@ use crate::{
|
||||
model::{MoneyFeeParamsV1, MoneyFeeUpdateV1, DARK_TOKEN_ID},
|
||||
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
|
||||
MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_TOTAL_FEES_PAID, MONEY_CONTRACT_ZKAS_FEE_NS_V1,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
|
||||
MONEY_CONTRACT_NULLIFIER_ROOTS_TREE, MONEY_CONTRACT_TOTAL_FEES_PAID,
|
||||
MONEY_CONTRACT_ZKAS_FEE_NS_V1,
|
||||
};
|
||||
|
||||
/// `get_metadata` function for `Money::FeeV1`
|
||||
@@ -205,10 +207,17 @@ pub(crate) fn money_fee_process_update_v1(
|
||||
let coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
|
||||
let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let nullifier_roots_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
|
||||
db_set(info_db, MONEY_CONTRACT_TOTAL_FEES_PAID, &serialize(&update.fee))?;
|
||||
|
||||
sparse_merkle_insert_batch(nullifiers_db, &[update.nullifier.inner()])?;
|
||||
sparse_merkle_insert_batch(
|
||||
info_db,
|
||||
nullifiers_db,
|
||||
nullifier_roots_db,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT,
|
||||
&[update.nullifier.inner()],
|
||||
)?;
|
||||
|
||||
db_set(coins_db, &serialize(&update.coin), &[])?;
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ use crate::{
|
||||
model::{MoneyTransferParamsV1, MoneyTransferUpdateV1},
|
||||
MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
|
||||
MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
|
||||
MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
|
||||
MONEY_CONTRACT_NULLIFIER_ROOTS_TREE, MONEY_CONTRACT_ZKAS_BURN_NS_V1,
|
||||
MONEY_CONTRACT_ZKAS_MINT_NS_V1,
|
||||
};
|
||||
|
||||
/// `get_metadata` function for `Money::TransferV1`
|
||||
@@ -234,10 +236,14 @@ pub(crate) fn money_transfer_process_update_v1(
|
||||
let coins_db = db_lookup(cid, MONEY_CONTRACT_COINS_TREE)?;
|
||||
let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
||||
let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
||||
let nullifier_roots_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
|
||||
|
||||
msg!("[TransferV1] Adding new nullifiers to the set");
|
||||
sparse_merkle_insert_batch(
|
||||
info_db,
|
||||
nullifiers_db,
|
||||
nullifier_roots_db,
|
||||
MONEY_CONTRACT_LATEST_NULLIFIER_ROOT,
|
||||
&update.nullifiers.iter().map(|n| n.inner()).collect::<Vec<_>>(),
|
||||
)?;
|
||||
|
||||
|
||||
@@ -73,12 +73,14 @@ pub const MONEY_CONTRACT_INFO_TREE: &str = "info";
|
||||
pub const MONEY_CONTRACT_COINS_TREE: &str = "coins";
|
||||
pub const MONEY_CONTRACT_COIN_ROOTS_TREE: &str = "coin_roots";
|
||||
pub const MONEY_CONTRACT_NULLIFIERS_TREE: &str = "nullifiers";
|
||||
pub const MONEY_CONTRACT_NULLIFIER_ROOTS_TREE: &str = "nullifier_roots";
|
||||
pub const MONEY_CONTRACT_TOKEN_FREEZE_TREE: &str = "token_freezes";
|
||||
|
||||
// These are keys inside the info tree
|
||||
pub const MONEY_CONTRACT_DB_VERSION: &[u8] = b"db_version";
|
||||
pub const MONEY_CONTRACT_COIN_MERKLE_TREE: &[u8] = b"coin_tree";
|
||||
pub const MONEY_CONTRACT_LATEST_COIN_ROOT: &[u8] = b"last_root";
|
||||
pub const MONEY_CONTRACT_COIN_MERKLE_TREE: &[u8] = b"coins_tree";
|
||||
pub const MONEY_CONTRACT_LATEST_COIN_ROOT: &[u8] = b"last_coins_root";
|
||||
pub const MONEY_CONTRACT_LATEST_NULLIFIER_ROOT: &[u8] = b"last_nullifiers_root";
|
||||
pub const MONEY_CONTRACT_TOTAL_FEES_PAID: &[u8] = b"total_fees_paid";
|
||||
|
||||
/// zkas fee circuit namespace
|
||||
|
||||
@@ -279,10 +279,23 @@ pub(crate) fn merkle_add(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u3
|
||||
);
|
||||
let latest_root_data = serialize(latest_root);
|
||||
assert_eq!(latest_root_data.len(), 32);
|
||||
let blockheight_data = serialize(&env.verifying_block_height);
|
||||
assert_eq!(blockheight_data.len(), 8);
|
||||
|
||||
if overlay.insert(&db_roots.tree, &latest_root_data, &blockheight_data).is_err() {
|
||||
let blockheight_data = serialize(&(env.verifying_block_height as u32));
|
||||
// This is hardcoded but should not be
|
||||
let tx_idx: u16 = 0;
|
||||
let call_idx: u16 = 0;
|
||||
|
||||
assert_eq!(blockheight_data.len(), 4);
|
||||
// Little-endian
|
||||
assert_eq!(blockheight_data[3], 0);
|
||||
|
||||
let mut value_data = Vec::with_capacity(7);
|
||||
value_data.write_slice(&blockheight_data[..3]).expect("Unable to serialize blockheight data");
|
||||
tx_idx.encode(&mut value_data).expect("Unable to serialize tx_id");
|
||||
call_idx.encode(&mut value_data).expect("Unable to serialize call_idx");
|
||||
assert_eq!(value_data.len(), 7);
|
||||
|
||||
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,
|
||||
|
||||
@@ -22,9 +22,9 @@ use darkfi_sdk::crypto::{
|
||||
pasta_prelude::*,
|
||||
smt::{PoseidonFp, SparseMerkleTree, StorageAdapter, EMPTY_NODES_FP, SMT_FP_DEPTH},
|
||||
};
|
||||
use darkfi_serial::Decodable;
|
||||
use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
|
||||
use halo2_proofs::pasta::pallas;
|
||||
use log::{error, warn};
|
||||
use log::{debug, error, warn};
|
||||
use num_bigint::BigUint;
|
||||
use wasmer::{FunctionEnvMut, WasmPtr};
|
||||
|
||||
@@ -112,8 +112,21 @@ pub(crate) fn sparse_merkle_insert_batch(
|
||||
|
||||
// The buffer should deserialize into:
|
||||
// - db_smt
|
||||
// - db_roots
|
||||
// - nullifiers (as Vec<pallas::Base>)
|
||||
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::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): 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_smt_index: u32 = match Decodable::decode(&mut buf_reader) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
@@ -126,20 +139,37 @@ pub(crate) fn sparse_merkle_insert_batch(
|
||||
};
|
||||
let db_smt_index = db_smt_index as usize;
|
||||
|
||||
let db_roots_index: u32 = match Decodable::decode(&mut buf_reader) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): 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_smt_index {
|
||||
if n_dbs <= db_info_index || n_dbs <= db_smt_index || n_dbs <= db_roots_index {
|
||||
error!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Requested DbHandle that is out of bounds", cid,
|
||||
);
|
||||
return darkfi_sdk::error::INTERNAL_ERROR
|
||||
}
|
||||
let db_info = &db_handles[db_info_index];
|
||||
let db_smt = &db_handles[db_smt_index];
|
||||
let db_roots = &db_handles[db_roots_index];
|
||||
|
||||
// Make sure that the contract owns the dbs it wants to write to
|
||||
if db_smt.contract_id != env.contract_id {
|
||||
if db_info.contract_id != env.contract_id ||
|
||||
db_smt.contract_id != env.contract_id ||
|
||||
db_roots.contract_id != env.contract_id
|
||||
{
|
||||
error!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Unauthorized to write to DbHandle", cid,
|
||||
@@ -147,6 +177,18 @@ pub(crate) fn sparse_merkle_insert_batch(
|
||||
return darkfi_sdk::error::CALLER_ACCESS_DENIED
|
||||
}
|
||||
|
||||
// This `key` represents the sled key in info where the latest root is
|
||||
let root_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Failed to decode key vec: {}", cid, e,
|
||||
);
|
||||
return darkfi_sdk::error::INTERNAL_ERROR
|
||||
}
|
||||
};
|
||||
|
||||
// This `nullifier` represents the leaf we're adding to the Merkle tree
|
||||
let nullifiers: Vec<pallas::Base> = match Decodable::decode(&mut buf_reader) {
|
||||
Ok(v) => v,
|
||||
@@ -201,6 +243,54 @@ pub(crate) fn sparse_merkle_insert_batch(
|
||||
return darkfi_sdk::error::INTERNAL_ERROR
|
||||
};
|
||||
|
||||
// Here we add the SMT root to our set of roots
|
||||
// Since each update to the tree is atomic, we only need to add the last root.
|
||||
let latest_root = smt.root();
|
||||
|
||||
debug!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Appending SMT root to db: {:?}", cid, latest_root,
|
||||
);
|
||||
let latest_root_data = serialize(&latest_root);
|
||||
assert_eq!(latest_root_data.len(), 32);
|
||||
|
||||
let blockheight_data = serialize(&(env.verifying_block_height as u32));
|
||||
// This is hardcoded but should not be
|
||||
let tx_idx: u16 = 0;
|
||||
let call_idx: u16 = 0;
|
||||
|
||||
assert_eq!(blockheight_data.len(), 4);
|
||||
// Little-endian
|
||||
assert_eq!(blockheight_data[3], 0);
|
||||
|
||||
let mut value_data = Vec::with_capacity(7);
|
||||
value_data.write_slice(&blockheight_data[..3]).expect("Unable to serialize blockheight data");
|
||||
tx_idx.encode(&mut value_data).expect("Unable to serialize tx_id");
|
||||
call_idx.encode(&mut value_data).expect("Unable to serialize call_idx");
|
||||
assert_eq!(value_data.len(), 7);
|
||||
|
||||
if overlay.insert(&db_roots.tree, &latest_root_data, &value_data).is_err() {
|
||||
error!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Couldn't insert to db_roots tree", cid,
|
||||
);
|
||||
return darkfi_sdk::error::INTERNAL_ERROR
|
||||
}
|
||||
|
||||
// Write a pointer to the latest known root
|
||||
debug!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Replacing latest SMT root pointer", cid,
|
||||
);
|
||||
|
||||
if overlay.insert(&db_info.tree, &root_key, &latest_root_data).is_err() {
|
||||
error!(
|
||||
target: "runtime::smt::sparse_merkle_insert_batch",
|
||||
"[WASM] [{}] sparse_merkle_insert_batch(): Couldn't insert latest root to db_info tree", cid,
|
||||
);
|
||||
return darkfi_sdk::error::INTERNAL_ERROR
|
||||
}
|
||||
|
||||
// Subtract used gas.
|
||||
// Here we count:
|
||||
// * The number of nullifiers we inserted into the DB
|
||||
|
||||
@@ -71,12 +71,18 @@ pub fn merkle_add(
|
||||
}
|
||||
|
||||
pub fn sparse_merkle_insert_batch(
|
||||
db_info: DbHandle,
|
||||
db_smt: DbHandle,
|
||||
db_roots: DbHandle,
|
||||
root_key: &[u8],
|
||||
elements: &[pallas::Base],
|
||||
) -> GenericResult<()> {
|
||||
let mut buf = vec![];
|
||||
let mut len = 0;
|
||||
len += db_info.encode(&mut buf)?;
|
||||
len += db_smt.encode(&mut buf)?;
|
||||
len += db_roots.encode(&mut buf)?;
|
||||
len += root_key.to_vec().encode(&mut buf)?;
|
||||
len += elements.to_vec().encode(&mut buf)?;
|
||||
|
||||
match unsafe { sparse_merkle_insert_batch_(buf.as_ptr(), len as u32) } {
|
||||
|
||||
Reference in New Issue
Block a user