txs_history.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use rusqlite::types::Value;
  19. use darkfi::{tx::Transaction, Error, Result};
  20. use darkfi_serial::{deserialize_async, serialize_async};
  21. use crate::{
  22. convert_named_params,
  23. error::{WalletDbError, WalletDbResult},
  24. Drk,
  25. };
  26. // Wallet SQL table constant names. These have to represent the `wallet.sql`
  27. // SQL schema.
  28. const WALLET_TXS_HISTORY_TABLE: &str = "transactions_history";
  29. const WALLET_TXS_HISTORY_COL_TX_HASH: &str = "transaction_hash";
  30. const WALLET_TXS_HISTORY_COL_STATUS: &str = "status";
  31. const WALLET_TXS_HISTORY_COL_TX: &str = "tx";
  32. impl Drk {
  33. /// Insert or update a `Transaction` history record into the wallet,
  34. /// with the provided status, and store its inverse query into the cache.
  35. pub async fn put_tx_history_record(
  36. &self,
  37. tx: &Transaction,
  38. status: &str,
  39. ) -> WalletDbResult<String> {
  40. // Create an SQL `INSERT OR REPLACE` query
  41. let query = format!(
  42. "INSERT OR REPLACE INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
  43. WALLET_TXS_HISTORY_TABLE,
  44. WALLET_TXS_HISTORY_COL_TX_HASH,
  45. WALLET_TXS_HISTORY_COL_STATUS,
  46. WALLET_TXS_HISTORY_COL_TX,
  47. );
  48. // Create its inverse query
  49. let tx_hash = tx.hash().to_string();
  50. // We only need to set the transaction status to "Reverted"
  51. let inverse = self.wallet.create_prepared_statement(
  52. &format!(
  53. "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
  54. WALLET_TXS_HISTORY_TABLE,
  55. WALLET_TXS_HISTORY_COL_STATUS,
  56. WALLET_TXS_HISTORY_COL_TX_HASH
  57. ),
  58. rusqlite::params!["Reverted", tx_hash],
  59. )?;
  60. // Execute the query
  61. self.wallet
  62. .exec_sql(&query, rusqlite::params![tx_hash, status, &serialize_async(tx).await,])?;
  63. // Store its inverse
  64. self.wallet.cache_inverse(inverse)?;
  65. Ok(tx_hash)
  66. }
  67. /// Insert or update a slice of [`Transaction`] history records into the wallet,
  68. /// with the provided status.
  69. pub async fn put_tx_history_records(
  70. &self,
  71. txs: &[&Transaction],
  72. status: &str,
  73. ) -> WalletDbResult<Vec<String>> {
  74. let mut ret = Vec::with_capacity(txs.len());
  75. for tx in txs {
  76. ret.push(self.put_tx_history_record(tx, status).await?);
  77. }
  78. Ok(ret)
  79. }
  80. /// Get a transaction history record.
  81. pub async fn get_tx_history_record(
  82. &self,
  83. tx_hash: &str,
  84. ) -> Result<(String, String, Transaction)> {
  85. let row = match self.wallet.query_single(
  86. WALLET_TXS_HISTORY_TABLE,
  87. &[],
  88. convert_named_params! {(WALLET_TXS_HISTORY_COL_TX_HASH, tx_hash)},
  89. ) {
  90. Ok(r) => r,
  91. Err(e) => {
  92. return Err(Error::DatabaseError(format!(
  93. "[get_tx_history_record] Transaction history record retrieval failed: {e:?}"
  94. )))
  95. }
  96. };
  97. let Value::Text(ref tx_hash) = row[0] else {
  98. return Err(Error::ParseFailed(
  99. "[get_tx_history_record] Transaction hash parsing failed",
  100. ))
  101. };
  102. let Value::Text(ref status) = row[1] else {
  103. return Err(Error::ParseFailed("[get_tx_history_record] Status parsing failed"))
  104. };
  105. let Value::Blob(ref bytes) = row[2] else {
  106. return Err(Error::ParseFailed(
  107. "[get_tx_history_record] Transaction bytes parsing failed",
  108. ))
  109. };
  110. let tx: Transaction = deserialize_async(bytes).await?;
  111. Ok((tx_hash.clone(), status.clone(), tx))
  112. }
  113. /// Fetch all transactions history records, excluding bytes column.
  114. pub fn get_txs_history(&self) -> WalletDbResult<Vec<(String, String)>> {
  115. let rows = self.wallet.query_multiple(
  116. WALLET_TXS_HISTORY_TABLE,
  117. &[WALLET_TXS_HISTORY_COL_TX_HASH, WALLET_TXS_HISTORY_COL_STATUS],
  118. &[],
  119. )?;
  120. let mut ret = Vec::with_capacity(rows.len());
  121. for row in rows {
  122. let Value::Text(ref tx_hash) = row[0] else {
  123. return Err(WalletDbError::ParseColumnValueError)
  124. };
  125. let Value::Text(ref status) = row[1] else {
  126. return Err(WalletDbError::ParseColumnValueError)
  127. };
  128. ret.push((tx_hash.clone(), status.clone()));
  129. }
  130. Ok(ret)
  131. }
  132. /// Reset the transaction history records in the wallet.
  133. pub fn reset_tx_history(&self) -> WalletDbResult<()> {
  134. println!("Resetting transactions history");
  135. let query = format!("DELETE FROM {};", WALLET_TXS_HISTORY_TABLE);
  136. self.wallet.exec_sql(&query, &[])?;
  137. println!("Successfully reset transactions history");
  138. Ok(())
  139. }
  140. /// Remove the transaction history records in the wallet
  141. /// that have been reverted.
  142. pub fn remove_reverted_txs(&self) -> WalletDbResult<()> {
  143. println!("Removing reverted transactions history records");
  144. let query = format!(
  145. "DELETE FROM {} WHERE {} = 'Reverted';",
  146. WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS
  147. );
  148. self.wallet.exec_sql(&query, &[])?;
  149. println!("Successfully removed reverted transactions history records");
  150. Ok(())
  151. }
  152. }