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

drk: properly scan blocks based on tx call order

skoupidi 2 лет назад
Родитель
Сommit
d1c11b7cf6
3 измененных файлов с 121 добавлено и 113 удалено
  1. 41 40
      bin/drk/src/dao.rs
  2. 37 41
      bin/drk/src/money.rs
  3. 43 32
      bin/drk/src/rpc.rs

+ 41 - 40
bin/drk/src/dao.rs

@@ -47,6 +47,7 @@ use darkfi_sdk::{
         SecretKey, DAO_CONTRACT_ID, MONEY_CONTRACT_ID,
     },
     pasta::pallas,
+    tx::TransactionHash,
     ContractCall,
 };
 use darkfi_serial::{
@@ -198,7 +199,7 @@ pub struct Dao {
     /// Leaf position of the DAO in the Merkle tree of DAOs
     pub leaf_position: Option<bridgetree::Position>,
     /// The transaction hash where the DAO was deployed
-    pub tx_hash: Option<blake3::Hash>,
+    pub tx_hash: Option<TransactionHash>,
     /// The call index in the transaction where the DAO was deployed
     pub call_index: Option<u8>,
 }
@@ -283,7 +284,7 @@ pub struct DaoProposal {
     /// Snapshotted Money Merkle tree
     pub money_snapshot_tree: Option<MerkleTree>,
     /// Transaction hash where this proposal was proposed
-    pub tx_hash: Option<blake3::Hash>,
+    pub tx_hash: Option<TransactionHash>,
     /// call index in the transaction where this proposal was proposed
     pub call_index: Option<u8>,
     /// The vote ID we've voted on this proposal
@@ -353,7 +354,7 @@ pub struct DaoVote {
     /// Blinding facfor of all votes
     pub all_vote_blind: ScalarBlind,
     /// Transaction hash where this vote was casted
-    pub tx_hash: Option<blake3::Hash>,
+    pub tx_hash: Option<TransactionHash>,
     /// call index in the transaction where this vote was casted
     pub call_index: Option<u8>,
 }
@@ -686,58 +687,60 @@ impl Drk {
 
     /// Append data related to DAO contract transactions into the wallet database.
     /// Optionally, if `confirm` is true, also append the data in the Merkle trees, etc.
-    pub async fn apply_tx_dao_data(&self, tx: &Transaction, confirm: bool) -> Result<()> {
-        let cid = *DAO_CONTRACT_ID;
+    pub async fn apply_tx_dao_data(
+        &self,
+        data: &[u8],
+        tx_hash: TransactionHash,
+        call_idx: u8,
+        confirm: bool,
+    ) -> Result<()> {
         let mut daos = self.get_daos().await?;
         let mut daos_to_confirm = vec![];
         let (mut daos_tree, mut proposals_tree) = self.get_dao_trees().await?;
 
         // DAOs that have been minted
-        let mut new_dao_bullas: Vec<(DaoBulla, Option<blake3::Hash>, u8)> = vec![];
+        let mut new_dao_bullas: Vec<(DaoBulla, Option<TransactionHash>, u8)> = vec![];
         // DAO proposals that have been minted
         let mut new_dao_proposals: Vec<(
             DaoProposeParams,
             Option<MerkleTree>,
-            Option<blake3::Hash>,
+            Option<TransactionHash>,
             u8,
         )> = vec![];
         let mut our_proposals: Vec<DaoProposal> = vec![];
         // DAO votes that have been seen
-        let mut new_dao_votes: Vec<(DaoVoteParams, Option<blake3::Hash>, u8)> = vec![];
+        let mut new_dao_votes: Vec<(DaoVoteParams, Option<TransactionHash>, u8)> = vec![];
         let mut dao_votes: Vec<DaoVote> = vec![];
 
         // Run through the transaction and see what we got:
-        for (i, call) in tx.calls.iter().enumerate() {
-            if call.data.contract_id == cid && call.data.data[0] == DaoFunction::Mint as u8 {
-                println!("Found Dao::Mint in call {i}");
-                let params: DaoMintParams = deserialize(&call.data.data[1..])?;
-                let tx_hash = if confirm { Some(blake3::hash(&serialize(tx))) } else { None };
-                new_dao_bullas.push((params.dao_bulla, tx_hash, i as u8));
-                continue
+        match DaoFunction::try_from(data[0])? {
+            DaoFunction::Mint => {
+                println!("[apply_tx_dao_data] Found Dao::Mint call");
+                let params: DaoMintParams = deserialize(&data[1..])?;
+                let tx_hash = if confirm { Some(tx_hash) } else { None };
+                new_dao_bullas.push((params.dao_bulla, tx_hash, call_idx));
             }
-
-            if call.data.contract_id == cid && call.data.data[0] == DaoFunction::Propose as u8 {
-                println!("Found Dao::Propose in call {i}");
-                let params: DaoProposeParams = deserialize(&call.data.data[1..])?;
-                let tx_hash = if confirm { Some(blake3::hash(&serialize(tx))) } else { None };
+            DaoFunction::Propose => {
+                println!("[apply_tx_dao_data] Found Dao::Propose call");
+                let params: DaoProposeParams = deserialize(&data[1..])?;
+                let tx_hash = if confirm { Some(tx_hash) } else { None };
                 // We need to clone the tree here for reproducing the snapshot Merkle root
                 let money_tree = if confirm { Some(self.get_money_tree().await?) } else { None };
-                new_dao_proposals.push((params, money_tree, tx_hash, i as u8));
-                continue
+                new_dao_proposals.push((params, money_tree, tx_hash, call_idx));
             }
-
-            if call.data.contract_id == cid && call.data.data[0] == DaoFunction::Vote as u8 {
-                println!("Found Dao::Vote in call {i}");
-                let params: DaoVoteParams = deserialize(&call.data.data[1..])?;
-                let tx_hash = if confirm { Some(blake3::hash(&serialize(tx))) } else { None };
-                new_dao_votes.push((params, tx_hash, i as u8));
-                continue
+            DaoFunction::Vote => {
+                println!("[apply_tx_dao_data] Found Dao::Vote call");
+                let params: DaoVoteParams = deserialize(&data[1..])?;
+                let tx_hash = if confirm { Some(tx_hash) } else { None };
+                new_dao_votes.push((params, tx_hash, call_idx));
             }
-
-            if call.data.contract_id == cid && call.data.data[0] == DaoFunction::Exec as u8 {
-                // This seems to not need any special action
-                println!("Found Dao::Exec in call {i}");
-                continue
+            DaoFunction::Exec => {
+                println!("[apply_tx_dao_data] Found Dao::Exec call");
+                // TODO: implement
+            }
+            DaoFunction::AuthMoneyTransfer => {
+                println!("[apply_tx_dao_data] Found Dao::AuthMoneyTransfer call");
+                // TODO: implement
             }
         }
 
@@ -749,7 +752,7 @@ impl Drk {
                 daos_tree.append(MerkleNode::from(new_bulla.0.inner()));
                 for dao in daos.iter_mut() {
                     if dao.bulla() == new_bulla.0 {
-                        println!("Found minted DAO {}, noting down for wallet update", new_bulla.0);
+                        println!("[apply_tx_dao_data] Found minted DAO {}, noting down for wallet update", new_bulla.0);
                         // We have this DAO imported in our wallet. Add the metadata:
                         dao.leaf_position = daos_tree.mark();
                         dao.tx_hash = new_bulla.1;
@@ -771,7 +774,7 @@ impl Drk {
                         // ID by looking at how many proposals we already have.
                         // We also assume we don't mantain duplicate DAOs in the
                         // wallet.
-                        println!("Managed to decrypt DAO proposal note");
+                        println!("[apply_tx_dao_data] Managed to decrypt DAO proposal note");
                         let daos_proposals = self.get_dao_proposals(dao.id).await?;
                         let our_prop = DaoProposal {
                             // This ID stuff is flaky.
@@ -798,7 +801,7 @@ impl Drk {
                 for dao in &daos {
                     // TODO: we shouldn't decrypt with all DAOs here
                     let note = vote.0.note.decrypt_unsafe(&dao.secret_key)?;
-                    println!("Managed to decrypt DAO proposal vote note");
+                    println!("[apply_tx_dao_data] Managed to decrypt DAO proposal vote note");
                     let daos_proposals = self.get_dao_proposals(dao.id).await?;
                     let mut proposal_id = None;
 
@@ -810,7 +813,7 @@ impl Drk {
                     }
 
                     if proposal_id.is_none() {
-                        println!("Warning: Decrypted DaoVoteNote but did not find proposal");
+                        println!("[apply_tx_dao_data] Warning: Decrypted DaoVoteNote but did not find proposal");
                         break
                     }
 
@@ -835,9 +838,7 @@ impl Drk {
                     dao_votes.push(v);
                 }
             }
-        }
 
-        if confirm {
             if let Err(e) = self.put_dao_trees(&daos_tree, &proposals_tree).await {
                 return Err(Error::RusqliteError(format!(
                     "[apply_tx_dao_data] Put DAO tree failed: {e:?}"

+ 37 - 41
bin/drk/src/money.rs

@@ -22,12 +22,12 @@ use lazy_static::lazy_static;
 use rand::rngs::OsRng;
 use rusqlite::types::Value;
 
-use darkfi::{tx::Transaction, zk::halo2::Field, Error, Result};
+use darkfi::{zk::halo2::Field, Error, Result};
 use darkfi_money_contract::{
     client::{MoneyNote, OwnCoin},
     model::{
-        Coin, MoneyPoWRewardParamsV1, MoneyTokenFreezeParamsV1, MoneyTokenMintParamsV1,
-        MoneyTransferParamsV1, Nullifier, TokenId, DARK_TOKEN_ID,
+        Coin, MoneyGenesisMintParamsV1, MoneyPoWRewardParamsV1, MoneyTokenFreezeParamsV1,
+        MoneyTokenMintParamsV1, MoneyTransferParamsV1, Nullifier, TokenId, DARK_TOKEN_ID,
     },
     MoneyFunction,
 };
@@ -627,30 +627,26 @@ impl Drk {
     }
 
     /// Append data related to Money contract transactions into the wallet database.
-    pub async fn apply_tx_money_data(&self, tx: &Transaction, _confirm: bool) -> Result<()> {
-        let cid = *MONEY_CONTRACT_ID;
-
+    pub async fn apply_tx_money_data(&self, data: &[u8]) -> Result<()> {
         let mut nullifiers: Vec<Nullifier> = vec![];
         let mut coins: Vec<Coin> = vec![];
         let mut notes: Vec<AeadEncryptedNote> = vec![];
         let mut freezes: Vec<TokenId> = vec![];
 
-        for (i, call) in tx.calls.iter().enumerate() {
-            if call.data.contract_id == cid && call.data.data[0] == MoneyFunction::PoWRewardV1 as u8
-            {
-                println!("Found Money::PoWRewardV1 in call {i}");
-                let params: MoneyPoWRewardParamsV1 = deserialize(&call.data.data[1..])?;
-
+        match MoneyFunction::try_from(data[0])? {
+            MoneyFunction::FeeV1 => {
+                println!("[apply_tx_money_data] Found Money::FeeV1 call");
+                // TODO: implement
+            }
+            MoneyFunction::GenesisMintV1 => {
+                println!("[apply_tx_money_data] Found Money::GenesisMintV1 call");
+                let params: MoneyGenesisMintParamsV1 = deserialize(&data[1..])?;
                 coins.push(params.output.coin);
                 notes.push(params.output.note);
-
-                continue
             }
-
-            if call.data.contract_id == cid && call.data.data[0] == MoneyFunction::TransferV1 as u8
-            {
-                println!("Found Money::TransferV1 in call {i}");
-                let params: MoneyTransferParamsV1 = deserialize(&call.data.data[1..])?;
+            MoneyFunction::TransferV1 => {
+                println!("[apply_tx_money_data] Found Money::TransferV1 call");
+                let params: MoneyTransferParamsV1 = deserialize(&data[1..])?;
 
                 for input in params.inputs {
                     nullifiers.push(input.nullifier);
@@ -660,13 +656,10 @@ impl Drk {
                     coins.push(output.coin);
                     notes.push(output.note);
                 }
-
-                continue
             }
-
-            if call.data.contract_id == cid && call.data.data[0] == MoneyFunction::OtcSwapV1 as u8 {
-                println!("Found Money::OtcSwapV1 in call {i}");
-                let params: MoneyTransferParamsV1 = deserialize(&call.data.data[1..])?;
+            MoneyFunction::OtcSwapV1 => {
+                println!("[apply_tx_money_data] Found Money::OtcSwapV1 call");
+                let params: MoneyTransferParamsV1 = deserialize(&data[1..])?;
 
                 for input in params.inputs {
                     nullifiers.push(input.nullifier);
@@ -676,27 +669,30 @@ impl Drk {
                     coins.push(output.coin);
                     notes.push(output.note);
                 }
-
-                continue
             }
-
-            if call.data.contract_id == cid && call.data.data[0] == MoneyFunction::TokenMintV1 as u8
-            {
-                println!("Found Money::MintV1 in call {i}");
-                let params: MoneyTokenMintParamsV1 = deserialize(&call.data.data[1..])?;
+            MoneyFunction::TokenMintV1 => {
+                println!("[apply_tx_money_data] Found Money::TokenMintV1 call");
+                let params: MoneyTokenMintParamsV1 = deserialize(&data[1..])?;
                 coins.push(params.coin);
+                // TODO: why is this commented?
                 //notes.push(output.note);
-                continue
             }
-
-            if call.data.contract_id == cid &&
-                call.data.data[0] == MoneyFunction::TokenFreezeV1 as u8
-            {
-                println!("Found Money::FreezeV1 in call {i}");
-                let params: MoneyTokenFreezeParamsV1 = deserialize(&call.data.data[1..])?;
+            MoneyFunction::TokenFreezeV1 => {
+                println!("[apply_tx_money_data] Found Money::TokenFreezeV1 call");
+                let params: MoneyTokenFreezeParamsV1 = deserialize(&data[1..])?;
                 let token_id = TokenId::derive_public(params.mint_public);
                 freezes.push(token_id);
             }
+            MoneyFunction::PoWRewardV1 => {
+                println!("[apply_tx_money_data] Found Money::PoWRewardV1 call");
+                let params: MoneyPoWRewardParamsV1 = deserialize(&data[1..])?;
+                coins.push(params.output.coin);
+                notes.push(params.output.note);
+            }
+            MoneyFunction::AuthTokenMintV1 => {
+                println!("[apply_tx_money_data] Found Money::AuthTokenMintV1 call");
+                // TODO: implement
+            }
         }
 
         let secrets = self.get_money_secrets().await?;
@@ -712,8 +708,8 @@ impl Drk {
             // Attempt to decrypt the note
             for secret in secrets.iter().chain(dao_secrets.iter()) {
                 if let Ok(note) = note.decrypt::<MoneyNote>(secret) {
-                    println!("Successfully decrypted a Money Note");
-                    println!("Witnessing coin in Merkle tree");
+                    println!("[apply_tx_money_data] Successfully decrypted a Money Note");
+                    println!("[apply_tx_money_data] Witnessing coin in Merkle tree");
                     let leaf_position = tree.mark().unwrap();
 
                     let owncoin =

+ 43 - 32
bin/drk/src/rpc.rs

@@ -32,7 +32,10 @@ use darkfi::{
     util::encoding::base64,
     Error, Result,
 };
-use darkfi_sdk::{crypto::ContractId, tx::TransactionHash};
+use darkfi_sdk::{
+    crypto::{ContractId, DAO_CONTRACT_ID, DEPLOYOOOR_CONTRACT_ID, MONEY_CONTRACT_ID},
+    tx::TransactionHash,
+};
 use darkfi_serial::{deserialize_async, serialize_async};
 
 use crate::{
@@ -132,12 +135,11 @@ impl Drk {
                         println!("=======================================");
 
                         println!("Deserialized successfully. Scanning block...");
-                        if let Err(e) = self.scan_block_money(&block_data).await {
+                        if let Err(e) = self.scan_block(&block_data).await {
                             return Err(Error::RusqliteError(format!(
-                                "[subscribe_blocks] Scaning blocks for Money failed: {e:?}"
+                                "[subscribe_blocks] Scanning block failed: {e:?}"
                             )))
                         }
-                        self.scan_block_dao(&block_data).await?;
                         if let Err(e) = self
                             .update_tx_history_records_status(&block_data.txs, "Finalized")
                             .await
@@ -166,15 +168,41 @@ impl Drk {
         Err(e)
     }
 
-    /// `scan_block_money` will go over transactions in a block and fetch the ones dealing
-    /// with the money contract. Then over all of them, try to see if any are related
-    /// to us. If any are found, the metadata is extracted and placed into the wallet
-    /// for future use.
-    async fn scan_block_money(&self, block: &BlockInfo) -> Result<()> {
-        println!("[Money] Iterating over {} transactions", block.txs.len());
-
+    /// `scan_block` will go over over transactions in a block and handle their calls
+    /// based on the called contract. Additionally, will update `last_scanned_block` to
+    /// the probided block height.
+    async fn scan_block(&self, block: &BlockInfo) -> Result<()> {
+        println!("[scan_block] Iterating over {} transactions", block.txs.len());
         for tx in block.txs.iter() {
-            self.apply_tx_money_data(tx, true).await?;
+            println!("[scan_block] Processing transaction: {}", tx.hash());
+            for (i, call) in tx.calls.iter().enumerate() {
+                if call.data.contract_id == *MONEY_CONTRACT_ID {
+                    println!("[scan_block] Found Money contract in call {i}");
+                    self.apply_tx_money_data(&call.data.data).await?;
+                    continue
+                }
+
+                if call.data.contract_id == *DAO_CONTRACT_ID {
+                    println!("[scan_block] Found DAO contract in call {i}");
+                    self.apply_tx_dao_data(
+                        &call.data.data,
+                        TransactionHash::new(*blake3::hash(&serialize_async(tx).await).as_bytes()),
+                        i as u8,
+                        true,
+                    )
+                    .await?;
+                    continue
+                }
+
+                if call.data.contract_id == *DEPLOYOOOR_CONTRACT_ID {
+                    println!("[scan_block] Found DeployoOor contract in call {i}");
+                    // TODO: implement
+                    continue
+                }
+
+                // TODO: For now we skip non-native contract calls
+                println!("[scan_block] Found non-native contract in call {i}, skipping.");
+            }
         }
 
         // Write this block height into `last_scanned_block`
@@ -182,26 +210,13 @@ impl Drk {
             format!("UPDATE {} SET {} = ?1;", *MONEY_INFO_TABLE, MONEY_INFO_COL_LAST_SCANNED_BLOCK);
         if let Err(e) = self.wallet.exec_sql(&query, rusqlite::params![block.header.height]).await {
             return Err(Error::RusqliteError(format!(
-                "[scan_block_money] Update last scanned block failed: {e:?}"
+                "[scan_block] Update last scanned block failed: {e:?}"
             )))
         }
 
         Ok(())
     }
 
-    /// `scan_block_dao` will go over transactions in a block and fetch the ones dealing
-    /// with the dao contract. Then over all of them, try to see if any are related
-    /// to us. If any are found, the metadata is extracted and placed into the wallet
-    /// for future use.
-    async fn scan_block_dao(&self, block: &BlockInfo) -> Result<()> {
-        println!("[DAO] Iterating over {} transactions", block.txs.len());
-        for tx in block.txs.iter() {
-            self.apply_tx_dao_data(tx, true).await?;
-        }
-
-        Ok(())
-    }
-
     /// Scans the blockchain starting from the last scanned block, for relevant
     /// money transfer transactions. If reset flag is provided, Merkle tree state
     /// and coins are reset, and start scanning from beginning. Alternatively,
@@ -253,12 +268,8 @@ impl Drk {
                         return Err(WalletDbError::GenericError)
                     }
                 };
-                if let Err(e) = self.scan_block_money(&block).await {
-                    eprintln!("[scan_blocks] Scan block Money failed: {e:?}");
-                    return Err(WalletDbError::GenericError)
-                };
-                if let Err(e) = self.scan_block_dao(&block).await {
-                    eprintln!("[scan_blocks] Scan block DAO failed: {e:?}");
+                if let Err(e) = self.scan_block(&block).await {
+                    eprintln!("[scan_blocks] Scan block failed: {e:?}");
                     return Err(WalletDbError::GenericError)
                 };
                 self.update_tx_history_records_status(&block.txs, "Finalized").await?;