txs_history.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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 a `Transaction` history record into the wallet.
  34. pub async fn insert_tx_history_record(&self, tx: &Transaction) -> WalletDbResult<String> {
  35. let query = format!(
  36. "INSERT OR IGNORE INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
  37. WALLET_TXS_HISTORY_TABLE,
  38. WALLET_TXS_HISTORY_COL_TX_HASH,
  39. WALLET_TXS_HISTORY_COL_STATUS,
  40. WALLET_TXS_HISTORY_COL_TX,
  41. );
  42. let tx_hash = tx.hash().to_string();
  43. self.wallet.exec_sql(
  44. &query,
  45. rusqlite::params![tx_hash, "Broadcasted", &serialize_async(tx).await,],
  46. )?;
  47. Ok(tx_hash)
  48. }
  49. /// Insert a slice of [`Transaction`] history records into the wallet.
  50. pub async fn insert_tx_history_records(
  51. &self,
  52. txs: &[Transaction],
  53. ) -> WalletDbResult<Vec<String>> {
  54. let mut ret = Vec::with_capacity(txs.len());
  55. for tx in txs {
  56. ret.push(self.insert_tx_history_record(tx).await?);
  57. }
  58. Ok(ret)
  59. }
  60. /// Get a transaction history record.
  61. pub async fn get_tx_history_record(
  62. &self,
  63. tx_hash: &str,
  64. ) -> Result<(String, String, Transaction)> {
  65. let row = match self.wallet.query_single(
  66. WALLET_TXS_HISTORY_TABLE,
  67. &[],
  68. convert_named_params! {(WALLET_TXS_HISTORY_COL_TX_HASH, tx_hash)},
  69. ) {
  70. Ok(r) => r,
  71. Err(e) => {
  72. return Err(Error::RusqliteError(format!(
  73. "[get_tx_history_record] Transaction history record retrieval failed: {e:?}"
  74. )))
  75. }
  76. };
  77. let Value::Text(ref tx_hash) = row[0] else {
  78. return Err(Error::ParseFailed(
  79. "[get_tx_history_record] Transaction hash parsing failed",
  80. ))
  81. };
  82. let Value::Text(ref status) = row[1] else {
  83. return Err(Error::ParseFailed("[get_tx_history_record] Status parsing failed"))
  84. };
  85. let Value::Blob(ref bytes) = row[2] else {
  86. return Err(Error::ParseFailed(
  87. "[get_tx_history_record] Transaction bytes parsing failed",
  88. ))
  89. };
  90. let tx: Transaction = deserialize_async(bytes).await?;
  91. Ok((tx_hash.clone(), status.clone(), tx))
  92. }
  93. /// Fetch all transactions history records, excluding bytes column.
  94. pub fn get_txs_history(&self) -> WalletDbResult<Vec<(String, String)>> {
  95. let rows = self.wallet.query_multiple(
  96. WALLET_TXS_HISTORY_TABLE,
  97. &[WALLET_TXS_HISTORY_COL_TX_HASH, WALLET_TXS_HISTORY_COL_STATUS],
  98. &[],
  99. )?;
  100. let mut ret = Vec::with_capacity(rows.len());
  101. for row in rows {
  102. let Value::Text(ref tx_hash) = row[0] else {
  103. return Err(WalletDbError::ParseColumnValueError)
  104. };
  105. let Value::Text(ref status) = row[1] else {
  106. return Err(WalletDbError::ParseColumnValueError)
  107. };
  108. ret.push((tx_hash.clone(), status.clone()));
  109. }
  110. Ok(ret)
  111. }
  112. /// Update given transactions history record statuses to the given one.
  113. pub fn update_tx_history_records_status(
  114. &self,
  115. txs_hashes: &[String],
  116. status: &str,
  117. ) -> WalletDbResult<()> {
  118. if txs_hashes.is_empty() {
  119. return Ok(())
  120. }
  121. let txs_hashes_string = format!("{:?}", txs_hashes).replace('[', "(").replace(']', ")");
  122. let query = format!(
  123. "UPDATE {} SET {} = ?1 WHERE {} IN {};",
  124. WALLET_TXS_HISTORY_TABLE,
  125. WALLET_TXS_HISTORY_COL_STATUS,
  126. WALLET_TXS_HISTORY_COL_TX_HASH,
  127. txs_hashes_string
  128. );
  129. self.wallet.exec_sql(&query, rusqlite::params![status])
  130. }
  131. /// Update all transaction history records statuses to the given one.
  132. pub fn update_all_tx_history_records_status(&self, status: &str) -> WalletDbResult<()> {
  133. let query = format!(
  134. "UPDATE {} SET {} = ?1",
  135. WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS,
  136. );
  137. self.wallet.exec_sql(&query, rusqlite::params![status])
  138. }
  139. }