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