garbage_collect.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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 darkfi::{error::TxVerifyFailed, validator::verification::verify_transactions, Error, Result};
  19. use darkfi_sdk::crypto::MerkleTree;
  20. use tracing::{debug, error, info};
  21. use crate::DarkfiNodePtr;
  22. /// Async task used for purging erroneous pending transactions from the nodes mempool.
  23. pub async fn garbage_collect_task(node: DarkfiNodePtr) -> Result<()> {
  24. info!(target: "darkfid::task::garbage_collect_task", "Starting garbage collection task...");
  25. // Purge all unreferenced contract trees from the database
  26. if let Err(e) = node.validator.consensus.purge_unreferenced_trees().await {
  27. error!(target: "darkfid::task::garbage_collect_task", "Purging unreferenced contract trees from the database failed: {e}");
  28. }
  29. // Grab all current unproposed transactions. We verify them in batches,
  30. // to not load them all in memory.
  31. let (mut last_checked, mut txs) =
  32. match node.validator.blockchain.transactions.get_after_pending(0, node.txs_batch_size) {
  33. Ok(pair) => pair,
  34. Err(e) => {
  35. error!(
  36. target: "darkfid::task::garbage_collect_task",
  37. "Uproposed transactions retrieval failed: {e}"
  38. );
  39. return Ok(())
  40. }
  41. };
  42. // Check if we have transactions to process
  43. if txs.is_empty() {
  44. info!(target: "darkfid::task::garbage_collect_task", "Garbage collection finished successfully!");
  45. return Ok(())
  46. }
  47. while !txs.is_empty() {
  48. // Verify each one against current forks
  49. for tx in txs {
  50. let tx_hash = tx.hash();
  51. let tx_vec = [tx.clone()];
  52. let mut valid = false;
  53. // Grab a lock over current consensus forks state
  54. let mut forks = node.validator.consensus.forks.write().await;
  55. // Iterate over them to verify transaction validity in their overlays
  56. for fork in forks.iter_mut() {
  57. // Clone forks' overlay
  58. let overlay = match fork.overlay.lock().unwrap().full_clone() {
  59. Ok(o) => o,
  60. Err(e) => {
  61. error!(
  62. target: "darkfid::task::garbage_collect_task",
  63. "Overlay full clone creation failed: {e}"
  64. );
  65. return Err(e)
  66. }
  67. };
  68. // Grab all current proposals transactions hashes
  69. let proposals_txs =
  70. match overlay.lock().unwrap().get_blocks_txs_hashes(&fork.proposals) {
  71. Ok(txs) => txs,
  72. Err(e) => {
  73. error!(
  74. target: "darkfid::task::garbage_collect_task",
  75. "Proposal transactions retrieval failed: {e}"
  76. );
  77. return Err(e)
  78. }
  79. };
  80. // If the hash is contained in the proposals transactions vec, skip it
  81. if proposals_txs.contains(&tx_hash) {
  82. continue
  83. }
  84. // Grab forks' next block height
  85. let next_block_height = match fork.get_next_block_height() {
  86. Ok(h) => h,
  87. Err(e) => {
  88. error!(
  89. target: "darkfid::task::garbage_collect_task",
  90. "Next fork block height retrieval failed: {e}"
  91. );
  92. return Err(e)
  93. }
  94. };
  95. // Verify transaction
  96. let result = verify_transactions(
  97. &overlay,
  98. next_block_height,
  99. node.validator.consensus.module.read().await.target,
  100. &tx_vec,
  101. &mut MerkleTree::new(1),
  102. false,
  103. )
  104. .await;
  105. // Drop new trees opened by the forks' overlay
  106. overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
  107. // Check result
  108. match result {
  109. Ok(_) => valid = true,
  110. Err(Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => {
  111. // Remove transaction from fork's mempool
  112. fork.mempool.retain(|tx| *tx != tx_hash);
  113. }
  114. Err(e) => {
  115. error!(
  116. target: "darkfid::task::garbage_collect_task",
  117. "Verifying transaction {tx_hash} failed: {e}"
  118. );
  119. return Err(e)
  120. }
  121. }
  122. }
  123. // Drop forks lock
  124. drop(forks);
  125. // Remove transaction if its invalid for all the forks
  126. if !valid {
  127. debug!(target: "darkfid::task::garbage_collect_task", "Removing invalid transaction: {tx_hash}");
  128. if let Err(e) = node.validator.blockchain.remove_pending_txs_hashes(&[tx_hash]) {
  129. error!(
  130. target: "darkfid::task::garbage_collect_task",
  131. "Removing invalid transaction {tx_hash} failed: {e}"
  132. );
  133. };
  134. }
  135. }
  136. // Grab next batch
  137. (last_checked, txs) = match node
  138. .validator
  139. .blockchain
  140. .transactions
  141. .get_after_pending(last_checked + node.txs_batch_size as u64, node.txs_batch_size)
  142. {
  143. Ok(pair) => pair,
  144. Err(e) => {
  145. error!(
  146. target: "darkfid::task::garbage_collect_task",
  147. "Uproposed transactions next batch retrieval failed: {e}"
  148. );
  149. break
  150. }
  151. };
  152. }
  153. // Purge all unreferenced contract trees from the database again
  154. if let Err(e) = node.validator.consensus.purge_unreferenced_trees().await {
  155. error!(target: "darkfid::task::garbage_collect_task", "Purging unreferenced contract trees from the database failed: {e}");
  156. }
  157. info!(target: "darkfid::task::garbage_collect_task", "Garbage collection finished successfully!");
  158. Ok(())
  159. }