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