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 agains best fork, and appends it to the pending txs
184    /// store if its valid.
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 in the pending store
192        if self.blockchain.transactions.contains_pending(&tx_hash)? {
193            debug!(target: "validator::append_tx", "We have already seen pending tx: {tx_hash}");
194            return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
195        }
196
197        // Grab the best fork to verify against
198        let index = best_fork_index(&self.consensus.forks)?;
199        let fork = self.consensus.forks[index].full_clone()?;
200
201        // Check if we have already seen this tx. This checks both the
202        // fork cache and the actual database.
203        if fork.overlay.lock().unwrap().transactions.contains(&tx_hash)? {
204            debug!(target: "validator::append_tx", "We have already seen tx: {tx_hash}");
205            return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
206        }
207
208        // Verify state transition
209        info!(target: "validator::append_tx", "Starting state transition validation for tx: {tx_hash}");
210        // Map of ZK proof verifying keys for the transaction
211        let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
212        for call in &tx.calls {
213            vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
214        }
215
216        // Grab forks' next block height
217        let next_block_height = fork.get_next_block_height()?;
218
219        // Verify transaction
220        verify_transaction(
221            &fork.overlay,
222            next_block_height,
223            self.consensus.module.target,
224            tx,
225            &mut MerkleTree::new(1),
226            &mut vks,
227            self.verify_fees,
228        )
229        .await?;
230
231        // Add transaction to pending txs store
232        if write {
233            self.blockchain.add_pending_txs(std::slice::from_ref(tx))?;
234            info!(target: "validator::append_tx", "Appended tx {tx_hash} to pending txs store");
235        }
236
237        Ok(())
238    }
239
240    /// The node tries to append provided proposal to its consensus
241    /// state.
242    pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
243        self.consensus.append_proposal(proposal, self.synced, self.verify_fees).await
244    }
245
246    /// The node checks if best fork can be confirmed.
247    /// If proposals can be confirmed, node appends them to canonical,
248    /// and resets the current forks.
249    pub async fn confirmation(&mut self) -> Result<Vec<BlockInfo>> {
250        info!(target: "validator::confirmation", "Performing confirmation check");
251
252        // Grab best fork index that can be confirmed
253        let confirmed_fork = self.consensus.confirmation().await?;
254        if confirmed_fork.is_none() {
255            info!(target: "validator::confirmation", "No proposals can be confirmed");
256            return Ok(vec![])
257        }
258
259        // Grab the actual best fork
260        let confirmed_fork = confirmed_fork.unwrap();
261        let fork = &mut self.consensus.forks[confirmed_fork];
262
263        // Find the excess over confirmation threshold
264        let excess = (fork.proposals.len() - self.consensus.confirmation_threshold) + 1;
265
266        // Grab confirmed proposals and update fork's sequences
267        let rest_proposals = fork.proposals.split_off(excess);
268        let rest_diffs = fork.diffs.split_off(excess);
269        let confirmed_proposals = fork.proposals.clone();
270        let diffs = fork.diffs.clone();
271        fork.proposals = rest_proposals;
272        fork.diffs = rest_diffs;
273
274        // Grab confirmed proposals blocks
275        let confirmed_blocks =
276            fork.overlay.lock().unwrap().get_blocks_by_hash(&confirmed_proposals)?;
277
278        // Apply confirmed proposals diffs and update PoW module
279        let mut module = self.consensus.module.clone();
280        let mut confirmed_txs = vec![];
281        let mut state_inverse_diffs_heights = vec![];
282        let mut state_inverse_diffs = vec![];
283        info!(target: "validator::confirmation", "Confirming proposals:");
284        for (index, proposal) in confirmed_proposals.iter().enumerate() {
285            info!(target: "validator::confirmation", "\t{proposal} ({}) - {}", confirmed_blocks[index].header.pow_data, confirmed_blocks[index].header.height);
286            fork.overlay.lock().unwrap().overlay.lock().unwrap().apply_diff(&diffs[index])?;
287            let next_difficulty = module.next_difficulty()?;
288            module.append(&confirmed_blocks[index].header, &next_difficulty)?;
289            confirmed_txs.extend_from_slice(&confirmed_blocks[index].txs);
290            state_inverse_diffs_heights.push(confirmed_blocks[index].header.height);
291            state_inverse_diffs.push(diffs[index].inverse());
292        }
293        self.consensus.module = module;
294
295        // Store the block inverse diffs
296        self.blockchain
297            .blocks
298            .insert_state_inverse_diff(&state_inverse_diffs_heights, &state_inverse_diffs)?;
299
300        // Reset forks starting with the confirmed blocks
301        self.consensus.reset_forks(&confirmed_proposals, &confirmed_fork, &confirmed_txs).await?;
302        info!(target: "validator::confirmation", "Confirmation completed!");
303
304        Ok(confirmed_blocks)
305    }
306
307    /// Apply provided set of [`BlockInfo`] without doing formal
308    /// verification. A set of [`HeaderHash`] is also provided, to
309    /// verify that the provided block hash matches the expected header
310    /// one.
311    ///
312    /// Note: this function should only be used for blocks received
313    /// using a checkpoint, since in that case we enforce the node to
314    /// follow the sequence, assuming all its blocks are valid.
315    /// Additionally, it will update any forks to a single empty one,
316    /// holding the updated module. Always remember to purge new trees
317    /// from the database if not needed.
318    pub async fn add_checkpoint_blocks(
319        &mut self,
320        blocks: &[BlockInfo],
321        headers: &[HeaderHash],
322    ) -> Result<()> {
323        // Check provided sequences are the same length
324        if blocks.len() != headers.len() {
325            return Err(Error::InvalidInputLengths)
326        }
327
328        debug!(target: "validator::add_checkpoint_blocks", "Instantiating BlockchainOverlay");
329        let overlay = BlockchainOverlay::new(&self.blockchain)?;
330
331        // Retrieve last block difficulty to access current ranks
332        let last_difficulty = self.blockchain.last_block_difficulty()?;
333        let mut current_targets_rank = last_difficulty.ranks.targets_rank;
334        let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
335
336        // Grab current PoW module to validate each block
337        let mut module = self.consensus.module.clone();
338
339        // Keep track of all blocks transactions to remove them from
340        // pending txs store.
341        let mut removed_txs = vec![];
342
343        // Keep track of all block database state diffs and their
344        // inverse.
345        let mut diffs_heights = vec![];
346        let mut diffs = vec![];
347        let mut inverse_diffs = vec![];
348
349        // Validate and insert each block
350        for (index, block) in blocks.iter().enumerate() {
351            // Verify block
352            match verify_checkpoint_block(&overlay, &diffs, block, &headers[index], module.target)
353                .await
354            {
355                Ok(()) => { /* Do nothing */ }
356                // Skip already existing block
357                Err(Error::BlockAlreadyExists(_)) => continue,
358                Err(e) => {
359                    error!(target: "validator::add_checkpoint_blocks", "Erroneous block found in set: {e}");
360                    return Err(Error::BlockIsInvalid(block.hash().as_string()))
361                }
362            };
363
364            // Calculate block rank
365            let (next_difficulty, target_distance_sq, hash_distance_sq) =
366                block_rank(&mut module, block)?;
367
368            // Update current ranks
369            current_targets_rank += target_distance_sq.clone();
370            current_hashes_rank += hash_distance_sq.clone();
371
372            // Generate block difficulty and update PoW module
373            let cumulative_difficulty =
374                module.cumulative_difficulty.clone() + next_difficulty.clone();
375            let ranks = BlockRanks::new(
376                target_distance_sq,
377                current_targets_rank.clone(),
378                hash_distance_sq,
379                current_hashes_rank.clone(),
380            );
381            let block_difficulty = BlockDifficulty::new(
382                block.header.height,
383                block.header.timestamp,
384                next_difficulty,
385                cumulative_difficulty,
386                ranks,
387            );
388            module.append_difficulty(&overlay, &block.header, block_difficulty)?;
389
390            // Store block transactions
391            for tx in &block.txs {
392                removed_txs.push(tx.clone());
393            }
394
395            // Store block database state diff and its inverse
396            diffs_heights.push(block.header.height);
397            let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
398            inverse_diffs.push(diff.inverse());
399            diffs.push(diff);
400        }
401
402        debug!(target: "validator::add_checkpoint_blocks", "Applying overlay changes");
403        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
404
405        // Store the block diffs
406        self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
407
408        // Remove blocks transactions from pending txs store
409        self.blockchain.remove_pending_txs(&removed_txs)?;
410
411        // Update PoW module
412        self.consensus.module = module.clone();
413
414        // Update forks
415        self.consensus.forks = vec![Fork::new(self.blockchain.clone(), module).await?];
416
417        Ok(())
418    }
419
420    /// Validate a set of [`BlockInfo`] in sequence and apply them if
421    /// all are valid.
422    ///
423    /// Note: this function should only be used in tests when we don't
424    /// want to perform consensus logic and always remember to purge
425    /// new trees from the database if not needed.
426    pub async fn add_test_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
427        debug!(target: "validator::add_test_blocks", "Instantiating BlockchainOverlay");
428        let overlay = BlockchainOverlay::new(&self.blockchain)?;
429
430        // Retrieve last block
431        let mut previous = &overlay.lock().unwrap().last_block()?;
432
433        // Retrieve last block difficulty to access current ranks
434        let last_difficulty = self.blockchain.last_block_difficulty()?;
435        let mut current_targets_rank = last_difficulty.ranks.targets_rank;
436        let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
437
438        // Grab current PoW module to validate each block
439        let mut module = self.consensus.module.clone();
440
441        // Keep track of all blocks transactions to remove them from
442        // pending txs store.
443        let mut removed_txs = vec![];
444
445        // Keep track of all block database state diffs and their
446        // 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 block in blocks {
453            // Verify block
454            match verify_block(
455                &overlay,
456                &diffs,
457                &mut module,
458                block,
459                previous,
460                true,
461                self.verify_fees,
462            )
463            .await
464            {
465                Ok(()) => { /* Do nothing */ }
466                // Skip already existing block
467                Err(Error::BlockAlreadyExists(_)) => {
468                    previous = block;
469                    continue
470                }
471                Err(e) => {
472                    error!(target: "validator::add_test_blocks", "Erroneous block found in set: {e}");
473                    return Err(Error::BlockIsInvalid(block.hash().as_string()))
474                }
475            };
476
477            // Calculate block rank
478            let (next_difficulty, target_distance_sq, hash_distance_sq) =
479                block_rank(&mut module, block)?;
480
481            // Update current ranks
482            current_targets_rank += target_distance_sq.clone();
483            current_hashes_rank += hash_distance_sq.clone();
484
485            // Generate block difficulty and update PoW module
486            let cumulative_difficulty =
487                module.cumulative_difficulty.clone() + next_difficulty.clone();
488            let ranks = BlockRanks::new(
489                target_distance_sq,
490                current_targets_rank.clone(),
491                hash_distance_sq,
492                current_hashes_rank.clone(),
493            );
494            let block_difficulty = BlockDifficulty::new(
495                block.header.height,
496                block.header.timestamp,
497                next_difficulty,
498                cumulative_difficulty,
499                ranks,
500            );
501            module.append_difficulty(&overlay, &block.header, block_difficulty)?;
502
503            // Store block transactions
504            for tx in &block.txs {
505                removed_txs.push(tx.clone());
506            }
507
508            // Store block database state diff and its inverse
509            diffs_heights.push(block.header.height);
510            let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
511            inverse_diffs.push(diff.inverse());
512            diffs.push(diff);
513
514            // Use last inserted block as next iteration previous
515            previous = block;
516        }
517
518        debug!(target: "validator::add_test_blocks", "Applying overlay changes");
519        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
520
521        // Store the block diffs
522        self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
523
524        // Remove blocks transactions from pending txs store
525        self.blockchain.remove_pending_txs(&removed_txs)?;
526
527        // Update PoW module
528        self.consensus.module = module;
529
530        Ok(())
531    }
532
533    /// Validate a set of [`Transaction`] in sequence and apply them if
534    /// all are valid. In case any of the transactions fail, they will
535    /// be returned to the caller. The function takes a boolean called
536    /// `write` which tells it to actually write the state transitions
537    /// to the database, and a boolean called `verify_fees` to
538    /// overwrite the nodes configured `verify_fees` flag.
539    ///
540    /// Returns the total gas used and total paid fees for the given
541    /// transactions.
542    ///
543    /// Note: This function should only be used in tests and always
544    /// remember to purge new trees from the database if not needed.
545    pub async fn add_test_transactions(
546        &self,
547        txs: &[Transaction],
548        verifying_block_height: u32,
549        block_target: u32,
550        write: bool,
551        verify_fees: bool,
552    ) -> Result<(u64, u64)> {
553        debug!(target: "validator::add_transactions", "Instantiating BlockchainOverlay");
554        let overlay = BlockchainOverlay::new(&self.blockchain)?;
555
556        // Verify all transactions and get erroneous ones
557        let verify_result = verify_transactions(
558            &overlay,
559            verifying_block_height,
560            block_target,
561            txs,
562            &mut MerkleTree::new(1),
563            verify_fees,
564        )
565        .await;
566
567        let lock = overlay.lock().unwrap();
568        let mut overlay = lock.overlay.lock().unwrap();
569
570        let gas_values = verify_result?;
571
572        if !write {
573            debug!(target: "validator::add_transactions", "Skipping apply of state updates because write=false");
574            return Ok(gas_values)
575        }
576
577        debug!(target: "validator::add_transactions", "Applying overlay changes");
578        overlay.apply()?;
579        Ok(gas_values)
580    }
581
582    /// Validate a producer `Transaction` and apply it if valid. In
583    /// case the transactions fail, ir will be returned to the caller.
584    /// The function takes a boolean called `write` which tells it to
585    /// actually write the state transitions to the database.
586    ///
587    /// Note: This function should only be used in tests and always
588    /// remember to purge new trees from the database if not needed.
589    pub async fn add_test_producer_transaction(
590        &self,
591        tx: &Transaction,
592        verifying_block_height: u32,
593        block_target: u32,
594        write: bool,
595    ) -> Result<()> {
596        debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
597        let overlay = BlockchainOverlay::new(&self.blockchain)?;
598
599        // Verify transaction
600        let mut erroneous_txs = vec![];
601        if let Err(e) = verify_producer_transaction(
602            &overlay,
603            verifying_block_height,
604            block_target,
605            tx,
606            &mut MerkleTree::new(1),
607        )
608        .await
609        {
610            warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {e}");
611            erroneous_txs.push(tx.clone());
612        }
613
614        let lock = overlay.lock().unwrap();
615        let mut overlay = lock.overlay.lock().unwrap();
616        if !erroneous_txs.is_empty() {
617            warn!(target: "validator::add_test_producer_transaction", "Erroneous transactions found in set");
618            return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
619        }
620
621        if !write {
622            debug!(target: "validator::add_test_producer_transaction", "Skipping apply of state updates because write=false");
623            return Ok(())
624        }
625
626        debug!(target: "validator::add_test_producer_transaction", "Applying overlay changes");
627        overlay.apply()?;
628        Ok(())
629    }
630
631    /// Retrieve all existing blocks and try to apply them
632    /// to an in memory overlay to verify their correctness.
633    /// Be careful as this will try to load everything in memory.
634    ///
635    /// Note: Always remember to purge new trees from the database if
636    /// not needed.
637    pub async fn validate_blockchain(
638        &self,
639        pow_target: u32,
640        pow_fixed_difficulty: Option<BigUint>,
641    ) -> Result<()> {
642        // An empty blockchain is considered valid
643        let mut blocks_count = self.blockchain.len() as u32;
644        info!(target: "validator::validate_blockchain", "Validating {blocks_count} blocks...");
645        if blocks_count == 0 {
646            info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
647            return Ok(())
648        }
649
650        // Create an in memory blockchain overlay
651        let sled_db = sled::Config::new().temporary(true).open()?;
652        let blockchain = Blockchain::new(&sled_db)?;
653        let overlay = BlockchainOverlay::new(&blockchain)?;
654
655        // Set previous
656        let mut previous = self.blockchain.genesis_block()?;
657
658        // Deploy native wasm contracts
659        deploy_native_contracts(&overlay, pow_target).await?;
660
661        // Update the contracts states monotree
662        let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
663        overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
664
665        // Validate genesis block
666        verify_genesis_block(&overlay, &[diff], &previous, pow_target).await?;
667        info!(target: "validator::validate_blockchain", "Genesis block validated successfully!");
668
669        // Write the changes to the in memory db
670        overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
671
672        // Create a PoW module to validate each block
673        let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, Some(0))?;
674
675        // Keep track of all block database state diffs
676        let mut diffs = vec![];
677
678        // Validate and insert each block
679        info!(target: "validator::validate_blockchain", "Validating rest blocks...");
680        blocks_count -= 1;
681        let mut index = 1;
682        while index <= blocks_count {
683            // Grab block
684            let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
685
686            // Verify block
687            if let Err(e) = verify_block(
688                &overlay,
689                &diffs,
690                &mut module,
691                &block,
692                &previous,
693                false,
694                self.verify_fees,
695            )
696            .await
697            {
698                error!(target: "validator::validate_blockchain", "Erroneous block found in set: {e}");
699                return Err(Error::BlockIsInvalid(block.hash().as_string()))
700            };
701
702            // Update PoW module
703            module.append(&block.header, &module.next_difficulty()?)?;
704
705            // Store block database state diff
706            let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
707            diffs.push(diff);
708
709            // Use last inserted block as next iteration previous
710            previous = block;
711
712            info!(target: "validator::validate_blockchain", "Block {index}/{blocks_count} validated successfully!");
713            index += 1;
714        }
715
716        info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
717        Ok(())
718    }
719
720    /// Auxiliary function to grab current mining RandomX key,
721    /// based on next block height.
722    /// If no forks exist, returns the canonical key.
723    pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
724        self.consensus.current_mining_randomx_key().await
725    }
726
727    /// Auxiliary function to grab best current fork full clone.
728    pub async fn best_current_fork(&self) -> Result<Fork> {
729        self.consensus.best_current_fork().await
730    }
731
732    /// Auxiliary function to retrieve current best fork next block
733    /// height.
734    pub async fn best_fork_next_block_height(&self) -> Result<u32> {
735        let index = best_fork_index(&self.consensus.forks)?;
736        let fork = &self.consensus.forks[index];
737        let next_block_height = fork.get_next_block_height()?;
738
739        Ok(next_block_height)
740    }
741
742    /// Auxiliary function to reset the validator blockchain and
743    /// consensus states to the provided block height.
744    pub async fn reset_to_height(&mut self, height: u32) -> Result<()> {
745        info!(target: "validator::reset_to_height", "Resetting validator to height: {height}");
746        // Reset our databasse to provided height
747        self.blockchain.reset_to_height(height)?;
748
749        // Reset consensus PoW module
750        self.consensus.reset_pow_module().await?;
751
752        // Purge current forks
753        self.consensus.purge_forks().await?;
754
755        info!(target: "validator::reset_to_height", "Validator reset successfully!");
756
757        Ok(())
758    }
759
760    /// Auxiliary function to rebuild the block difficulties database
761    /// based on current validator blockchain.
762    /// Be careful as this will try to load everything in memory.
763    pub async fn rebuild_block_difficulties(
764        &self,
765        pow_target: u32,
766        pow_fixed_difficulty: Option<BigUint>,
767    ) -> Result<()> {
768        info!(target: "validator::rebuild_block_difficulties", "Rebuilding validator block difficulties...");
769        // Clear the block difficulties tree
770        self.blockchain.blocks.difficulty.clear()?;
771
772        // An empty blockchain doesn't have difficulty records
773        let mut blocks_count = self.blockchain.len() as u32;
774        info!(target: "validator::rebuild_block_difficulties", "Rebuilding {blocks_count} block difficulties...");
775        if blocks_count == 0 {
776            info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
777            return Ok(())
778        }
779
780        // Create a PoW module and an in memory overlay to compute each
781        // block difficulty.
782        let mut module =
783            PoWModule::new(self.blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
784
785        // Grab genesis block difficulty to access current ranks
786        let genesis_block = self.blockchain.genesis_block()?;
787        let last_difficulty = BlockDifficulty::genesis(genesis_block.header.timestamp);
788        let mut targets_rank = last_difficulty.ranks.targets_rank;
789        let mut hashes_rank = last_difficulty.ranks.hashes_rank;
790
791        // Grab each block to compute its difficulty
792        blocks_count -= 1;
793        let mut index = 1;
794        while index <= blocks_count {
795            // Grab block
796            let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
797
798            // Calculate block rank
799            let (next_difficulty, target_distance_sq, hash_distance_sq) =
800                block_rank(&mut module, &block)?;
801
802            // Update chain ranks
803            targets_rank += target_distance_sq.clone();
804            hashes_rank += hash_distance_sq.clone();
805
806            // Generate block difficulty and update PoW module
807            let cumulative_difficulty =
808                module.cumulative_difficulty.clone() + next_difficulty.clone();
809            let ranks = BlockRanks::new(
810                target_distance_sq,
811                targets_rank.clone(),
812                hash_distance_sq,
813                hashes_rank.clone(),
814            );
815            let block_difficulty = BlockDifficulty::new(
816                block.header.height,
817                block.header.timestamp,
818                next_difficulty,
819                cumulative_difficulty,
820                ranks,
821            );
822            module.append(&block.header, &block_difficulty.difficulty)?;
823
824            // Add difficulty to database
825            self.blockchain.blocks.insert_difficulty(&[block_difficulty])?;
826
827            info!(target: "validator::rebuild_block_difficulties", "Block {index}/{blocks_count} difficulty added successfully!");
828            index += 1;
829        }
830
831        // Flush the database
832        self.blockchain.sled_db.flush()?;
833
834        info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
835
836        Ok(())
837    }
838}