txs_history.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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 lazy_static::lazy_static;
  19. use rusqlite::types::Value;
  20. use darkfi::{tx::Transaction, util::encoding::base64, Error, Result};
  21. use darkfi_sdk::crypto::MONEY_CONTRACT_ID;
  22. use darkfi_serial::{deserialize_async, serialize_async};
  23. use crate::{
  24. convert_named_params,
  25. error::{WalletDbError, WalletDbResult},
  26. Drk,
  27. };
  28. // Wallet SQL table constant names. These have to represent the `wallet.sql`
  29. // SQL schema. Table names are prefixed with the contract ID to avoid collisions.
  30. lazy_static! {
  31. pub static ref WALLET_TXS_HISTORY_TABLE: String =
  32. format!("{}_transactions_history", MONEY_CONTRACT_ID.to_string());
  33. }
  34. const WALLET_TXS_HISTORY_COL_TX_HASH: &str = "transaction_hash";
  35. const WALLET_TXS_HISTORY_COL_STATUS: &str = "status";
  36. const WALLET_TXS_HISTORY_COL_TX: &str = "tx";
  37. impl Drk {
  38. /// Insert a [`Transaction`] history record into the wallet.
  39. pub async fn insert_tx_history_record(&self, tx: &Transaction) -> WalletDbResult<()> {
  40. let query = format!(
  41. "INSERT INTO {} ({}, {}, {}) VALUES (?1, ?2, ?3);",
  42. *WALLET_TXS_HISTORY_TABLE,
  43. WALLET_TXS_HISTORY_COL_TX_HASH,
  44. WALLET_TXS_HISTORY_COL_STATUS,
  45. WALLET_TXS_HISTORY_COL_TX,
  46. );
  47. let tx_hash = tx.hash();
  48. self.wallet
  49. .exec_sql(
  50. &query,
  51. rusqlite::params![
  52. tx_hash.to_string(),
  53. "Broadcasted",
  54. base64::encode(&serialize_async(tx).await),
  55. ],
  56. )
  57. .await
  58. }
  59. /// Get a transaction history record.
  60. pub async fn get_tx_history_record(
  61. &self,
  62. tx_hash: &str,
  63. ) -> Result<(String, String, Transaction)> {
  64. let row = match self
  65. .wallet
  66. .query_single(
  67. &WALLET_TXS_HISTORY_TABLE,
  68. &[],
  69. convert_named_params! {(WALLET_TXS_HISTORY_COL_TX_HASH, tx_hash)},
  70. )
  71. .await
  72. {
  73. Ok(r) => r,
  74. Err(e) => {
  75. return Err(Error::RusqliteError(format!(
  76. "[get_tx_history_record] Transaction history record retrieval failed: {e:?}"
  77. )))
  78. }
  79. };
  80. let Value::Text(ref tx_hash) = row[0] else {
  81. return Err(Error::ParseFailed(
  82. "[get_tx_history_record] Transaction hash parsing failed",
  83. ))
  84. };
  85. let tx_hash = tx_hash.clone();
  86. let Value::Text(ref status) = row[1] else {
  87. return Err(Error::ParseFailed("[get_tx_history_record] Status parsing failed"))
  88. };
  89. let status = status.clone();
  90. let Value::Text(ref tx_encoded) = row[2] else {
  91. return Err(Error::ParseFailed(
  92. "[get_tx_history_record] Encoded transaction parsing failed",
  93. ))
  94. };
  95. let Some(tx_bytes) = base64::decode(tx_encoded) else {
  96. return Err(Error::ParseFailed(
  97. "[get_tx_history_record] Encoded transaction parsing failed",
  98. ))
  99. };
  100. let tx: Transaction = deserialize_async(&tx_bytes).await?;
  101. Ok((tx_hash, status, tx))
  102. }
  103. /// Fetch all transactions history records, excluding bytes column.
  104. pub async fn get_txs_history(&self) -> WalletDbResult<Vec<(String, String)>> {
  105. let rows = self
  106. .wallet
  107. .query_multiple(
  108. &WALLET_TXS_HISTORY_TABLE,
  109. &[WALLET_TXS_HISTORY_COL_TX_HASH, WALLET_TXS_HISTORY_COL_STATUS],
  110. &[],
  111. )
  112. .await?;
  113. let mut ret = Vec::with_capacity(rows.len());
  114. for row in rows {
  115. let Value::Text(ref tx_hash) = row[0] else {
  116. return Err(WalletDbError::ParseColumnValueError)
  117. };
  118. let tx_hash = tx_hash.clone();
  119. let Value::Text(ref status) = row[1] else {
  120. return Err(WalletDbError::ParseColumnValueError)
  121. };
  122. let status = status.clone();
  123. ret.push((tx_hash, status));
  124. }
  125. Ok(ret)
  126. }
  127. /// Update a transactions history record status to the given one.
  128. pub async fn update_tx_history_record_status(
  129. &self,
  130. tx_hash: &str,
  131. status: &str,
  132. ) -> WalletDbResult<()> {
  133. let query = format!(
  134. "UPDATE {} SET {} = ?1 WHERE {} = ?2;",
  135. *WALLET_TXS_HISTORY_TABLE,
  136. WALLET_TXS_HISTORY_COL_STATUS,
  137. WALLET_TXS_HISTORY_COL_TX_HASH,
  138. );
  139. self.wallet.exec_sql(&query, rusqlite::params![status, tx_hash]).await
  140. }
  141. /// Update given transactions history record statuses to the given one.
  142. pub async fn update_tx_history_records_status(
  143. &self,
  144. txs: &Vec<Transaction>,
  145. status: &str,
  146. ) -> WalletDbResult<()> {
  147. if txs.is_empty() {
  148. return Ok(())
  149. }
  150. let mut txs_hashes = Vec::with_capacity(txs.len());
  151. for tx in txs {
  152. let tx_hash = tx.hash();
  153. txs_hashes.push(format!("{tx_hash}"));
  154. }
  155. let txs_hashes_string = format!("{:?}", txs_hashes).replace('[', "(").replace(']', ")");
  156. let query = format!(
  157. "UPDATE {} SET {} = ?1 WHERE {} IN {};",
  158. *WALLET_TXS_HISTORY_TABLE,
  159. WALLET_TXS_HISTORY_COL_STATUS,
  160. WALLET_TXS_HISTORY_COL_TX_HASH,
  161. txs_hashes_string
  162. );
  163. self.wallet.exec_sql(&query, rusqlite::params![status]).await
  164. }
  165. /// Update all transaction history records statuses to the given one.
  166. pub async fn update_all_tx_history_records_status(&self, status: &str) -> WalletDbResult<()> {
  167. let query = format!(
  168. "UPDATE {} SET {} = ?1",
  169. *WALLET_TXS_HISTORY_TABLE, WALLET_TXS_HISTORY_COL_STATUS,
  170. );
  171. self.wallet.exec_sql(&query, rusqlite::params![status]).await
  172. }
  173. }