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

dao::propose(): when a proposal is made, we snapshot the current coins root (old) AND nullifiers root (newly added)

zero 2 лет назад
Родитель
Сommit
51d7f2996a

+ 19 - 5
src/contract/dao/src/entrypoint/propose.rs

@@ -18,7 +18,8 @@
 
 use darkfi_money_contract::{
     MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
-    MONEY_CONTRACT_NULLIFIERS_TREE,
+    MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
+    MONEY_CONTRACT_NULLIFIER_ROOTS_TREE,
 };
 use darkfi_sdk::{
     crypto::{contract_id::MONEY_CONTRACT_ID, pasta_prelude::*, ContractId, MerkleNode, PublicKey},
@@ -161,11 +162,23 @@ pub(crate) fn dao_propose_process_instruction(
         msg!("[Dao::Propose] Error: Failed to fetch latest Money Merkle root");
         return Err(ContractError::Internal)
     };
-    let snapshot_root: MerkleNode = deserialize(&data)?;
-    msg!("[Dao::Propose] Snapshotting Money at Merkle root {}", snapshot_root);
+    let snapshot_coins: MerkleNode = deserialize(&data)?;
+
+    let Some(data) = db_get(money_info_db, MONEY_CONTRACT_LATEST_NULLIFIER_ROOT)? else {
+        msg!("[Dao::Propose] Error: Failed to fetch latest Money SMT root");
+        return Err(ContractError::Internal)
+    };
+    let snapshot_nulls: pallas::Base = deserialize(&data)?;
+
+    msg!(
+        "[Dao::Propose] Snapshotting Money at Merkle {} and SMT {:?}",
+        snapshot_coins,
+        snapshot_nulls
+    );
 
     // Create state update
-    let update = DaoProposeUpdate { proposal_bulla: params.proposal_bulla, snapshot_root };
+    let update =
+        DaoProposeUpdate { proposal_bulla: params.proposal_bulla, snapshot_coins, snapshot_nulls };
     let mut update_data = vec![];
     update_data.write_u8(DaoFunction::Propose as u8)?;
     update.encode(&mut update_data)?;
@@ -183,7 +196,8 @@ pub(crate) fn dao_propose_process_update(
     // Build the proposal metadata
     let proposal_metadata = DaoProposalMetadata {
         vote_aggregate: DaoBlindAggregateVote::default(),
-        snapshot_root: update.snapshot_root,
+        snapshot_coins: update.snapshot_coins,
+        snapshot_nulls: update.snapshot_nulls,
     };
 
     // Set the new proposal in the db

+ 3 - 2
src/contract/dao/src/entrypoint/vote.rs

@@ -139,11 +139,12 @@ pub(crate) fn dao_vote_process_instruction(
     let mut vote_nullifiers = vec![];
 
     for input in &params.inputs {
-        if proposal_metadata.snapshot_root != input.merkle_root {
+        // TODO: remove merkle_coins entirely from input. It's not needed.
+        if proposal_metadata.snapshot_coins != input.merkle_root {
             msg!(
                 "[Dao::Vote] Error: Invalid input Merkle root: {} (expected {})",
                 input.merkle_root,
-                proposal_metadata.snapshot_root
+                proposal_metadata.snapshot_coins
             );
             return Err(DaoError::InvalidInputMerkleRoot.into())
         }

+ 6 - 2
src/contract/dao/src/model.rs

@@ -258,7 +258,9 @@ pub struct DaoProposeUpdate {
     /// Minted proposal bulla
     pub proposal_bulla: DaoProposalBulla,
     /// Snapshotted Merkle root in the Money state
-    pub snapshot_root: MerkleNode,
+    pub snapshot_coins: MerkleNode,
+    /// Snapshotted SMT root in the Money state
+    pub snapshot_nulls: pallas::Base,
 }
 
 /// Metadata for a DAO proposal on the blockchain
@@ -267,7 +269,9 @@ pub struct DaoProposalMetadata {
     /// Vote aggregate
     pub vote_aggregate: DaoBlindAggregateVote,
     /// Snapshotted Merkle root in the Money state
-    pub snapshot_root: MerkleNode,
+    pub snapshot_coins: MerkleNode,
+    /// Snapshotted SMT root in the Money state
+    pub snapshot_nulls: pallas::Base,
 }
 
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]

+ 15 - 7
src/contract/money/src/entrypoint.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::{
-    crypto::{pasta_prelude::Field, ContractId, MerkleNode, MerkleTree},
+    crypto::{pasta_prelude::Field, smt::EMPTY_NODES_FP, ContractId, MerkleNode, MerkleTree},
     dark_tree::DarkLeaf,
     db::{db_init, db_lookup, db_set, zkas_db_set},
     error::ContractResult,
@@ -33,10 +33,12 @@ use crate::{
         MoneyPoWRewardUpdateV1, MoneyTokenFreezeUpdateV1, MoneyTokenMintUpdateV1,
         MoneyTransferUpdateV1,
     },
-    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_NULLIFIER_ROOTS_TREE,
-    MONEY_CONTRACT_TOKEN_FREEZE_TREE, MONEY_CONTRACT_TOTAL_FEES_PAID,
+    MoneyFunction, EMPTY_COINS_TREE_ROOT, MONEY_CONTRACT_COINS_TREE,
+    MONEY_CONTRACT_COIN_MERKLE_TREE, MONEY_CONTRACT_COIN_ROOTS_TREE, MONEY_CONTRACT_DB_VERSION,
+    MONEY_CONTRACT_INFO_TREE, MONEY_CONTRACT_LATEST_COIN_ROOT,
+    MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, MONEY_CONTRACT_NULLIFIERS_TREE,
+    MONEY_CONTRACT_NULLIFIER_ROOTS_TREE, MONEY_CONTRACT_TOKEN_FREEZE_TREE,
+    MONEY_CONTRACT_TOTAL_FEES_PAID,
 };
 
 /// `Money::Fee` functions
@@ -124,13 +126,13 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
     zkas_db_set(&token_frz_v1_bincode[..])?;
 
     // Set up a database tree to hold Merkle roots of all coin trees
-    // k=MerkleNode, v=[]
+    // k=root_hash:32, v=(block_height:3, tx_idx:2, call_idx: 2)
     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=[]
+    // k=root_hash:32, v=(block_height:3, tx_idx:2, call_idx: 2)
     if db_lookup(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE).is_err() {
         db_init(cid, MONEY_CONTRACT_NULLIFIER_ROOTS_TREE)?;
     }
@@ -171,6 +173,12 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
             // Initialize the paid fees accumulator
             db_set(info_db, MONEY_CONTRACT_TOTAL_FEES_PAID, &serialize(&0_u64))?;
 
+            // Initialize coins and nulls latest root field
+            // This will result in exhausted gas so we use a precalculated value:
+            //let root = coin_tree.root(0).unwrap();
+            db_set(info_db, MONEY_CONTRACT_LATEST_COIN_ROOT, &serialize(&EMPTY_COINS_TREE_ROOT))?;
+            db_set(info_db, MONEY_CONTRACT_LATEST_NULLIFIER_ROOT, &serialize(&EMPTY_NODES_FP[0]))?;
+
             info_db
         }
     };

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

@@ -83,6 +83,12 @@ 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";
 
+/// Precalculated root hash for a tree containing Fp::ZERO to save gas
+pub const EMPTY_COINS_TREE_ROOT: [u8; 32] = [
+    0xb8, 0xc1, 0x07, 0x5a, 0x80, 0xa8, 0x09, 0x65, 0xc2, 0x39, 0x8f, 0x71, 0x1f, 0xe7, 0x3e, 0x05,
+    0xb4, 0xed, 0xae, 0xde, 0xf1, 0x62, 0xf2, 0x61, 0xd4, 0xee, 0xd7, 0xcd, 0x72, 0x74, 0x8d, 0x17,
+];
+
 /// zkas fee circuit namespace
 pub const MONEY_CONTRACT_ZKAS_FEE_NS_V1: &str = "Fee_V1";
 /// zkas mint circuit namespace

+ 0 - 1
src/contract/money/tests/integration.rs

@@ -18,7 +18,6 @@
 
 use darkfi::Result;
 use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
-use darkfi_sdk::blockchain::expected_reward;
 
 #[test]
 fn money_integration() -> Result<()> {