garbage_collect.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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 std::collections::HashMap;
  19. use darkfi::{
  20. blockchain::parse_record, tx::Transaction, validator::verification::verify_transaction,
  21. zk::VerifyingKey, Result,
  22. };
  23. use darkfi_sdk::{crypto::MerkleTree, tx::TransactionHash};
  24. use smol::channel::Receiver;
  25. use tracing::{debug, error, info};
  26. use crate::DarkfiNodePtr;
  27. /// Auxiliary macro to check if channel receiver is empty so we can
  28. /// abort current iteration.
  29. macro_rules! trigger_queue_check {
  30. ($receiver:ident, $label:tt) => {
  31. if !$receiver.is_empty() {
  32. continue $label
  33. }
  34. };
  35. }
  36. /// Async task used for purging unreferenced trees and erroneous
  37. /// pending transactions from the nodes mempool.
  38. pub async fn garbage_collect_task(receiver: Receiver<()>, node: DarkfiNodePtr) -> Result<()> {
  39. info!(target: "darkfid::task::garbage_collect_task", "Starting garbage collection task...");
  40. 'outer: loop {
  41. // Wait for a new trigger
  42. if let Err(e) = receiver.recv().await {
  43. error!(target: "darkfid::task::garbage_collect_task", "recv fail: {e}");
  44. continue
  45. };
  46. // Purge all unreferenced contract trees from the database
  47. trigger_queue_check!(receiver, 'outer);
  48. debug!(target: "darkfid::task::garbage_collect_task", "Starting garbage collection iteration...");
  49. if let Err(e) = node
  50. .validator
  51. .read()
  52. .await
  53. .consensus
  54. .purge_unreferenced_trees(&mut node.registry.state.read().await.new_trees())
  55. .await
  56. {
  57. error!(target: "darkfid::task::garbage_collect_task", "Purging unreferenced contract trees from the database failed: {e}");
  58. continue
  59. }
  60. debug!(target: "darkfid::task::garbage_collect_task", "Unreferenced trees purged successfully, retrieving pending transactions...");
  61. // Check if our mempool is empty
  62. trigger_queue_check!(receiver, 'outer);
  63. let validator = node.validator.read().await;
  64. if validator.blockchain.transactions.pending.is_empty()? {
  65. debug!(target: "darkfid::task::garbage_collect_task", "No pending transactions to process");
  66. continue
  67. }
  68. // Grab validator current best fork and an iterator over its
  69. // pending transactions so we don't hold the validator lock.
  70. let pending = validator.blockchain.transactions.pending.iter();
  71. let fork = match validator.best_current_fork().await {
  72. Ok(f) => f,
  73. Err(e) => {
  74. error!(target: "darkfid::task::garbage_collect_task", "Retrieving validator current best fork failed: {e}");
  75. continue
  76. }
  77. };
  78. let verify_fees = validator.verify_fees;
  79. drop(validator);
  80. // Transactions Merkle tree
  81. trigger_queue_check!(receiver, 'outer);
  82. let mut tree = MerkleTree::new(1);
  83. // Map of ZK proof verifying keys for the current transactions
  84. // batch.
  85. let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
  86. // Grab forks' next block height
  87. let next_block_height = match fork.get_next_block_height() {
  88. Ok(h) => h,
  89. Err(e) => {
  90. error!(
  91. target: "darkfid::task::garbage_collect_task",
  92. "Next fork block height retrieval failed: {e}"
  93. );
  94. continue
  95. }
  96. };
  97. // Iterate over all pending transactions
  98. for record in pending {
  99. trigger_queue_check!(receiver, 'outer);
  100. let record = match record {
  101. Ok(r) => r,
  102. Err(e) => {
  103. error!(target: "darkfid::task::garbage_collect_task", "Failed retrieving pending tx: {e}");
  104. continue 'outer
  105. }
  106. };
  107. let (tx_hash, tx) = match parse_record::<TransactionHash, Transaction>(record) {
  108. Ok((h, t)) => (h, t),
  109. Err(e) => {
  110. error!(target: "darkfid::task::garbage_collect_task", "Failed parsing pending tx: {e}");
  111. continue
  112. }
  113. };
  114. // If the transaction has already been proposed, remove it
  115. trigger_queue_check!(receiver, 'outer);
  116. debug!(target: "darkfid::task::garbage_collect_task", "Checking transaction: {tx_hash}");
  117. if fork.overlay.lock().unwrap().transactions.contains(&tx_hash)? {
  118. debug!(target: "darkfid::task::garbage_collect_task", "Transaction {tx_hash} has already been proposed, removing...");
  119. if let Err(e) = fork.blockchain.remove_pending_txs_hashes(&[tx_hash]) {
  120. error!(target: "darkfid::task::garbage_collect_task", "Failed removing pending tx: {e}");
  121. };
  122. continue
  123. }
  124. // Update the verifying keys map
  125. trigger_queue_check!(receiver, 'outer);
  126. for call in &tx.calls {
  127. vks.entry(call.data.contract_id.to_bytes()).or_default();
  128. }
  129. // Verify the transaction against current state
  130. trigger_queue_check!(receiver, 'outer);
  131. fork.overlay.lock().unwrap().checkpoint();
  132. let result = verify_transaction(
  133. &fork.overlay,
  134. next_block_height,
  135. fork.module.target,
  136. &tx,
  137. &mut tree,
  138. &mut vks,
  139. verify_fees,
  140. )
  141. .await;
  142. fork.overlay.lock().unwrap().revert_to_checkpoint();
  143. if let Err(e) = result {
  144. debug!(target: "darkfid::task::garbage_collect_task", "Pending transaction {tx_hash} verification failed: {e}");
  145. if let Err(e) = fork.blockchain.remove_pending_txs_hashes(&[tx_hash]) {
  146. error!(target: "darkfid::task::garbage_collect_task", "Failed removing pending tx: {e}");
  147. };
  148. continue
  149. }
  150. debug!(target: "darkfid::task::garbage_collect_task", "Pending transaction {tx_hash} verification successfully.");
  151. }
  152. }
  153. }