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