| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2026 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use darkfi::{error::TxVerifyFailed, validator::verification::verify_transactions, Error, Result};
- use darkfi_sdk::crypto::MerkleTree;
- use tracing::{debug, error, info};
- use crate::DarkfiNodePtr;
- /// Async task used for purging erroneous pending transactions from the nodes mempool.
- pub async fn garbage_collect_task(node: DarkfiNodePtr) -> Result<()> {
- info!(target: "darkfid::task::garbage_collect_task", "Starting garbage collection task...");
- // Purge all unreferenced contract trees from the database
- if let Err(e) = node.validator.consensus.purge_unreferenced_trees().await {
- error!(target: "darkfid::task::garbage_collect_task", "Purging unreferenced contract trees from the database failed: {e}");
- }
- // Grab all current unproposed transactions. We verify them in batches,
- // to not load them all in memory.
- let (mut last_checked, mut txs) =
- match node.validator.blockchain.transactions.get_after_pending(0, node.txs_batch_size) {
- Ok(pair) => pair,
- Err(e) => {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Uproposed transactions retrieval failed: {e}"
- );
- return Ok(())
- }
- };
- // Check if we have transactions to process
- if txs.is_empty() {
- info!(target: "darkfid::task::garbage_collect_task", "Garbage collection finished successfully!");
- return Ok(())
- }
- while !txs.is_empty() {
- // Verify each one against current forks
- for tx in txs {
- let tx_hash = tx.hash();
- let tx_vec = [tx.clone()];
- let mut valid = false;
- // Grab a lock over current consensus forks state
- let mut forks = node.validator.consensus.forks.write().await;
- // Iterate over them to verify transaction validity in their overlays
- for fork in forks.iter_mut() {
- // Clone forks' overlay
- let overlay = match fork.overlay.lock().unwrap().full_clone() {
- Ok(o) => o,
- Err(e) => {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Overlay full clone creation failed: {e}"
- );
- return Err(e)
- }
- };
- // Grab all current proposals transactions hashes
- let proposals_txs =
- match overlay.lock().unwrap().get_blocks_txs_hashes(&fork.proposals) {
- Ok(txs) => txs,
- Err(e) => {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Proposal transactions retrieval failed: {e}"
- );
- return Err(e)
- }
- };
- // If the hash is contained in the proposals transactions vec, skip it
- if proposals_txs.contains(&tx_hash) {
- continue
- }
- // Grab forks' next block height
- let next_block_height = match fork.get_next_block_height() {
- Ok(h) => h,
- Err(e) => {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Next fork block height retrieval failed: {e}"
- );
- return Err(e)
- }
- };
- // Verify transaction
- let result = verify_transactions(
- &overlay,
- next_block_height,
- node.validator.consensus.module.read().await.target,
- &tx_vec,
- &mut MerkleTree::new(1),
- false,
- )
- .await;
- // Drop new trees opened by the forks' overlay
- overlay.lock().unwrap().overlay.lock().unwrap().purge_new_trees()?;
- // Check result
- match result {
- Ok(_) => valid = true,
- Err(Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => {
- // Remove transaction from fork's mempool
- fork.mempool.retain(|tx| *tx != tx_hash);
- }
- Err(e) => {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Verifying transaction {tx_hash} failed: {e}"
- );
- return Err(e)
- }
- }
- }
- // Drop forks lock
- drop(forks);
- // Remove transaction if its invalid for all the forks
- if !valid {
- debug!(target: "darkfid::task::garbage_collect_task", "Removing invalid transaction: {tx_hash}");
- if let Err(e) = node.validator.blockchain.remove_pending_txs_hashes(&[tx_hash]) {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Removing invalid transaction {tx_hash} failed: {e}"
- );
- };
- }
- }
- // Grab next batch
- (last_checked, txs) = match node
- .validator
- .blockchain
- .transactions
- .get_after_pending(last_checked + node.txs_batch_size as u64, node.txs_batch_size)
- {
- Ok(pair) => pair,
- Err(e) => {
- error!(
- target: "darkfid::task::garbage_collect_task",
- "Uproposed transactions next batch retrieval failed: {e}"
- );
- break
- }
- };
- }
- // Purge all unreferenced contract trees from the database again
- if let Err(e) = node.validator.consensus.purge_unreferenced_trees().await {
- error!(target: "darkfid::task::garbage_collect_task", "Purging unreferenced contract trees from the database failed: {e}");
- }
- info!(target: "darkfid::task::garbage_collect_task", "Garbage collection finished successfully!");
- Ok(())
- }
|