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