| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2026 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use darkfi_serial::deserialize;
- use crate::{
- cache::CacheOverlay,
- dao::{SLED_MERKLE_TREES_DAO_DAOS, SLED_MERKLE_TREES_DAO_PROPOSALS},
- error::{WalletDbError, WalletDbResult},
- money::SLED_MERKLE_TREES_MONEY,
- Drk,
- };
- impl Drk {
- /// Get a scanned block information record.
- pub fn get_scanned_block(&self, height: &u32) -> WalletDbResult<(String, String)> {
- let Ok(query_result) = self.cache.scanned_blocks.get(height.to_be_bytes()) else {
- return Err(WalletDbError::QueryExecutionFailed);
- };
- let Some(value_bytes) = query_result else {
- return Err(WalletDbError::RowNotFound);
- };
- let Ok((hash, signing_key)) = deserialize(&value_bytes) else {
- return Err(WalletDbError::ParseColumnValueError);
- };
- Ok((hash, signing_key))
- }
- /// Fetch all scanned block information records.
- pub fn get_scanned_block_records(&self) -> WalletDbResult<Vec<(u32, String, String)>> {
- let mut scanned_blocks = vec![];
- for record in self.cache.scanned_blocks.iter() {
- let Ok((key, value)) = record else {
- return Err(WalletDbError::QueryExecutionFailed);
- };
- let key: [u8; 4] = match key.as_ref().try_into() {
- Ok(k) => k,
- Err(_) => return Err(WalletDbError::ParseColumnValueError),
- };
- let key = u32::from_be_bytes(key);
- let Ok((hash, signing_key)) = deserialize(&value) else {
- return Err(WalletDbError::ParseColumnValueError);
- };
- scanned_blocks.push((key, hash, signing_key));
- }
- Ok(scanned_blocks)
- }
- /// Get the last scanned block height and hash from the wallet.
- /// If database is empty default (0, '-') is returned.
- pub fn get_last_scanned_block(&self) -> WalletDbResult<(u32, String)> {
- let Ok(query_result) = self.cache.scanned_blocks.last() else {
- return Err(WalletDbError::QueryExecutionFailed);
- };
- let Some((key, value)) = query_result else { return Ok((0, String::from("-"))) };
- let key: [u8; 4] = match key.as_ref().try_into() {
- Ok(k) => k,
- Err(_) => return Err(WalletDbError::ParseColumnValueError),
- };
- let key = u32::from_be_bytes(key);
- let Ok((hash, _)) = deserialize::<(String, String)>(&value) else {
- return Err(WalletDbError::ParseColumnValueError);
- };
- Ok((key, hash))
- }
- /// Reset the scanned blocks information records in the cache.
- pub fn reset_scanned_blocks(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
- output.push(String::from("Resetting scanned blocks"));
- if let Err(e) = self.cache.scanned_blocks.clear() {
- output
- .push(format!("[reset_scanned_blocks] Resetting scanned blocks tree failed: {e}"));
- return Err(WalletDbError::GenericError)
- }
- if let Err(e) = self.cache.state_inverse_diff.clear() {
- output.push(format!(
- "[reset_scanned_blocks] Resetting state inverse diffs tree failed: {e}"
- ));
- return Err(WalletDbError::GenericError)
- }
- output.push(String::from("Successfully reset scanned blocks"));
- Ok(())
- }
- /// 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,
- output: &mut Vec<String>,
- ) -> WalletDbResult<()> {
- output.push(format!("Resetting wallet state to block: {height}"));
- // If genesis block height(0) was provided,
- // perform a full reset.
- if height == 0 {
- return self.reset(output)
- }
- // Grab last scanned block height
- let (last, _) = self.get_last_scanned_block()?;
- // Check if requested height is after it
- if last <= height {
- output.push(String::from(
- "Requested block height is greater or equal to last scanned block",
- ));
- return Ok(())
- }
- // Grab our current merkle trees
- let mut money_tree = match self.get_money_tree().await {
- Ok(t) => t,
- Err(e) => {
- output.push(format!("[reset_to_height] Money merkle tree retrieval failed: {e}"));
- return Err(WalletDbError::GenericError)
- }
- };
- let (mut dao_daos_tree, mut dao_proposals_tree) = match self.get_dao_trees().await {
- Ok(p) => p,
- Err(e) => {
- output.push(format!("[reset_to_height] DAO merkle trees retrieval 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) => {
- output.push(format!("[reset_to_height] Creating cache overlay failed: {e}"));
- return Err(WalletDbError::GenericError)
- }
- };
- // Grab all state inverse diffs until requested height,
- // going backwards.
- for height in (height + 1..=last).rev() {
- let inverse_diff = match self.cache.get_state_inverse_diff(&height) {
- Ok(d) => d,
- Err(e) => {
- output.push(format!(
- "[reset_to_height] Retrieving state inverse diff from cache failed: {e}"
- ));
- return Err(WalletDbError::GenericError)
- }
- };
- // Apply it
- if let Err(e) = overlay.0.add_diff(&inverse_diff) {
- output.push(format!(
- "[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) {
- output.push(format!("[reset_to_height] Applying state inverse diff to the cache overlay failed: {e}"));
- return Err(WalletDbError::GenericError)
- }
- // Remove it
- if let Err(e) = self.cache.state_inverse_diff.remove(height.to_be_bytes()) {
- output.push(format!(
- "[reset_to_height] Removing state inverse diff from the cache failed: {e}"
- ));
- return Err(WalletDbError::GenericError)
- }
- // Rewind and update the merkle trees
- money_tree.rewind();
- dao_daos_tree.rewind();
- dao_proposals_tree.rewind();
- if let Err(e) = self.cache.insert_merkle_trees(&[
- (SLED_MERKLE_TREES_MONEY, &money_tree),
- (SLED_MERKLE_TREES_DAO_DAOS, &dao_daos_tree),
- (SLED_MERKLE_TREES_DAO_PROPOSALS, &dao_proposals_tree),
- ]) {
- output.push(format!("[reset_to_height] Updating merkle trees failed: {e}"));
- return Err(WalletDbError::GenericError)
- };
- // Flush sled
- if let Err(e) = self.cache.sled_db.flush() {
- output.push(format!("[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, output)?;
- // Unspent all wallet coins spent after the reset height
- self.unspent_money_coins_after(&height, output)?;
- // Unfreeze tokens mint authorities frozen after the reset
- // height.
- self.unfreeze_mint_authorities_after(&height, output)?;
- // Unconfirm DAOs minted after the reset height
- self.unconfirm_daos_after(&height, output)?;
- // Unconfirm DAOs proposals minted after the reset height
- self.unconfirm_dao_proposals_after(&height, output)?;
- // Reset execution information for DAOs proposals executed
- // after the reset height.
- self.unexec_dao_proposals_after(&height, output)?;
- // Remove all DAOs proposals votes created after the reset
- // height.
- self.remove_dao_votes_after(&height, output)?;
- // Unlock all contracts frozen after the reset height
- self.unlock_deploy_authorities_after(&height, output)?;
- // Remove all contracts history records created after the reset
- // height.
- self.remove_deploy_history_after(&height, output)?;
- // Set reverted status to all transactions executed after reset
- // height.
- self.revert_transactions_after(&height, output)?;
- output.push(String::from("Successfully reset wallet state"));
- Ok(())
- }
- }
|