/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2024 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 . */ use std::{fs, sync::Arc}; use url::Url; use darkfi::{rpc::client::RpcClient, util::path::expand_path, Error, Result}; /// Error codes pub mod error; use error::{WalletDbError, WalletDbResult}; /// darkfid JSON-RPC related methods pub mod rpc; /// Payment methods pub mod transfer; /// Swap methods pub mod swap; /// Token methods pub mod token; /// CLI utility functions pub mod cli_util; /// Wallet functionality related to Money pub mod money; /// Wallet functionality related to Dao pub mod dao; /// Wallet functionality related to Deployooor pub mod deploy; /// Wallet functionality related to transactions history pub mod txs_history; /// Wallet functionality related to scanned blocks pub mod scanned_blocks; /// Wallet database operations handler pub mod walletdb; use walletdb::{WalletDb, WalletPtr}; /// CLI-util structure pub struct Drk { /// Wallet database operations handler pub wallet: WalletPtr, /// JSON-RPC client to execute requests to darkfid daemon pub rpc_client: Option, /// Flag indicating if fun stuff are enabled pub fun: bool, } impl Drk { pub async fn new( wallet_path: String, wallet_pass: String, endpoint: Option, ex: Arc>, fun: bool, ) -> Result { // Initialize wallet let wallet_path = expand_path(&wallet_path)?; if !wallet_path.exists() { if let Some(parent) = wallet_path.parent() { fs::create_dir_all(parent)?; } } let Ok(wallet) = WalletDb::new(Some(wallet_path), Some(&wallet_pass)) else { return Err(Error::DatabaseError(format!("{}", WalletDbError::InitializationFailed))); }; // Initialize rpc client let rpc_client = if let Some(endpoint) = endpoint { Some(RpcClient::new(endpoint, ex).await?) } else { None }; Ok(Self { wallet, rpc_client, fun }) } /// Initialize wallet with tables for `Drk`. pub async fn initialize_wallet(&self) -> WalletDbResult<()> { // Initialize wallet schema self.wallet.exec_batch_sql(include_str!("../wallet.sql"))?; Ok(()) } /// Auxiliary function to completely reset wallet state. pub async fn reset(&self) -> WalletDbResult<()> { println!("Resetting full wallet state"); self.reset_scanned_blocks()?; self.reset_money_tree().await?; 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_votes()?; self.reset_tx_history()?; println!("Successfully reset full wallet state"); Ok(()) } /// Auxiliary function to reset `walletdb` inverse cache state. /// Additionally, set current trees state inverse queries. /// We keep the entire trees state as two distinct inverse queries, /// since we execute per transaction call, so we don't have to update /// them on each iteration. pub async fn reset_inverse_cache(&self) -> Result<()> { // Reset `walletdb` inverse cache if let Err(e) = self.wallet.clear_inverse_cache() { return Err(Error::DatabaseError(format!( "[reset_inverse_cache] Clearing wallet inverse cache failed: {e:?}" ))) } // Grab current money tree state query and insert it into inverse cache let query = self.get_money_tree_state_query().await?; if let Err(e) = self.wallet.cache_inverse(query) { return Err(Error::DatabaseError(format!( "[reset_inverse_cache] Inserting money query into inverse cache failed: {e:?}" ))) } // Grab current DAO trees state query and insert it into inverse cache let query = self.get_dao_trees_state_query().await?; if let Err(e) = self.wallet.cache_inverse(query) { return Err(Error::DatabaseError(format!( "[reset_inverse_cache] Inserting DAO query into inverse cache failed: {e:?}" ))) } Ok(()) } /// Auxiliary function to store current `walletdb` inverse cache /// in scanned blocks information for provided block height and hash. /// Additionally, clear `walletdb` inverse cache state. pub fn store_inverse_cache(&self, height: u32, hash: &str) -> Result<()> { // Grab current inverse state rollback query let rollback_query = match self.wallet.grab_inverse_cache_block() { Ok(q) => q, Err(e) => { return Err(Error::DatabaseError(format!( "[store_inverse_cache] Creating rollback query failed: {e:?}" ))) } }; // Store it as a scanned blocks information record if let Err(e) = self.put_scanned_block_record(height, hash, &rollback_query) { return Err(Error::DatabaseError(format!( "[store_inverse_cache] Inserting scanned blocks information record failed: {e:?}" ))) }; // Reset `walletdb` inverse cache if let Err(e) = self.wallet.clear_inverse_cache() { return Err(Error::DatabaseError(format!( "[store_inverse_cache] Clearing wallet inverse cache failed: {e:?}" ))) }; Ok(()) } }