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

drk: use block references to reset stuff

skoupidi 1 год назад
Родитель
Сommit
fa3b2b8277

+ 16 - 1
bin/drk/src/cache.rs

@@ -28,7 +28,7 @@ use darkfi_sdk::{
     error::{ContractError, ContractResult},
     pasta::pallas,
 };
-use darkfi_serial::serialize;
+use darkfi_serial::{deserialize, serialize};
 use log::error;
 use num_bigint::BigUint;
 use sled_overlay::{sled, SledDbOverlay, SledDbOverlayStateDiff};
@@ -79,6 +79,21 @@ impl Cache {
             money_smt,
         })
     }
+
+    /// Fetch given block height numbers from the store's state inverse
+    /// diffs tree. The function will fail if a block height number was
+    /// not found.
+    pub fn get_state_inverse_diff(&self, heights: &[u32]) -> Result<Vec<SledDbOverlayStateDiff>> {
+        let mut ret = Vec::with_capacity(heights.len());
+        for height in heights {
+            match self.state_inverse_diff.get(height.to_be_bytes())? {
+                Some(found) => ret.push(deserialize(&found)?),
+                None => return Err(Error::BlockStateInverseDiffNotFound(*height)),
+            };
+        }
+
+        Ok(ret)
+    }
 }
 
 /// Overlay structure over a [`Cache`] instance.

+ 78 - 4
bin/drk/src/dao.rs

@@ -1628,7 +1628,7 @@ impl Drk {
     }
 
     /// Reset the DAO Merkle trees in the cache.
-    pub async fn reset_dao_trees(&self) -> WalletDbResult<()> {
+    pub fn reset_dao_trees(&self) -> WalletDbResult<()> {
         println!("Resetting DAO Merkle trees");
         if let Err(e) = self.cache.merkle_trees.remove(SLED_MERKLE_TREES_DAO_DAOS) {
             println!("[reset_dao_trees] Resetting DAO DAOs Merkle tree failed: {e:?}");
@@ -1644,7 +1644,7 @@ impl Drk {
     }
 
     /// Reset confirmed DAOs in the wallet.
-    pub async fn reset_daos(&self) -> WalletDbResult<()> {
+    pub fn reset_daos(&self) -> WalletDbResult<()> {
         println!("Resetting DAO confirmations");
         let query = format!(
             "UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL;",
@@ -1660,8 +1660,27 @@ impl Drk {
         Ok(())
     }
 
+    /// Reset confirmed DAOs in the wallet that were minted after
+    /// provided height.
+    pub fn unconfirm_daos_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Resetting DAO confirmations after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL WHERE {} > ?1;",
+            *DAO_DAOS_TABLE,
+            DAO_DAOS_COL_LEAF_POSITION,
+            DAO_DAOS_COL_MINT_HEIGHT,
+            DAO_DAOS_COL_TX_HASH,
+            DAO_DAOS_COL_CALL_INDEX,
+            DAO_DAOS_COL_MINT_HEIGHT,
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully unconfirmed DAOs");
+
+        Ok(())
+    }
+
     /// Reset all DAO proposals in the wallet.
-    pub async fn reset_dao_proposals(&self) -> WalletDbResult<()> {
+    pub fn reset_dao_proposals(&self) -> WalletDbResult<()> {
         println!("Resetting DAO proposals confirmations");
         let query = format!(
             "UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL;",
@@ -1681,11 +1700,66 @@ impl Drk {
         Ok(())
     }
 
+    /// Reset DAO proposals in the wallet that were minted after
+    /// provided height.
+    pub fn unconfirm_dao_proposals_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Resetting DAO proposals confirmations after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL, {} = NULL WHERE {} > ?1;",
+            *DAO_PROPOSALS_TABLE,
+            DAO_PROPOSALS_COL_LEAF_POSITION,
+            DAO_PROPOSALS_COL_MONEY_SNAPSHOT_TREE,
+            DAO_PROPOSALS_COL_NULLIFIERS_SMT_SNAPSHOT,
+            DAO_PROPOSALS_COL_MINT_HEIGHT,
+            DAO_PROPOSALS_COL_TX_HASH,
+            DAO_PROPOSALS_COL_CALL_INDEX,
+            DAO_PROPOSALS_COL_EXEC_HEIGHT,
+            DAO_PROPOSALS_COL_EXEC_TX_HASH,
+            DAO_PROPOSALS_COL_MINT_HEIGHT,
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully unconfirmed DAO proposals");
+
+        Ok(())
+    }
+
+    /// Reset execution information in the wallet for DAO proposals
+    /// that were executed after provided height.
+    pub fn unexec_dao_proposals_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Resetting DAO proposals execution information after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = NULL, {} = NULL WHERE {} > ?1;",
+            *DAO_PROPOSALS_TABLE,
+            DAO_PROPOSALS_COL_EXEC_HEIGHT,
+            DAO_PROPOSALS_COL_EXEC_TX_HASH,
+            DAO_PROPOSALS_COL_EXEC_HEIGHT,
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully reset DAO proposals execution information");
+
+        Ok(())
+    }
+
     /// Reset all DAO votes in the wallet.
     pub fn reset_dao_votes(&self) -> WalletDbResult<()> {
         println!("Resetting DAO votes");
         let query = format!("DELETE FROM {};", *DAO_VOTES_TABLE);
-        self.wallet.exec_sql(&query, &[])
+        self.wallet.exec_sql(&query, &[])?;
+        println!("Successfully reset DAO votes");
+
+        Ok(())
+    }
+
+    /// Remove the DAO votes in the wallet that were created after
+    /// provided height.
+    pub fn remove_dao_votes_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Removing DAO votes after: {height}");
+        let query =
+            format!("DELETE FROM {} WHERE {} > ?1;", *DAO_VOTES_TABLE, DAO_VOTES_COL_BLOCK_HEIGHT);
+        self.wallet.exec_sql(&query, rusqlite::params![height])?;
+        println!("Successfully removed DAO votes");
+
+        Ok(())
     }
 
     /// Import given DAO params into the wallet with a given name.

+ 30 - 0
bin/drk/src/deploy.rs

@@ -84,6 +84,36 @@ impl Drk {
         Ok(())
     }
 
+    /// Reset all token deploy authorities frozen status in the wallet.
+    pub fn reset_deploy_authorities(&self) -> WalletDbResult<()> {
+        println!("Resetting deploy authorities frozen status");
+        let query = format!(
+            "UPDATE {} SET {} = 0, {} = NULL;",
+            *DEPLOY_AUTH_TABLE, DEPLOY_AUTH_COL_IS_FROZEN, DEPLOY_AUTH_COL_FREEZE_HEIGHT
+        );
+        self.wallet.exec_sql(&query, &[])?;
+        println!("Successfully reset deploy authorities frozen status");
+
+        Ok(())
+    }
+
+    /// Remove deploy authorities frozen status in the wallet that
+    /// where frozen after provided height.
+    pub fn unfreeze_deploy_authorities_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Resetting deploy authorities frozen status after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = 0, {} = NULL WHERE {} > ?1;",
+            *DEPLOY_AUTH_TABLE,
+            DEPLOY_AUTH_COL_IS_FROZEN,
+            DEPLOY_AUTH_COL_FREEZE_HEIGHT,
+            DEPLOY_AUTH_COL_FREEZE_HEIGHT
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully reset deploy authorities frozen status");
+
+        Ok(())
+    }
+
     /// List contract deploy authorities from the wallet
     pub async fn list_deploy_auth(&self) -> Result<Vec<(i64, ContractId, bool, Option<u32>)>> {
         let rows = match self.wallet.query_multiple(&DEPLOY_AUTH_TABLE, &[], &[]) {

+ 5 - 4
bin/drk/src/lib.rs

@@ -122,17 +122,18 @@ impl Drk {
     }
 
     /// Auxiliary function to completely reset wallet state.
-    pub async fn reset(&self) -> WalletDbResult<()> {
+    pub fn reset(&self) -> WalletDbResult<()> {
         println!("Resetting full wallet state");
         self.reset_scanned_blocks()?;
         self.reset_money_tree()?;
         self.reset_money_smt()?;
         self.reset_money_coins()?;
         self.reset_mint_authorities()?;
-        self.reset_dao_trees().await?;
-        self.reset_daos().await?;
-        self.reset_dao_proposals().await?;
+        self.reset_dao_trees()?;
+        self.reset_daos()?;
+        self.reset_dao_proposals()?;
         self.reset_dao_votes()?;
+        self.reset_deploy_authorities()?;
         self.reset_tx_history()?;
         println!("Successfully reset full wallet state");
         Ok(())

+ 1 - 1
bin/drk/src/main.rs

@@ -2032,7 +2032,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'static>>) -> Result<()> {
             .await;
 
             if let Some(height) = reset {
-                if let Err(e) = drk.reset_to_height(height).await {
+                if let Err(e) = drk.reset_to_height(height) {
                     eprintln!("Failed during wallet reset: {e:?}");
                     exit(2);
                 }

+ 34 - 7
bin/drk/src/money.rs

@@ -651,20 +651,15 @@ impl Drk {
 
     /// Mark a given coin in the wallet as unspent.
     pub async fn unspend_coin(&self, coin: &Coin) -> WalletDbResult<()> {
-        let is_spend = 0;
-        let spent_height: Option<u32> = None;
         let query = format!(
-            "UPDATE {} SET {} = ?1, {} = ?2, {} = ?3 WHERE {} = ?4;",
+            "UPDATE {} SET {} = 0, {} = NULL, {} = '-' WHERE {} = ?1;",
             *MONEY_COINS_TABLE,
             MONEY_COINS_COL_IS_SPENT,
             MONEY_COINS_COL_SPENT_HEIGHT,
             MONEY_COINS_COL_SPENT_TX_HASH,
             MONEY_COINS_COL_COIN
         );
-        self.wallet.exec_sql(
-            &query,
-            rusqlite::params![is_spend, spent_height, "-", serialize_async(&coin.inner()).await],
-        )
+        self.wallet.exec_sql(&query, rusqlite::params![serialize_async(&coin.inner()).await])
     }
 
     /// Fetch the Money Merkle tree from the cache.
@@ -1120,6 +1115,38 @@ impl Drk {
         Ok(())
     }
 
+    /// Remove the Money coins in the wallet that were created after
+    /// provided height.
+    pub fn remove_money_coins_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Removing coins after: {height}");
+        let query = format!(
+            "DELETE FROM {} WHERE {} > ?1;",
+            *MONEY_COINS_TABLE, MONEY_COINS_COL_CREATION_HEIGHT
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![height])?;
+        println!("Successfully removed coins");
+
+        Ok(())
+    }
+
+    /// Mark the Money coins in the wallet that were spent after
+    /// provided height as unspent.
+    pub fn unspent_money_coins_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Unspenting coins after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = 0, {} = NULL, {} = '=' WHERE {} > ?1;",
+            *MONEY_COINS_TABLE,
+            MONEY_COINS_COL_IS_SPENT,
+            MONEY_COINS_COL_SPENT_HEIGHT,
+            MONEY_COINS_COL_SPENT_TX_HASH,
+            MONEY_COINS_COL_SPENT_HEIGHT
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully unspent coins");
+
+        Ok(())
+    }
+
     /// Retrieve token by provided string.
     /// Input string represents either an alias or a token id.
     pub async fn get_token(&self, input: String) -> Result<TokenId> {

+ 3 - 3
bin/drk/src/rpc.rs

@@ -239,7 +239,7 @@ impl Drk {
                         // Check if a reorg block was received, to reset to its previous
                         if block.header.height <= last_scanned_height {
                             let reset_height = block.header.height.saturating_sub(1);
-                            if let Err(e) = self.reset_to_height(reset_height).await {
+                            if let Err(e) = self.reset_to_height(reset_height) {
                                 return Err(Error::DatabaseError(format!(
                                     "[subscribe_blocks] Wallet state reset failed: {e:?}"
                                 )))
@@ -483,7 +483,7 @@ impl Drk {
 
                 // Reset to its height
                 println!("Last common block found: {height} - {scanned_block_hash}");
-                self.reset_to_height(height).await?;
+                self.reset_to_height(height)?;
                 break
             }
         }
@@ -491,7 +491,7 @@ impl Drk {
         // If last scanned block is genesis(0) we reset,
         // otherwise continue with the next block height.
         if height == 0 {
-            self.reset().await?;
+            self.reset()?;
         } else {
             height += 1;
         }

+ 72 - 12
bin/drk/src/scanned_blocks.rs

@@ -19,6 +19,7 @@
 use darkfi_serial::deserialize;
 
 use crate::{
+    cache::CacheOverlay,
     error::{WalletDbError, WalletDbResult},
     Drk,
 };
@@ -88,16 +89,15 @@ impl Drk {
 
     /// Reset state to provided block height.
     /// If genesis block height(0) was provided, perform a full reset.
-    pub async fn reset_to_height(&self, height: u32) -> WalletDbResult<()> {
+    pub fn reset_to_height(&self, height: u32) -> WalletDbResult<()> {
         println!("Resetting wallet state to block: {height}");
 
         // If genesis block height(0) was provided,
         // perform a full reset.
         if height == 0 {
-            return self.reset().await
+            return self.reset()
         }
-        // TODO
-        /*
+
         // Grab last scanned block height
         let (last, _) = self.get_last_scanned_block()?;
 
@@ -107,15 +107,75 @@ impl Drk {
             return Ok(())
         }
 
-        // Iterate the range (height, last] in reverse to grab the corresponding blocks
-        for height in (height + 1..=last).rev() {
-            let (height, hash, query) = self.get_scanned_block_record(height)?;
-            println!("Reverting block: {height} - {hash}");
-            self.wallet.exec_batch_sql(&query)?;
-            let query = format!("DELETE FROM {WALLET_SCANNED_BLOCKS_TABLE} WHERE {WALLET_SCANNED_BLOCKS_COL_HEIGH} = {height};");
-            self.wallet.exec_batch_sql(&query)?;
+        // Grab all state inverse diffs until requested height,
+        // going backwards.
+        let heights: Vec<u32> = (height + 1..=last).rev().collect();
+        let inverse_diffs = match self.cache.get_state_inverse_diff(&heights) {
+            Ok(d) => d,
+            Err(e) => {
+                println!(
+                    "[reset_to_height] Retrieving state inverse diffs from cache failed: {e:?}"
+                );
+                return Err(WalletDbError::GenericError)
+            }
+        };
+
+        // Create an overlay to apply the reverse diffs
+        let mut overlay = match CacheOverlay::new(&self.cache) {
+            Ok(o) => o,
+            Err(e) => {
+                println!("[reset_to_height] Creating cache overlay failed: {e:?}");
+                return Err(WalletDbError::GenericError)
+            }
+        };
+
+        // Apply the inverse diffs sequence
+        for inverse_diff in inverse_diffs {
+            if let Err(e) = overlay.0.add_diff(&inverse_diff) {
+                println!("[reset_to_height] Adding state inverse diff to the cache overlay failed: {e:?}");
+                return Err(WalletDbError::GenericError)
+            }
+            if let Err(e) = overlay.0.apply_diff(&inverse_diff) {
+                println!("[reset_to_height] Applying state inverse diff to the cache overlay failed: {e:?}");
+                return Err(WalletDbError::GenericError)
+            }
+            if let Err(e) = self.cache.sled_db.flush() {
+                println!("[reset_to_height] Flushing cache sled database failed: {e:?}");
+                return Err(WalletDbError::GenericError)
+            }
         }
-        */
+
+        // Remove all wallet coins created after the reset height
+        self.remove_money_coins_after(&height)?;
+
+        // Unspent all wallet coins spent after the reset height
+        self.unspent_money_coins_after(&height)?;
+
+        // Unfreeze tokens mint authorities frozen after the reset
+        // height.
+        self.unfreeze_mint_authorities_after(&height)?;
+
+        // Unconfirm DAOs minted after the reset height
+        self.unconfirm_daos_after(&height)?;
+
+        // Unconfirm DAOs proposals minted after the reset height
+        self.unconfirm_dao_proposals_after(&height)?;
+
+        // Reset execution information for DAOs proposals executed
+        // after the reset height.
+        self.unexec_dao_proposals_after(&height)?;
+
+        // Remove all DAOs proposals votes created after the reset
+        // height.
+        self.remove_dao_votes_after(&height)?;
+
+        // Unfreeze all contracts frozen after the reset height
+        self.unfreeze_deploy_authorities_after(&height)?;
+
+        // Set reverted status to all transactions executed after reset
+        // height.
+        self.revert_transactions_after(&height)?;
+
         println!("Successfully reset wallet state");
         Ok(())
     }

+ 20 - 4
bin/drk/src/token.rs

@@ -177,13 +177,29 @@ impl Drk {
     /// Reset all token mint authorities frozen status in the wallet.
     pub fn reset_mint_authorities(&self) -> WalletDbResult<()> {
         println!("Resetting mint authorities frozen status");
-        let freeze_height: Option<u32> = None;
         let query = format!(
-            "UPDATE {} SET {} = 0, {} = ?1",
+            "UPDATE {} SET {} = 0, {} = NULL;",
             *MONEY_TOKENS_TABLE, MONEY_TOKENS_COL_IS_FROZEN, MONEY_TOKENS_COL_FREEZE_HEIGHT
         );
-        self.wallet.exec_sql(&query, rusqlite::params![freeze_height])?;
-        println!("Successfully mint authorities frozen status");
+        self.wallet.exec_sql(&query, &[])?;
+        println!("Successfully reset mint authorities frozen status");
+
+        Ok(())
+    }
+
+    /// Remove token mint authorities frozen status in the wallet that
+    /// where frozen after provided height.
+    pub fn unfreeze_mint_authorities_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Resetting mint authorities frozen status after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = 0, {} = NULL WHERE {} > ?1;",
+            *MONEY_TOKENS_TABLE,
+            MONEY_TOKENS_COL_IS_FROZEN,
+            MONEY_TOKENS_COL_FREEZE_HEIGHT,
+            MONEY_TOKENS_COL_FREEZE_HEIGHT
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully reset mint authorities frozen status");
 
         Ok(())
     }

+ 17 - 0
bin/drk/src/txs_history.rs

@@ -183,6 +183,23 @@ impl Drk {
         Ok(())
     }
 
+    /// Set reverted status to the transaction history records in the
+    /// wallet that where executed after provided height.
+    pub fn revert_transactions_after(&self, height: &u32) -> WalletDbResult<()> {
+        println!("Reverting transactions history after: {height}");
+        let query = format!(
+            "UPDATE {} SET {} = 'Reverted', {} = NULL WHERE {} > ?1;",
+            WALLET_TXS_HISTORY_TABLE,
+            WALLET_TXS_HISTORY_COL_STATUS,
+            WALLET_TXS_HISTORY_BLOCK_HEIGHT,
+            WALLET_TXS_HISTORY_BLOCK_HEIGHT
+        );
+        self.wallet.exec_sql(&query, rusqlite::params![Some(*height)])?;
+        println!("Successfully reverted transactions history");
+
+        Ok(())
+    }
+
     /// Remove the transaction history records in the wallet
     /// that have been reverted.
     pub fn remove_reverted_txs(&self) -> WalletDbResult<()> {