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