Просмотр исходного кода

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.

zero 2 лет назад
Родитель
Сommit
2919a595f1

+ 9 - 3
src/contract/money/src/entrypoint.rs

@@ -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() {

+ 11 - 2
src/contract/money/src/entrypoint/fee_v1.rs

@@ -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), &[])?;
 

+ 7 - 1
src/contract/money/src/entrypoint/transfer_v1.rs

@@ -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<_>>(),
     )?;
 

+ 4 - 2
src/contract/money/src/lib.rs

@@ -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

+ 16 - 3
src/runtime/import/merkle.rs

@@ -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,

+ 94 - 4
src/runtime/import/smt.rs

@@ -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

+ 6 - 0
src/sdk/src/merkle.rs

@@ -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) } {