darkfi/validator/
mod.rs

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
19use std::{collections::HashMap, sync::Arc};
20
21use darkfi_sdk::{blockchain::compute_fee, crypto::MerkleTree};
22use num_bigint::BigUint;
23use sled_overlay::sled;
24use smol::lock::RwLock;
25use tracing::{debug, error, info, warn};
26
27use crate::{
28    blockchain::{
29        block_store::{BlockDifficulty, BlockInfo, BlockRanks},
30        Blockchain, BlockchainOverlay, HeaderHash,
31    },
32    error::TxVerifyFailed,
33    tx::Transaction,
34    zk::VerifyingKey,
35    Error, Result,
36};
37
38/// DarkFi consensus module
39pub mod consensus;
40use consensus::{Consensus, Fork, Proposal};
41
42/// DarkFi PoW module
43pub mod pow;
44use pow::PoWModule;
45
46/// RandomX infrastructure
47pub mod randomx_factory;
48pub use randomx_factory::RandomXFactory;
49
50/// Verification functions
51pub mod verification;
52use verification::{
53    verify_block, verify_checkpoint_block, verify_genesis_block, verify_producer_transaction,
54    verify_transaction, verify_transactions,
55};
56
57/// Fee calculation helpers
58pub mod fees;
59
60/// Helper utilities
61pub mod utils;
62use utils::{best_fork_index, block_rank, deploy_native_contracts};
63
64/// Configuration for initializing [`Validator`]
65#[derive(Clone)]
66pub struct ValidatorConfig {
67    /// Currently configured confirmation security threshold
68    pub confirmation_threshold: usize,
69    /// Currently configured max in-memory forks to maintain.
70    pub max_forks: usize,
71    /// Currently configured PoW target
72    pub pow_target: u32,
73    /// Optional fixed difficulty, for testing purposes
74    pub pow_fixed_difficulty: Option<BigUint>,
75    /// Genesis block
76    pub genesis_block: BlockInfo,
77    /// Flag to enable tx fee verification
78    pub verify_fees: bool,
79}
80
81/// Atomic pointer to validator.
82pub type ValidatorPtr = Arc<RwLock<Validator>>;
83
84/// This struct represents a DarkFi validator node.
85pub struct Validator {
86    /// Canonical (confirmed) blockchain
87    pub blockchain: Blockchain,
88    /// Hot/Live data used by the consensus algorithm
89    pub consensus: Consensus,
90    /// Flag signalling if the node is synced
91    pub synced: bool,
92    /// Flag to enable tx fee verification
93    pub verify_fees: bool,
94}
95
96impl Validator {
97    pub async fn new(db: &sled::Db, config: &ValidatorConfig) -> Result<ValidatorPtr> {
98        info!(target: "validator::new", "Initializing Validator");
99
100        info!(target: "validator::new", "Initializing Blockchain");
101        let blockchain = Blockchain::new(db)?;
102
103        // Create an overlay over whole blockchain so we can write
104        // stuff.
105        let overlay = BlockchainOverlay::new(&blockchain)?;
106
107        // Deploy native wasm contracts
108        deploy_native_contracts(&overlay, config.pow_target).await?;
109
110        // Update the contracts states monotree in case native
111        // contracts zkas has changed.
112        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
113        overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
114
115        // Add genesis block if blockchain is empty
116        if blockchain.genesis().is_err() {
117            info!(target: "validator::new", "Appending genesis block");
118            verify_genesis_block(&overlay, &[diff], &config.genesis_block, config.pow_target)
119                .await?;
120        };
121
122        // Write the changes to the actual chain db
123        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
124
125        info!(target: "validator::new", "Initializing Consensus");
126        let consensus = Consensus::new(
127            blockchain.clone(),
128            config.confirmation_threshold,
129            config.max_forks,
130            config.pow_target,
131            config.pow_fixed_difficulty.clone(),
132        )?;
133
134        // Create the actual state
135        let state = Arc::new(RwLock::new(Self {
136            blockchain,
137            consensus,
138            synced: false,
139            verify_fees: config.verify_fees,
140        }));
141
142        info!(target: "validator::new", "Finished initializing validator");
143        Ok(state)
144    }
145
146    /// Auxiliary function to compute provided transaction's required
147    /// fee, against current best fork. The function takes a boolean
148    /// called `verify_fee` to overwrite the nodes configured
149    /// `verify_fees` flag.
150    ///
151    /// Note: Always remember to purge new trees from the database if
152    /// not needed.
153    pub async fn calculate_fee(&self, tx: &Transaction, verify_fee: bool) -> Result<u64> {
154        // Grab the best fork to verify against
155        let index = best_fork_index(&self.consensus.forks)?;
156        let fork = self.consensus.forks[index].full_clone()?;
157
158        // Map of ZK proof verifying keys for the transaction
159        let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
160        for call in &tx.calls {
161            vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
162        }
163
164        // Grab forks' next block height
165        let next_block_height = fork.get_next_block_height()?;
166
167        // Verify transaction to grab the gas used
168        let verify_result = verify_transaction(
169            &fork.overlay,
170            next_block_height,
171            self.consensus.module.target,
172            tx,
173            &mut MerkleTree::new(1),
174            &mut vks,
175            verify_fee,
176        )
177        .await?;
178
179        Ok(compute_fee(&verify_result.total_gas_used()))
180    }
181
182    /// The node retrieves a transaction, validates its state
183    /// transition, and appends it to the pending txs store.
184    ///
185    /// Note: Always remember to purge new trees from the database if
186    /// not needed.
187    pub async fn append_tx(&mut self, tx: &Transaction, write: bool) -> Result<()> {
188        let tx_hash = tx.hash();
189
190        // Check if we have already seen this tx
191        let tx_in_txstore = self.blockchain.transactions.contains(&tx_hash)?;
192        let tx_in_pending_txs_store = self.blockchain.transactions.contains_pending(&tx_hash)?;
193
194        if tx_in_txstore || tx_in_pending_txs_store {
195            debug!(target: "validator::append_tx", "We have already seen tx: {tx_hash}");
196            return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
197        }
198
199        // Verify state transition
200        info!(target: "validator::append_tx", "Starting state transition validation for tx: {tx_hash}");
201        let tx_vec = [tx.clone()];
202        let mut valid = false;
203
204        // Iterate over node forks to verify transaction validity in
205        // their overlays.
206        for fork in self.consensus.forks.iter_mut() {
207            // Clone fork state
208            let fork_clone = fork.full_clone()?;
209
210            // Grab forks' next block height
211            let next_block_height = fork_clone.get_next_block_height()?;
212
213            // Verify transaction
214            let verify_result = verify_transactions(
215                &fork_clone.overlay,
216                next_block_height,
217                self.consensus.module.target,
218                &tx_vec,
219                &mut MerkleTree::new(1),
220                self.verify_fees,
221            )
222            .await;
223
224            // Handle response
225            match verify_result {
226                Ok(_) => {}
227                Err(Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => continue,
228                Err(e) => return Err(e),
229            }
230
231            valid = true;
232
233            // Store transaction hash in forks' mempool
234            if write {
235                fork.mempool.push(tx_hash);
236            }
237        }
238
239        // Return error if transaction is not valid for any fork
240        if !valid {
241            return Err(TxVerifyFailed::ErroneousTxs(tx_vec.to_vec()).into())
242        }
243
244        // Add transaction to pending txs store
245        if write {
246            self.blockchain.add_pending_txs(&tx_vec)?;
247            info!(target: "validator::append_tx", "Appended tx {tx_hash} to pending txs store");
248        }
249
250        Ok(())
251    }
252
253    /// The node removes invalid transactions from the pending txs
254    /// store.
255    ///
256    /// Note: Always remember to purge new trees from the database if
257    /// not needed.
258    pub async fn purge_pending_txs(&mut self) -> Result<()> {
259        info!(target: "validator::purge_pending_txs", "Removing invalid transactions from pending transactions store...");
260
261        // Check if any pending transactions exist
262        let pending_txs = self.blockchain.get_pending_txs()?;
263        if pending_txs.is_empty() {
264            info!(target: "validator::purge_pending_txs", "No pending transactions found");
265            return Ok(())
266        }
267
268        let mut removed_txs = vec![];
269        for tx in pending_txs {
270            let tx_hash = tx.hash();
271            let tx_vec = [tx.clone()];
272            let mut valid = false;
273
274            // Iterate over node forks to verify transaction validity
275            // in their overlays.
276            for fork in self.consensus.forks.iter_mut() {
277                // Clone fork state
278                let fork_clone = fork.full_clone()?;
279
280                // Grab forks' next block height
281                let next_block_height = fork_clone.get_next_block_height()?;
282
283                // Verify transaction
284                let verify_result = verify_transactions(
285                    &fork_clone.overlay,
286                    next_block_height,
287                    self.consensus.module.target,
288                    &tx_vec,
289                    &mut MerkleTree::new(1),
290                    self.verify_fees,
291                )
292                .await;
293
294                // Handle response
295                match verify_result {
296                    Ok(_) => {
297                        valid = true;
298                        continue
299                    }
300                    Err(Error::TxVerifyFailed(TxVerifyFailed::ErroneousTxs(_))) => {}
301                    Err(e) => return Err(e),
302                }
303
304                // Remove erroneous transaction from forks' mempool
305                fork.mempool.retain(|x| *x != tx_hash);
306            }
307
308            // Remove pending transaction if it's not valid for
309            // canonical or any fork.
310            if !valid {
311                removed_txs.push(tx)
312            }
313        }
314
315        if removed_txs.is_empty() {
316            info!(target: "validator::purge_pending_txs", "No erroneous transactions found");
317            return Ok(())
318        }
319        info!(target: "validator::purge_pending_txs", "Removing {} erroneous transactions...", removed_txs.len());
320        self.blockchain.remove_pending_txs(&removed_txs)?;
321
322        Ok(())
323    }
324
325    /// The node tries to append provided proposal to its consensus
326    /// state.
327    pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
328        self.consensus.append_proposal(proposal, self.synced, self.verify_fees).await
329    }
330
331    /// The node checks if best fork can be confirmed.
332    /// If proposals can be confirmed, node appends them to canonical,
333    /// and resets the current forks.
334    pub async fn confirmation(&mut self) -> Result<Vec<BlockInfo>> {
335        info!(target: "validator::confirmation", "Performing confirmation check");
336
337        // Grab best fork index that can be confirmed
338        let confirmed_fork = self.consensus.confirmation().await?;
339        if confirmed_fork.is_none() {
340            info!(target: "validator::confirmation", "No proposals can be confirmed");
341            return Ok(vec![])
342        }
343
344        // Grab the actual best fork
345        let confirmed_fork = confirmed_fork.unwrap();
346        let fork = &mut self.consensus.forks[confirmed_fork];
347
348        // Find the excess over confirmation threshold
349        let excess = (fork.proposals.len() - self.consensus.confirmation_threshold) + 1;
350
351        // Grab confirmed proposals and update fork's sequences
352        let rest_proposals = fork.proposals.split_off(excess);
353        let rest_diffs = fork.diffs.split_off(excess);
354        let confirmed_proposals = fork.proposals.clone();
355        let diffs = fork.diffs.clone();
356        fork.proposals = rest_proposals;
357        fork.diffs = rest_diffs;
358
359        // Grab confirmed proposals blocks
360        let confirmed_blocks =
361            fork.overlay.lock().unwrap().get_blocks_by_hash(&confirmed_proposals)?;
362
363        // Apply confirmed proposals diffs and update PoW module
364        let mut module = self.consensus.module.clone();
365        let mut confirmed_txs = vec![];
366        let mut state_inverse_diffs_heights = vec![];
367        let mut state_inverse_diffs = vec![];
368        info!(target: "validator::confirmation", "Confirming proposals:");
369        for (index, proposal) in confirmed_proposals.iter().enumerate() {
370            info!(target: "validator::confirmation", "\t{proposal} ({}) - {}", confirmed_blocks[index].header.pow_data, confirmed_blocks[index].header.height);
371            fork.overlay.lock().unwrap().overlay.lock().unwrap().apply_diff(&diffs[index])?;
372            let next_difficulty = module.next_difficulty()?;
373            module.append(&confirmed_blocks[index].header, &next_difficulty)?;
374            confirmed_txs.extend_from_slice(&confirmed_blocks[index].txs);
375            state_inverse_diffs_heights.push(confirmed_blocks[index].header.height);
376            state_inverse_diffs.push(diffs[index].inverse());
377        }
378        self.consensus.module = module;
379
380        // Store the block inverse diffs
381        self.blockchain
382            .blocks
383            .insert_state_inverse_diff(&state_inverse_diffs_heights, &state_inverse_diffs)?;
384
385        // Reset forks starting with the confirmed blocks
386        self.consensus.reset_forks(&confirmed_proposals, &confirmed_fork, &confirmed_txs).await?;
387        info!(target: "validator::confirmation", "Confirmation completed!");
388
389        Ok(confirmed_blocks)
390    }
391
392    /// Apply provided set of [`BlockInfo`] without doing formal
393    /// verification. A set of [`HeaderHash`] is also provided, to
394    /// verify that the provided block hash matches the expected header
395    /// one.
396    ///
397    /// Note: this function should only be used for blocks received
398    /// using a checkpoint, since in that case we enforce the node to
399    /// follow the sequence, assuming all its blocks are valid.
400    /// Additionally, it will update any forks to a single empty one,
401    /// holding the updated module. Always remember to purge new trees
402    /// from the database if not needed.
403    pub async fn add_checkpoint_blocks(
404        &mut self,
405        blocks: &[BlockInfo],
406        headers: &[HeaderHash],
407    ) -> Result<()> {
408        // Check provided sequences are the same length
409        if blocks.len() != headers.len() {
410            return Err(Error::InvalidInputLengths)
411        }
412
413        debug!(target: "validator::add_checkpoint_blocks", "Instantiating BlockchainOverlay");
414        let overlay = BlockchainOverlay::new(&self.blockchain)?;
415
416        // Retrieve last block difficulty to access current ranks
417        let last_difficulty = self.blockchain.last_block_difficulty()?;
418        let mut current_targets_rank = last_difficulty.ranks.targets_rank;
419        let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
420
421        // Grab current PoW module to validate each block
422        let mut module = self.consensus.module.clone();
423
424        // Keep track of all blocks transactions to remove them from
425        // pending txs store.
426        let mut removed_txs = vec![];
427
428        // Keep track of all block database state diffs and their
429        // inverse.
430        let mut diffs_heights = vec![];
431        let mut diffs = vec![];
432        let mut inverse_diffs = vec![];
433
434        // Validate and insert each block
435        for (index, block) in blocks.iter().enumerate() {
436            // Verify block
437            match verify_checkpoint_block(&overlay, &diffs, block, &headers[index], module.target)
438                .await
439            {
440                Ok(()) => { /* Do nothing */ }
441                // Skip already existing block
442                Err(Error::BlockAlreadyExists(_)) => continue,
443                Err(e) => {
444                    error!(target: "validator::add_checkpoint_blocks", "Erroneous block found in set: {e}");
445                    return Err(Error::BlockIsInvalid(block.hash().as_string()))
446                }
447            };
448
449            // Grab next mine target and difficulty
450            let (next_target, next_difficulty) = module.next_mine_target_and_difficulty()?;
451
452            // Calculate block rank
453            let (target_distance_sq, hash_distance_sq) = block_rank(block, &next_target)?;
454
455            // Update current ranks
456            current_targets_rank += target_distance_sq.clone();
457            current_hashes_rank += hash_distance_sq.clone();
458
459            // Generate block difficulty and update PoW module
460            let cumulative_difficulty =
461                module.cumulative_difficulty.clone() + next_difficulty.clone();
462            let ranks = BlockRanks::new(
463                target_distance_sq,
464                current_targets_rank.clone(),
465                hash_distance_sq,
466                current_hashes_rank.clone(),
467            );
468            let block_difficulty = BlockDifficulty::new(
469                block.header.height,
470                block.header.timestamp,
471                next_difficulty,
472                cumulative_difficulty,
473                ranks,
474            );
475            module.append_difficulty(&overlay, &block.header, block_difficulty)?;
476
477            // Store block transactions
478            for tx in &block.txs {
479                removed_txs.push(tx.clone());
480            }
481
482            // Store block database state diff and its inverse
483            diffs_heights.push(block.header.height);
484            let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
485            inverse_diffs.push(diff.inverse());
486            diffs.push(diff);
487        }
488
489        debug!(target: "validator::add_checkpoint_blocks", "Applying overlay changes");
490        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
491
492        // Store the block diffs
493        self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
494
495        // Remove blocks transactions from pending txs store
496        self.blockchain.remove_pending_txs(&removed_txs)?;
497
498        // Update PoW module
499        self.consensus.module = module.clone();
500
501        // Update forks
502        self.consensus.forks = vec![Fork::new(self.blockchain.clone(), module).await?];
503
504        Ok(())
505    }
506
507    /// Validate a set of [`BlockInfo`] in sequence and apply them if
508    /// all are valid.
509    ///
510    /// Note: this function should only be used in tests when we don't
511    /// want to perform consensus logic and always remember to purge
512    /// new trees from the database if not needed.
513    pub async fn add_test_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
514        debug!(target: "validator::add_test_blocks", "Instantiating BlockchainOverlay");
515        let overlay = BlockchainOverlay::new(&self.blockchain)?;
516
517        // Retrieve last block
518        let mut previous = &overlay.lock().unwrap().last_block()?;
519
520        // Retrieve last block difficulty to access current ranks
521        let last_difficulty = self.blockchain.last_block_difficulty()?;
522        let mut current_targets_rank = last_difficulty.ranks.targets_rank;
523        let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
524
525        // Grab current PoW module to validate each block
526        let mut module = self.consensus.module.clone();
527
528        // Keep track of all blocks transactions to remove them from
529        // pending txs store.
530        let mut removed_txs = vec![];
531
532        // Keep track of all block database state diffs and their
533        // inverse.
534        let mut diffs_heights = vec![];
535        let mut diffs = vec![];
536        let mut inverse_diffs = vec![];
537
538        // Validate and insert each block
539        for block in blocks {
540            // Verify block
541            match verify_block(
542                &overlay,
543                &diffs,
544                &mut module,
545                block,
546                previous,
547                true,
548                self.verify_fees,
549            )
550            .await
551            {
552                Ok(()) => { /* Do nothing */ }
553                // Skip already existing block
554                Err(Error::BlockAlreadyExists(_)) => {
555                    previous = block;
556                    continue
557                }
558                Err(e) => {
559                    error!(target: "validator::add_test_blocks", "Erroneous block found in set: {e}");
560                    return Err(Error::BlockIsInvalid(block.hash().as_string()))
561                }
562            };
563
564            // Grab next mine target and difficulty
565            let (next_target, next_difficulty) = module.next_mine_target_and_difficulty()?;
566
567            // Calculate block rank
568            let (target_distance_sq, hash_distance_sq) = block_rank(block, &next_target)?;
569
570            // Update current ranks
571            current_targets_rank += target_distance_sq.clone();
572            current_hashes_rank += hash_distance_sq.clone();
573
574            // Generate block difficulty and update PoW module
575            let cumulative_difficulty =
576                module.cumulative_difficulty.clone() + next_difficulty.clone();
577            let ranks = BlockRanks::new(
578                target_distance_sq,
579                current_targets_rank.clone(),
580                hash_distance_sq,
581                current_hashes_rank.clone(),
582            );
583            let block_difficulty = BlockDifficulty::new(
584                block.header.height,
585                block.header.timestamp,
586                next_difficulty,
587                cumulative_difficulty,
588                ranks,
589            );
590            module.append_difficulty(&overlay, &block.header, block_difficulty)?;
591
592            // Store block transactions
593            for tx in &block.txs {
594                removed_txs.push(tx.clone());
595            }
596
597            // Store block database state diff and its inverse
598            diffs_heights.push(block.header.height);
599            let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
600            inverse_diffs.push(diff.inverse());
601            diffs.push(diff);
602
603            // Use last inserted block as next iteration previous
604            previous = block;
605        }
606
607        debug!(target: "validator::add_test_blocks", "Applying overlay changes");
608        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
609
610        // Store the block diffs
611        self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
612
613        // Purge pending erroneous txs since canonical state has been
614        // changed.
615        self.blockchain.remove_pending_txs(&removed_txs)?;
616        self.purge_pending_txs().await?;
617
618        // Update PoW module
619        self.consensus.module = module;
620
621        Ok(())
622    }
623
624    /// Validate a set of [`Transaction`] in sequence and apply them if
625    /// all are valid. In case any of the transactions fail, they will
626    /// be returned to the caller. The function takes a boolean called
627    /// `write` which tells it to actually write the state transitions
628    /// to the database, and a boolean called `verify_fees` to
629    /// overwrite the nodes configured `verify_fees` flag.
630    ///
631    /// Returns the total gas used and total paid fees for the given
632    /// transactions.
633    ///
634    /// Note: This function should only be used in tests and always
635    /// remember to purge new trees from the database if not needed.
636    pub async fn add_test_transactions(
637        &self,
638        txs: &[Transaction],
639        verifying_block_height: u32,
640        block_target: u32,
641        write: bool,
642        verify_fees: bool,
643    ) -> Result<(u64, u64)> {
644        debug!(target: "validator::add_transactions", "Instantiating BlockchainOverlay");
645        let overlay = BlockchainOverlay::new(&self.blockchain)?;
646
647        // Verify all transactions and get erroneous ones
648        let verify_result = verify_transactions(
649            &overlay,
650            verifying_block_height,
651            block_target,
652            txs,
653            &mut MerkleTree::new(1),
654            verify_fees,
655        )
656        .await;
657
658        let lock = overlay.lock().unwrap();
659        let mut overlay = lock.overlay.lock().unwrap();
660
661        let gas_values = verify_result?;
662
663        if !write {
664            debug!(target: "validator::add_transactions", "Skipping apply of state updates because write=false");
665            return Ok(gas_values)
666        }
667
668        debug!(target: "validator::add_transactions", "Applying overlay changes");
669        overlay.apply()?;
670        Ok(gas_values)
671    }
672
673    /// Validate a producer `Transaction` and apply it if valid. In
674    /// case the transactions fail, ir will be returned to the caller.
675    /// The function takes a boolean called `write` which tells it to
676    /// actually write the state transitions to the database.
677    ///
678    /// Note: This function should only be used in tests and always
679    /// remember to purge new trees from the database if not needed.
680    pub async fn add_test_producer_transaction(
681        &self,
682        tx: &Transaction,
683        verifying_block_height: u32,
684        block_target: u32,
685        write: bool,
686    ) -> Result<()> {
687        debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
688        let overlay = BlockchainOverlay::new(&self.blockchain)?;
689
690        // Verify transaction
691        let mut erroneous_txs = vec![];
692        if let Err(e) = verify_producer_transaction(
693            &overlay,
694            verifying_block_height,
695            block_target,
696            tx,
697            &mut MerkleTree::new(1),
698        )
699        .await
700        {
701            warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {e}");
702            erroneous_txs.push(tx.clone());
703        }
704
705        let lock = overlay.lock().unwrap();
706        let mut overlay = lock.overlay.lock().unwrap();
707        if !erroneous_txs.is_empty() {
708            warn!(target: "validator::add_test_producer_transaction", "Erroneous transactions found in set");
709            return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
710        }
711
712        if !write {
713            debug!(target: "validator::add_test_producer_transaction", "Skipping apply of state updates because write=false");
714            return Ok(())
715        }
716
717        debug!(target: "validator::add_test_producer_transaction", "Applying overlay changes");
718        overlay.apply()?;
719        Ok(())
720    }
721
722    /// Retrieve all existing blocks and try to apply them
723    /// to an in memory overlay to verify their correctness.
724    /// Be careful as this will try to load everything in memory.
725    ///
726    /// Note: Always remember to purge new trees from the database if
727    /// not needed.
728    pub async fn validate_blockchain(
729        &self,
730        pow_target: u32,
731        pow_fixed_difficulty: Option<BigUint>,
732    ) -> Result<()> {
733        // An empty blockchain is considered valid
734        let mut blocks_count = self.blockchain.len() as u32;
735        info!(target: "validator::validate_blockchain", "Validating {blocks_count} blocks...");
736        if blocks_count == 0 {
737            info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
738            return Ok(())
739        }
740
741        // Create an in memory blockchain overlay
742        let sled_db = sled::Config::new().temporary(true).open()?;
743        let blockchain = Blockchain::new(&sled_db)?;
744        let overlay = BlockchainOverlay::new(&blockchain)?;
745
746        // Set previous
747        let mut previous = self.blockchain.genesis_block()?;
748
749        // Deploy native wasm contracts
750        deploy_native_contracts(&overlay, pow_target).await?;
751
752        // Update the contracts states monotree
753        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
754        overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
755
756        // Validate genesis block
757        verify_genesis_block(&overlay, &[diff], &previous, pow_target).await?;
758        info!(target: "validator::validate_blockchain", "Genesis block validated successfully!");
759
760        // Write the changes to the in memory db
761        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
762
763        // Create a PoW module to validate each block
764        let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, Some(0))?;
765
766        // Keep track of all block database state diffs
767        let mut diffs = vec![];
768
769        // Validate and insert each block
770        info!(target: "validator::validate_blockchain", "Validating rest blocks...");
771        blocks_count -= 1;
772        let mut index = 1;
773        while index <= blocks_count {
774            // Grab block
775            let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
776
777            // Verify block
778            if let Err(e) = verify_block(
779                &overlay,
780                &diffs,
781                &mut module,
782                &block,
783                &previous,
784                false,
785                self.verify_fees,
786            )
787            .await
788            {
789                error!(target: "validator::validate_blockchain", "Erroneous block found in set: {e}");
790                return Err(Error::BlockIsInvalid(block.hash().as_string()))
791            };
792
793            // Update PoW module
794            module.append(&block.header, &module.next_difficulty()?)?;
795
796            // Store block database state diff
797            let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
798            diffs.push(diff);
799
800            // Use last inserted block as next iteration previous
801            previous = block;
802
803            info!(target: "validator::validate_blockchain", "Block {index}/{blocks_count} validated successfully!");
804            index += 1;
805        }
806
807        info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
808        Ok(())
809    }
810
811    /// Auxiliary function to grab current mining RandomX key,
812    /// based on next block height.
813    /// If no forks exist, returns the canonical key.
814    pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
815        self.consensus.current_mining_randomx_key().await
816    }
817
818    /// Auxiliary function to grab best current fork full clone.
819    pub async fn best_current_fork(&self) -> Result<Fork> {
820        self.consensus.best_current_fork().await
821    }
822
823    /// Auxiliary function to retrieve current best fork next block
824    /// height.
825    pub async fn best_fork_next_block_height(&self) -> Result<u32> {
826        let index = best_fork_index(&self.consensus.forks)?;
827        let fork = &self.consensus.forks[index];
828        let next_block_height = fork.get_next_block_height()?;
829
830        Ok(next_block_height)
831    }
832
833    /// Auxiliary function to reset the validator blockchain and
834    /// consensus states to the provided block height.
835    pub async fn reset_to_height(&mut self, height: u32) -> Result<()> {
836        info!(target: "validator::reset_to_height", "Resetting validator to height: {height}");
837        // Reset our databasse to provided height
838        self.blockchain.reset_to_height(height)?;
839
840        // Reset consensus PoW module
841        self.consensus.reset_pow_module().await?;
842
843        // Purge current forks
844        self.consensus.purge_forks().await?;
845
846        info!(target: "validator::reset_to_height", "Validator reset successfully!");
847
848        Ok(())
849    }
850
851    /// Auxiliary function to rebuild the block difficulties database
852    /// based on current validator blockchain.
853    /// Be careful as this will try to load everything in memory.
854    pub async fn rebuild_block_difficulties(
855        &self,
856        pow_target: u32,
857        pow_fixed_difficulty: Option<BigUint>,
858    ) -> Result<()> {
859        info!(target: "validator::rebuild_block_difficulties", "Rebuilding validator block difficulties...");
860        // Clear the block difficulties tree
861        self.blockchain.blocks.difficulty.clear()?;
862
863        // An empty blockchain doesn't have difficulty records
864        let mut blocks_count = self.blockchain.len() as u32;
865        info!(target: "validator::rebuild_block_difficulties", "Rebuilding {blocks_count} block difficulties...");
866        if blocks_count == 0 {
867            info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
868            return Ok(())
869        }
870
871        // Create a PoW module and an in memory overlay to compute each
872        // block difficulty.
873        let mut module =
874            PoWModule::new(self.blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
875
876        // Grab genesis block difficulty to access current ranks
877        let genesis_block = self.blockchain.genesis_block()?;
878        let last_difficulty = BlockDifficulty::genesis(genesis_block.header.timestamp);
879        let mut targets_rank = last_difficulty.ranks.targets_rank;
880        let mut hashes_rank = last_difficulty.ranks.hashes_rank;
881
882        // Grab each block to compute its difficulty
883        blocks_count -= 1;
884        let mut index = 1;
885        while index <= blocks_count {
886            // Grab block
887            let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
888
889            // Grab next mine target and difficulty
890            let (next_target, next_difficulty) = module.next_mine_target_and_difficulty()?;
891
892            // Calculate block rank
893            let (target_distance_sq, hash_distance_sq) = block_rank(&block, &next_target)?;
894
895            // Update chain ranks
896            targets_rank += target_distance_sq.clone();
897            hashes_rank += hash_distance_sq.clone();
898
899            // Generate block difficulty and update PoW module
900            let cumulative_difficulty =
901                module.cumulative_difficulty.clone() + next_difficulty.clone();
902            let ranks = BlockRanks::new(
903                target_distance_sq,
904                targets_rank.clone(),
905                hash_distance_sq,
906                hashes_rank.clone(),
907            );
908            let block_difficulty = BlockDifficulty::new(
909                block.header.height,
910                block.header.timestamp,
911                next_difficulty,
912                cumulative_difficulty,
913                ranks,
914            );
915            module.append(&block.header, &block_difficulty.difficulty)?;
916
917            // Add difficulty to database
918            self.blockchain.blocks.insert_difficulty(&[block_difficulty])?;
919
920            info!(target: "validator::rebuild_block_difficulties", "Block {index}/{blocks_count} difficulty added successfully!");
921            index += 1;
922        }
923
924        // Flush the database
925        self.blockchain.sled_db.flush()?;
926
927        info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
928
929        Ok(())
930    }
931}