drk: use block references to reset stuff

This commit is contained in:
skoupidi
2025-06-07 18:33:42 +03:00
parent 1d1303dff2
commit fa3b2b8277
10 changed files with 276 additions and 36 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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