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}