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