transactions.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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 log::info;
  19. use tinyjson::JsonValue;
  20. use darkfi::{
  21. blockchain::{
  22. HeaderHash, SLED_PENDING_TX_ORDER_TREE, SLED_PENDING_TX_TREE, SLED_TX_LOCATION_TREE,
  23. SLED_TX_TREE,
  24. },
  25. tx::Transaction,
  26. Error, Result,
  27. };
  28. use darkfi_sdk::tx::TransactionHash;
  29. use crate::ExplorerDb;
  30. #[derive(Debug, Clone)]
  31. /// Structure representing a `TRANSACTIONS_TABLE` record.
  32. pub struct TransactionRecord {
  33. /// Transaction hash identifier
  34. pub transaction_hash: String,
  35. /// Header hash identifier of the block this transaction was included in
  36. pub header_hash: String,
  37. // TODO: Split the payload into a more easily readable fields
  38. /// Transaction payload
  39. pub payload: Transaction,
  40. }
  41. impl TransactionRecord {
  42. /// Auxiliary function to convert a `TransactionRecord` into a `JsonValue` array.
  43. pub fn to_json_array(&self) -> JsonValue {
  44. JsonValue::Array(vec![
  45. JsonValue::String(self.transaction_hash.clone()),
  46. JsonValue::String(self.header_hash.clone()),
  47. JsonValue::String(format!("{:?}", self.payload)),
  48. ])
  49. }
  50. }
  51. impl From<(&String, &Transaction)> for TransactionRecord {
  52. fn from((header_hash, transaction): (&String, &Transaction)) -> Self {
  53. Self {
  54. transaction_hash: transaction.hash().to_string(),
  55. header_hash: header_hash.clone(),
  56. payload: transaction.clone(),
  57. }
  58. }
  59. }
  60. impl ExplorerDb {
  61. /// Resets transactions in the database by clearing transaction-related trees, returning an Ok result on success.
  62. pub fn reset_transactions(&self) -> Result<()> {
  63. // Initialize transaction trees to reset
  64. let trees_to_reset =
  65. [SLED_TX_TREE, SLED_TX_LOCATION_TREE, SLED_PENDING_TX_TREE, SLED_PENDING_TX_ORDER_TREE];
  66. // Iterate over each associated transaction tree and delete its contents
  67. for tree_name in &trees_to_reset {
  68. let tree = &self.blockchain.sled_db.open_tree(tree_name)?;
  69. tree.clear()?;
  70. let tree_name_str = std::str::from_utf8(tree_name)?;
  71. info!(target: "blockchain-explorer::blocks", "Successfully reset transaction tree: {tree_name_str}");
  72. }
  73. Ok(())
  74. }
  75. /// Provides the transaction count of all the transactions in the explorer database.
  76. pub fn get_transaction_count(&self) -> usize {
  77. self.blockchain.txs_len()
  78. }
  79. /// Fetch all known transactions from the database.
  80. pub fn get_transactions(&self) -> Result<Vec<TransactionRecord>> {
  81. // Retrieve all transactions and handle any errors encountered
  82. let transactions = self.blockchain.transactions.get_all().map_err(|e| {
  83. Error::DatabaseError(format!("[get_transactions] Trxs retrieval: {e:?}"))
  84. })?;
  85. // Transform the found transactions into a vector of transaction records
  86. let transaction_records: Vec<TransactionRecord> = transactions
  87. .iter()
  88. .map(|(tx_hash, tx)| TransactionRecord::from((&tx_hash.as_string(), tx)))
  89. .collect();
  90. Ok(transaction_records)
  91. }
  92. /// Fetch all transactions from the database for the given block header hash.
  93. pub fn get_transactions_by_header_hash(
  94. &self,
  95. header_hash: &str,
  96. ) -> Result<Vec<TransactionRecord>> {
  97. // Parse header hash, returning an error if parsing fails
  98. let header_hash = header_hash
  99. .parse::<HeaderHash>()
  100. .map_err(|_| Error::ParseFailed("[get_transactions_by_header_hash] Invalid hash"))?;
  101. // Fetch block by hash and handle encountered errors
  102. let blocks = match self.blockchain.get_blocks_by_hash(&[header_hash]) {
  103. Ok(blocks) => blocks,
  104. Err(Error::BlockNotFound(_)) => return Ok(vec![]),
  105. Err(e) => {
  106. return Err(Error::DatabaseError(format!(
  107. "[get_transactions_by_header_hash] Block retrieval failed: {e:?}"
  108. )))
  109. }
  110. };
  111. // Transform block transactions into transaction records
  112. Ok(blocks[0]
  113. .txs
  114. .iter()
  115. .map(|tx| TransactionRecord::from((&blocks[0].header.hash().as_string(), tx)))
  116. .collect::<Vec<TransactionRecord>>())
  117. }
  118. /// Fetch a transaction given its header hash.
  119. pub fn get_transaction_by_hash(
  120. &self,
  121. tx_hash: &TransactionHash,
  122. ) -> Result<Option<TransactionRecord>> {
  123. let tx_store = &self.blockchain.transactions;
  124. // Attempt to retrieve the transaction using the provided hash handling any potential errors
  125. let txs = tx_store.get(&[*tx_hash], false).map_err(|e| {
  126. Error::DatabaseError(format!(
  127. "[get_transaction_by_hash] Transaction retrieval failed: {e:?}"
  128. ))
  129. })?;
  130. // Check if transaction was found
  131. if txs[0].is_none() {
  132. return Ok(None);
  133. };
  134. // Retrieve the location of the transaction to obtain its header hash
  135. let (block_height, _) = tx_store.get_location(&[*tx_hash], true).map_err(|e| {
  136. Error::DatabaseError(format!(
  137. "[get_transaction_by_hash] Location retrieval failed: {e:?}"
  138. ))
  139. })?[0]
  140. .unwrap();
  141. // Retrieve the block corresponding to the transaction's height
  142. let header_hash =
  143. &self.blockchain.blocks.get_order(&[block_height], true).map_err(|e| {
  144. Error::DatabaseError(format!(
  145. "[get_transaction_by_hash] Block retrieval failed: {e:?}"
  146. ))
  147. })?[0]
  148. .unwrap();
  149. // Transform the transaction into a TransactionRecord
  150. Ok(Some(TransactionRecord::from((&header_hash.as_string(), txs[0].as_ref().unwrap()))))
  151. }
  152. }