Преглед изворни кода

runtime/merkle: Introduce pointers to latest merkle roots in merkle_add()

Also migrate all the contracts that use merkle_add to this API.

This is the better approach, since it avoids having to run redundant
operations like lots of hashing when we want to retrieve the latest
Merkle root in some tree. It is too expensive to do it directly in WASM,
so doing this through the injected host function allows us to price it
more accordingly.
parazyd пре 3 година
родитељ
комит
7ec7d9244f

+ 4 - 2
src/contract/consensus/src/entrypoint/proposal_v1.rs

@@ -18,8 +18,9 @@
 
 use darkfi_money_contract::{
     error::MoneyError, CONSENSUS_CONTRACT_INFO_TREE, CONSENSUS_CONTRACT_NULLIFIERS_TREE,
-    CONSENSUS_CONTRACT_STAKED_COINS_TREE, CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE,
-    CONSENSUS_CONTRACT_STAKED_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_ZKAS_PROPOSAL_NS_V1,
+    CONSENSUS_CONTRACT_STAKED_COINS_TREE, CONSENSUS_CONTRACT_STAKED_COIN_LATEST_COIN_ROOT,
+    CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE, CONSENSUS_CONTRACT_STAKED_COIN_ROOTS_TREE,
+    CONSENSUS_CONTRACT_ZKAS_PROPOSAL_NS_V1,
 };
 use darkfi_sdk::{
     crypto::{pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, MerkleNode},
@@ -239,6 +240,7 @@ pub(crate) fn consensus_proposal_process_update_v1(
     merkle_add(
         info_db,
         staked_coin_roots_db,
+        &serialize(&CONSENSUS_CONTRACT_STAKED_COIN_LATEST_COIN_ROOT),
         &serialize(&CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE),
         &coins,
     )?;

+ 5 - 3
src/contract/consensus/src/entrypoint/stake_v1.rs

@@ -20,9 +20,10 @@ use darkfi_money_contract::{
     error::MoneyError,
     model::{ConsensusStakeParamsV1, ConsensusStakeUpdateV1, MoneyStakeParamsV1},
     MoneyFunction, CONSENSUS_CONTRACT_INFO_TREE, CONSENSUS_CONTRACT_STAKED_COINS_TREE,
-    CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE, CONSENSUS_CONTRACT_STAKED_COIN_ROOTS_TREE,
-    CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
-    MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_NULLIFIERS_TREE,
+    CONSENSUS_CONTRACT_STAKED_COIN_LATEST_COIN_ROOT, CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE,
+    CONSENSUS_CONTRACT_STAKED_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE,
+    CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1, MONEY_CONTRACT_COIN_ROOTS_TREE,
+    MONEY_CONTRACT_NULLIFIERS_TREE,
 };
 use darkfi_sdk::{
     crypto::{pasta_prelude::*, ContractId, MerkleNode, PublicKey, MONEY_CONTRACT_ID},
@@ -190,6 +191,7 @@ pub(crate) fn consensus_stake_process_update_v1(
     merkle_add(
         info_db,
         staked_coin_roots_db,
+        &serialize(&CONSENSUS_CONTRACT_STAKED_COIN_LATEST_COIN_ROOT),
         &serialize(&CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE),
         &coins,
     )?;

+ 4 - 3
src/contract/consensus/src/entrypoint/unstake_request_v1.rs

@@ -19,9 +19,9 @@
 use darkfi_money_contract::{
     error::MoneyError, model::ConsensusUnstakeReqParamsV1, CONSENSUS_CONTRACT_INFO_TREE,
     CONSENSUS_CONTRACT_NULLIFIERS_TREE, CONSENSUS_CONTRACT_STAKED_COIN_ROOTS_TREE,
-    CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COIN_MERKLE_TREE,
-    CONSENSUS_CONTRACT_UNSTAKED_COIN_ROOTS_TREE, CONSENSUS_CONTRACT_ZKAS_BURN_NS_V1,
-    CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
+    CONSENSUS_CONTRACT_UNSTAKED_COINS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COIN_LATEST_COIN_ROOT,
+    CONSENSUS_CONTRACT_UNSTAKED_COIN_MERKLE_TREE, CONSENSUS_CONTRACT_UNSTAKED_COIN_ROOTS_TREE,
+    CONSENSUS_CONTRACT_ZKAS_BURN_NS_V1, CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
     crypto::{pasta_prelude::*, ContractId, MerkleNode},
@@ -183,6 +183,7 @@ pub(crate) fn consensus_unstake_request_process_update_v1(
     merkle_add(
         info_db,
         unstaked_coin_roots_db,
+        &serialize(&CONSENSUS_CONTRACT_UNSTAKED_COIN_LATEST_COIN_ROOT),
         &serialize(&CONSENSUS_CONTRACT_UNSTAKED_COIN_MERKLE_TREE),
         &coins,
     )?;

+ 1 - 0
src/contract/dao/src/client/mod.rs

@@ -71,6 +71,7 @@ pub const DAO_PROPOSALS_COL_AMOUNT: &str = "amount";
 pub const DAO_PROPOSALS_COL_SENDCOIN_TOKEN_ID: &str = "sendcoin_token_id";
 pub const DAO_PROPOSALS_COL_BULLA_BLIND: &str = "bulla_blind";
 pub const DAO_PROPOSALS_COL_LEAF_POSITION: &str = "leaf_position";
+pub const DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE: &str = "money_snapshot_tree";
 pub const DAO_PROPOSALS_COL_TX_HASH: &str = "tx_hash";
 pub const DAO_PROPOSALS_COL_CALL_INDEX: &str = "call_index";
 pub const DAO_PROPOSALS_COL_OUR_VOTE_ID: &str = "our_vote_id";

+ 9 - 2
src/contract/dao/src/entrypoint/mint.rs

@@ -30,7 +30,8 @@ use crate::{
     error::DaoError,
     model::{DaoMintParams, DaoMintUpdate},
     DaoFunction, DAO_CONTRACT_DB_DAO_BULLAS, DAO_CONTRACT_DB_DAO_MERKLE_ROOTS,
-    DAO_CONTRACT_DB_INFO_TREE, DAO_CONTRACT_KEY_DAO_MERKLE_TREE, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
+    DAO_CONTRACT_DB_INFO_TREE, DAO_CONTRACT_KEY_DAO_MERKLE_TREE, DAO_CONTRACT_KEY_LATEST_DAO_ROOT,
+    DAO_CONTRACT_ZKAS_DAO_MINT_NS,
 };
 
 /// `get_metadata` function for `Dao::Mint`
@@ -98,7 +99,13 @@ pub(crate) fn dao_mint_process_update(cid: ContractId, update: DaoMintUpdate) ->
     db_set(bulla_db, &serialize(&update.dao_bulla), &[])?;
 
     let dao = vec![MerkleNode::from(update.dao_bulla.inner())];
-    merkle_add(info_db, roots_db, &serialize(&DAO_CONTRACT_KEY_DAO_MERKLE_TREE), &dao)?;
+    merkle_add(
+        info_db,
+        roots_db,
+        &serialize(&DAO_CONTRACT_KEY_LATEST_DAO_ROOT),
+        &serialize(&DAO_CONTRACT_KEY_DAO_MERKLE_TREE),
+        &dao,
+    )?;
 
     Ok(())
 }

+ 1 - 0
src/contract/dao/src/lib.rs

@@ -73,6 +73,7 @@ pub const DAO_CONTRACT_DB_VOTE_NULLIFIERS: &str = "dao_vote_nullifiers";
 // These are keys inside the info tree
 pub const DAO_CONTRACT_KEY_DB_VERSION: &str = "db_version";
 pub const DAO_CONTRACT_KEY_DAO_MERKLE_TREE: &str = "dao_merkle_tree";
+pub const DAO_CONTRACT_KEY_LATEST_DAO_ROOT: &str = "dao_last_root";
 
 /// zkas dao mint circuit namespace
 pub const DAO_CONTRACT_ZKAS_DAO_MINT_NS: &str = "DaoMint";

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

@@ -33,8 +33,8 @@ use crate::{
     error::MoneyError,
     model::{MoneyTokenMintParamsV1, MoneyTokenMintUpdateV1},
     MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
-    MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_TOKEN_FREEZE_TREE,
-    MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
+    MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
+    MONEY_CONTRACT_TOKEN_FREEZE_TREE, MONEY_CONTRACT_ZKAS_TOKEN_MINT_NS_V1,
 };
 
 /// `get_metadata` function for `Money::TokenMintV1`
@@ -157,7 +157,13 @@ pub(crate) fn money_token_mint_process_update_v1(
 
     msg!("[MintV1] Adding new coin to the Merkle tree");
     let coins = vec![MerkleNode::from(update.coin.inner())];
-    merkle_add(info_db, coin_roots_db, &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE), &coins)?;
+    merkle_add(
+        info_db,
+        coin_roots_db,
+        &serialize(&MONEY_CONTRACT_LATEST_COIN_ROOT),
+        &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE),
+        &coins,
+    )?;
 
     Ok(())
 }

+ 9 - 2
src/contract/money/src/entrypoint/transfer_v1.rs

@@ -34,7 +34,8 @@ use crate::{
     model::{MoneyTransferParamsV1, MoneyTransferUpdateV1},
     MoneyFunction, MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE,
     MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_FAUCET_PUBKEYS, MONEY_CONTRACT_INFO_TREE,
-    MONEY_CONTRACT_NULLIFIERS_TREE, MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
+    MONEY_CONTRACT_LATEST_COIN_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
+    MONEY_CONTRACT_ZKAS_BURN_NS_V1, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 
 /// `get_metadata` function for `Money::TransferV1`
@@ -285,7 +286,13 @@ pub(crate) fn money_transfer_process_update_v1(
 
     msg!("[TransferV1] Adding new coins to the Merkle tree");
     let coins: Vec<_> = update.coins.iter().map(|x| MerkleNode::from(x.inner())).collect();
-    merkle_add(info_db, coin_roots_db, &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE), &coins)?;
+    merkle_add(
+        info_db,
+        coin_roots_db,
+        &serialize(&MONEY_CONTRACT_LATEST_COIN_ROOT),
+        &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE),
+        &coins,
+    )?;
 
     Ok(())
 }

+ 8 - 2
src/contract/money/src/entrypoint/unstake_v1.rs

@@ -34,7 +34,7 @@ use crate::{
     model::{ConsensusUnstakeParamsV1, MoneyUnstakeParamsV1, MoneyUnstakeUpdateV1},
     MoneyFunction, CONSENSUS_CONTRACT_NULLIFIERS_TREE, CONSENSUS_CONTRACT_UNSTAKED_COIN_ROOTS_TREE,
     MONEY_CONTRACT_COINS_TREE, MONEY_CONTRACT_COIN_MERKLE_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE,
-    MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
+    MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 
 /// `get_metadata` function for `Money::UnstakeV1`
@@ -187,7 +187,13 @@ pub(crate) fn money_unstake_process_update_v1(
 
     msg!("[MoneyUnstakeV1] Adding new coin to the Merkle tree");
     let coins: Vec<_> = vec![MerkleNode::from(update.coin.inner())];
-    merkle_add(info_db, coin_roots_db, &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE), &coins)?;
+    merkle_add(
+        info_db,
+        coin_roots_db,
+        &serialize(&MONEY_CONTRACT_LATEST_COIN_ROOT),
+        &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE),
+        &coins,
+    )?;
 
     Ok(())
 }

+ 3 - 0
src/contract/money/src/lib.rs

@@ -76,6 +76,7 @@ pub const MONEY_CONTRACT_TOKEN_FREEZE_TREE: &str = "token_freezes";
 // These are keys inside the info tree
 pub const MONEY_CONTRACT_DB_VERSION: &str = "db_version";
 pub const MONEY_CONTRACT_COIN_MERKLE_TREE: &str = "coin_tree";
+pub const MONEY_CONTRACT_LATEST_COIN_ROOT: &str = "last_root";
 pub const MONEY_CONTRACT_FAUCET_PUBKEYS: &str = "faucet_pubkeys";
 
 /// zkas mint circuit namespace
@@ -101,7 +102,9 @@ pub const CONSENSUS_CONTRACT_UNSTAKED_COIN_ROOTS_TREE: &str = "consensus_unstake
 // These are keys inside the consensus info tree
 pub const CONSENSUS_CONTRACT_DB_VERSION: &str = "db_version";
 pub const CONSENSUS_CONTRACT_STAKED_COIN_MERKLE_TREE: &str = "consensus_staked_coin_tree";
+pub const CONSENSUS_CONTRACT_STAKED_COIN_LATEST_COIN_ROOT: &str = "consensus_staked_last_root";
 pub const CONSENSUS_CONTRACT_UNSTAKED_COIN_MERKLE_TREE: &str = "consensus_unstaked_coin_tree";
+pub const CONSENSUS_CONTRACT_UNSTAKED_COIN_LATEST_COIN_ROOT: &str = "consensus_unstaked_last_root";
 
 /// zkas consensus mint circuit namespace
 pub const CONSENSUS_CONTRACT_ZKAS_MINT_NS_V1: &str = "ConsensusMint_V1";

+ 24 - 4
src/runtime/import/merkle.rs

@@ -45,7 +45,8 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
             // The buffer should deserialize into:
             // - db_info
             // - db_roots
-            // - key (as Vec<u8>) (key being the name of the sled tree where the Merkle tree is)
+            // - root_key (as Vec<u8>) (key being the name of the sled key in info_db where the latest root is)
+            // - tree_key (as Vec<u8>) (key being the name of the sled key in info_db where the Merkle tree is)
             // - coins (as Vec<MerkleNode>) (the coins being added into the Merkle tree)
             let mut buf_reader = Cursor::new(buf);
             // FIXME: There's a type DbHandle=u32, but this should maybe be renamed
@@ -86,8 +87,17 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                 return -2
             }
 
+            // 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::merkle", "Failed to decode key vec: {}", e);
+                    return -2
+                }
+            };
+
             // This `key` represents the sled database tree name
-            let key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
+            let tree_key: Vec<u8> = match Decodable::decode(&mut buf_reader) {
                 Ok(v) => v,
                 Err(e) => {
                     error!(target: "runtime::merkle", "Failed to decode key vec: {}", e);
@@ -114,7 +124,7 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                 .overlay
                 .lock()
                 .unwrap()
-                .get(&db_info.tree, &key)
+                .get(&db_info.tree, &tree_key)
             {
                 Ok(v) => v,
                 Err(e) => {
@@ -181,7 +191,7 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
             // Apply changes to overlay
             let lock = env.blockchain.lock().unwrap();
             let mut overlay = lock.overlay.lock().unwrap();
-            if overlay.insert(&db_info.tree, &key, &tree_data).is_err() {
+            if overlay.insert(&db_info.tree, &tree_key, &tree_data).is_err() {
                 error!(target: "runtime::merkle", "Couldn't insert to db_info tree");
                 return -2
             }
@@ -202,6 +212,16 @@ pub(crate) fn merkle_add(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -
                 }
             }
 
+            // Write a pointer to the latest known root
+            if !new_roots.is_empty() {
+                debug!(target: "runtime::merkle", "Replacing latest Merkle root pointer");
+                let latest_root = serialize(new_roots.last().unwrap());
+                if overlay.insert(&db_info.tree, &root_key, &latest_root).is_err() {
+                    error!(target: "runtime::merkle", "Couldn't insert latest root to db_info tree");
+                    return -2
+                }
+            }
+
             0
         }
         _ => -1,

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

@@ -27,19 +27,22 @@ use super::{
 /// Add given elements into a Merkle tree.
 /// * `db_info` is a handle for a database where the Merkle tree is stored.
 /// * `db_roots` is a handle for a database where all the new Merkle roots are stored.
-/// * `key` is the serialized key for `db_info`.
+/// * `root_key` is the serialized key pointing to the latest Merkle root in `db_info`
+/// * `tree_key` is the serialized key pointing to the Merkle tree in `db_info`.
 /// * `elements` are the items we want to add to the Merkle tree.
 pub fn merkle_add(
     db_info: DbHandle,
     db_roots: DbHandle,
-    key: &[u8],
+    root_key: &[u8],
+    tree_key: &[u8],
     elements: &[MerkleNode],
 ) -> GenericResult<()> {
     let mut buf = vec![];
     let mut len = 0;
     len += db_info.encode(&mut buf)?;
     len += db_roots.encode(&mut buf)?;
-    len += key.to_vec().encode(&mut buf)?;
+    len += root_key.to_vec().encode(&mut buf)?;
+    len += tree_key.to_vec().encode(&mut buf)?;
     len += elements.to_vec().encode(&mut buf)?;
 
     match unsafe { merkle_add_(buf.as_ptr(), len as u32) } {