| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2026 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::{collections::HashMap, sync::Arc};
- use darkfi_sdk::{crypto::MerkleTree, fee::minimum_fee};
- use kvdb_overlay::Database;
- use num_bigint::BigUint;
- use smol::lock::RwLock;
- use tracing::{debug, error, info, warn};
- use crate::{
- blockchain::{
- block_store::{BlockDifficulty, BlockInfo, BlockRanks},
- Blockchain, BlockchainOverlay, HeaderHash,
- },
- error::TxVerifyFailed,
- tx::Transaction,
- util::time::Timestamp,
- zk::VerifyingKey,
- Error, Result,
- };
- /// DarkFi consensus module
- pub mod consensus;
- use consensus::{Consensus, Fork, Proposal};
- /// DarkFi PoW module
- pub mod pow;
- use pow::PoWModule;
- /// RandomX infrastructure
- pub mod randomx_factory;
- pub use randomx_factory::RandomXFactory;
- /// Verification functions
- pub mod verification;
- use verification::{
- verify_block, verify_checkpoint_block, verify_genesis_block, verify_producer_transaction,
- verify_transaction, verify_transactions,
- };
- /// Fee calculation helpers
- pub mod fees;
- /// Helper utilities
- pub mod utils;
- use utils::{best_fork_index, block_rank, deploy_native_contracts};
- /// Configuration for initializing [`Validator`]
- #[derive(Clone)]
- pub struct ValidatorConfig {
- /// Currently configured confirmation security threshold
- pub confirmation_threshold: usize,
- /// Currently configured max in-memory forks to maintain.
- pub max_forks: usize,
- /// Currently configured PoW target
- pub pow_target: u32,
- /// Optional fixed difficulty, for testing purposes
- pub pow_fixed_difficulty: Option<BigUint>,
- /// Genesis block
- pub genesis_block: BlockInfo,
- /// Flag to enable tx fee verification
- pub verify_fees: bool,
- }
- /// Atomic pointer to validator.
- pub type ValidatorPtr = Arc<RwLock<Validator>>;
- /// This struct represents a DarkFi validator node.
- pub struct Validator {
- /// Canonical (confirmed) blockchain
- pub blockchain: Blockchain,
- /// Hot/Live data used by the consensus algorithm
- pub consensus: Consensus,
- /// Flag signalling if the node is synced
- pub synced: bool,
- /// Flag to enable tx fee verification
- pub verify_fees: bool,
- }
- impl Validator {
- pub async fn new(kvdb: &Database, config: &ValidatorConfig) -> Result<ValidatorPtr> {
- info!(target: "validator::new", "Initializing Validator");
- info!(target: "validator::new", "Initializing Blockchain");
- let blockchain = Blockchain::new(kvdb)?;
- // Create an overlay over whole blockchain so we can write
- // stuff.
- let overlay = BlockchainOverlay::new(&blockchain)?;
- // Deploy native wasm contracts
- deploy_native_contracts(&overlay, config.pow_target).await?;
- // Update the contracts states monotree in case native
- // contracts zkas has changed.
- let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
- overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
- // Add genesis block if blockchain is empty
- if blockchain.genesis().is_err() {
- info!(target: "validator::new", "Appending genesis block");
- verify_genesis_block(&overlay, &[diff], &config.genesis_block, config.pow_target)
- .await?;
- };
- // Write the changes to the actual chain db
- overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
- info!(target: "validator::new", "Initializing Consensus");
- let consensus = Consensus::new(
- blockchain.clone(),
- config.confirmation_threshold,
- config.max_forks,
- config.pow_target,
- config.pow_fixed_difficulty.clone(),
- )?;
- // Create the actual state
- let state = Arc::new(RwLock::new(Self {
- blockchain,
- consensus,
- synced: false,
- verify_fees: config.verify_fees,
- }));
- info!(target: "validator::new", "Finished initializing validator");
- Ok(state)
- }
- /// Auxiliary function to compute provided transaction's required
- /// fee, against current best fork. The function takes a boolean
- /// called `verify_fee` to overwrite the nodes configured
- /// `verify_fees` flag.
- ///
- /// Note: Always remember to purge new trees from the database if
- /// not needed.
- pub async fn calculate_fee(&self, tx: &Transaction, verify_fee: bool) -> Result<u64> {
- // Grab the best fork to verify against
- let index = best_fork_index(&self.consensus.forks)?;
- let fork = self.consensus.forks[index].full_clone()?;
- // Map of ZK proof verifying keys for the transaction
- let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
- for call in &tx.calls {
- vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
- }
- // Grab forks' next block height
- let next_block_height = fork.get_next_block_height()?;
- // Verify transaction to grab the gas used
- let verify_result = verify_transaction(
- &fork.overlay,
- next_block_height,
- self.consensus.module.target,
- tx,
- &mut MerkleTree::new(1),
- &mut vks,
- verify_fee,
- )
- .await?;
- Ok(minimum_fee(verify_result.total_gas_used())?)
- }
- /// The node retrieves a transaction, validates its state
- /// transition agains best fork, and appends it to the pending txs
- /// store if its valid.
- ///
- /// Note: Always remember to purge new trees from the database if
- /// not needed.
- pub async fn append_tx(&mut self, tx: &Transaction, write: bool) -> Result<()> {
- let tx_hash = tx.hash();
- // Check if we have already seen this tx in the pending store
- if self.blockchain.transactions.contains_pending(&tx_hash)? {
- debug!(target: "validator::append_tx", "We have already seen pending tx: {tx_hash}");
- return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
- }
- // Grab the best fork to verify against
- let index = best_fork_index(&self.consensus.forks)?;
- let fork = self.consensus.forks[index].full_clone()?;
- // Check if we have already seen this tx. This checks both the
- // fork cache and the actual database.
- if fork.overlay.lock().unwrap().transactions.contains(&tx_hash)? {
- debug!(target: "validator::append_tx", "We have already seen tx: {tx_hash}");
- return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
- }
- // Verify state transition
- info!(target: "validator::append_tx", "Starting state transition validation for tx: {tx_hash}");
- // Map of ZK proof verifying keys for the transaction
- let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
- for call in &tx.calls {
- vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
- }
- // Grab forks' next block height
- let next_block_height = fork.get_next_block_height()?;
- // Verify transaction
- verify_transaction(
- &fork.overlay,
- next_block_height,
- self.consensus.module.target,
- tx,
- &mut MerkleTree::new(1),
- &mut vks,
- self.verify_fees,
- )
- .await?;
- // Add transaction to pending txs store
- if write {
- self.blockchain.add_pending_txs(std::slice::from_ref(tx))?;
- info!(target: "validator::append_tx", "Appended tx {tx_hash} to pending txs store");
- }
- Ok(())
- }
- /// The node tries to append provided proposal to its consensus
- /// state.
- pub async fn append_proposal(
- &mut self,
- proposal: &Proposal,
- timestamp_bound: Option<Timestamp>,
- ) -> Result<()> {
- self.consensus.append_proposal(proposal, timestamp_bound, self.verify_fees).await
- }
- /// The node checks if best fork can be confirmed.
- /// If proposals can be confirmed, node appends them to canonical,
- /// and resets the current forks.
- pub async fn confirmation(&mut self) -> Result<Vec<BlockInfo>> {
- info!(target: "validator::confirmation", "Performing confirmation check");
- // Grab best fork index that can be confirmed
- let confirmed_fork = self.consensus.confirmation().await?;
- if confirmed_fork.is_none() {
- info!(target: "validator::confirmation", "No proposals can be confirmed");
- return Ok(vec![])
- }
- // Grab the actual best fork
- let confirmed_fork = confirmed_fork.unwrap();
- let fork = &mut self.consensus.forks[confirmed_fork];
- // Find the excess over confirmation threshold
- let excess = (fork.proposals.len() - self.consensus.confirmation_threshold) + 1;
- // Grab confirmed proposals and update fork's sequences
- let rest_proposals = fork.proposals.split_off(excess);
- let rest_diffs = fork.diffs.split_off(excess);
- let confirmed_proposals = fork.proposals.clone();
- let diffs = fork.diffs.clone();
- fork.proposals = rest_proposals;
- fork.diffs = rest_diffs;
- // Grab confirmed proposals blocks
- let confirmed_blocks =
- fork.overlay.lock().unwrap().get_blocks_by_hash(&confirmed_proposals)?;
- // Apply confirmed proposals diffs and update PoW module
- let mut module = self.consensus.module.clone();
- let mut confirmed_txs = vec![];
- let mut state_inverse_diffs_heights = vec![];
- let mut state_inverse_diffs = vec![];
- info!(target: "validator::confirmation", "Confirming proposals:");
- for (index, proposal) in confirmed_proposals.iter().enumerate() {
- info!(target: "validator::confirmation", "\t{proposal} ({}) - {}", confirmed_blocks[index].header.pow_data, confirmed_blocks[index].header.height);
- fork.overlay.lock().unwrap().overlay.lock().unwrap().apply_diff(&diffs[index])?;
- let next_difficulty = module.next_difficulty()?;
- module.append(&confirmed_blocks[index].header, &next_difficulty)?;
- confirmed_txs.extend_from_slice(&confirmed_blocks[index].txs);
- state_inverse_diffs_heights.push(confirmed_blocks[index].header.height);
- state_inverse_diffs.push(diffs[index].inverse());
- }
- self.consensus.module = module;
- // Store the block inverse diffs
- self.blockchain
- .blocks
- .insert_state_inverse_diff(&state_inverse_diffs_heights, &state_inverse_diffs)?;
- // Reset forks starting with the confirmed blocks
- self.consensus.reset_forks(&confirmed_proposals, &confirmed_fork, &confirmed_txs).await?;
- info!(target: "validator::confirmation", "Confirmation completed!");
- Ok(confirmed_blocks)
- }
- /// Apply provided set of [`BlockInfo`] without doing formal
- /// verification. A set of [`HeaderHash`] is also provided, to
- /// verify that the provided block hash matches the expected header
- /// one.
- ///
- /// Note: this function should only be used for blocks received
- /// using a checkpoint, since in that case we enforce the node to
- /// follow the sequence, assuming all its blocks are valid.
- /// Additionally, it will update any forks to a single empty one,
- /// holding the updated module. Always remember to purge new trees
- /// from the database if not needed.
- pub async fn add_checkpoint_blocks(
- &mut self,
- blocks: &[BlockInfo],
- headers: &[HeaderHash],
- ) -> Result<()> {
- // Check provided sequences are the same length
- if blocks.len() != headers.len() {
- return Err(Error::InvalidInputLengths)
- }
- debug!(target: "validator::add_checkpoint_blocks", "Instantiating BlockchainOverlay");
- let overlay = BlockchainOverlay::new(&self.blockchain)?;
- // Retrieve last block difficulty to access current ranks
- let last_difficulty = self.blockchain.last_block_difficulty()?;
- let mut current_targets_rank = last_difficulty.ranks.targets_rank;
- let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
- // Grab current PoW module to validate each block
- let mut module = self.consensus.module.clone();
- // Keep track of all blocks transactions to remove them from
- // pending txs store.
- let mut removed_txs = vec![];
- // Keep track of all block database state diffs and their
- // inverse.
- let mut diffs_heights = vec![];
- let mut diffs = vec![];
- let mut inverse_diffs = vec![];
- // Validate and insert each block
- for (index, block) in blocks.iter().enumerate() {
- // Verify block
- match verify_checkpoint_block(&overlay, &diffs, block, &headers[index], module.target)
- .await
- {
- Ok(()) => { /* Do nothing */ }
- // Skip already existing block
- Err(Error::BlockAlreadyExists(_)) => continue,
- Err(e) => {
- error!(target: "validator::add_checkpoint_blocks", "Erroneous block found in set: {e}");
- return Err(Error::BlockIsInvalid(block.hash().as_string()))
- }
- };
- // Calculate block rank
- let (next_difficulty, target_distance_sq, hash_distance_sq) =
- block_rank(&mut module, block)?;
- // Update current ranks
- current_targets_rank += target_distance_sq.clone();
- current_hashes_rank += hash_distance_sq.clone();
- // Generate block difficulty and update PoW module
- let cumulative_difficulty =
- module.cumulative_difficulty.clone() + next_difficulty.clone();
- let ranks = BlockRanks::new(
- target_distance_sq,
- current_targets_rank.clone(),
- hash_distance_sq,
- current_hashes_rank.clone(),
- );
- let block_difficulty = BlockDifficulty::new(
- block.header.height,
- block.header.timestamp,
- next_difficulty,
- cumulative_difficulty,
- ranks,
- );
- module.append_difficulty(&overlay, &block.header, block_difficulty)?;
- // Store block transactions
- for tx in &block.txs {
- removed_txs.push(tx.clone());
- }
- // Store block database state diff and its inverse
- diffs_heights.push(block.header.height);
- let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
- inverse_diffs.push(diff.inverse());
- diffs.push(diff);
- }
- debug!(target: "validator::add_checkpoint_blocks", "Applying overlay changes");
- overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
- // Store the block diffs
- self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
- // Remove blocks transactions from pending txs store
- self.blockchain.remove_pending_txs(&removed_txs)?;
- // Update PoW module
- self.consensus.module = module.clone();
- // Update forks
- self.consensus.forks = vec![Fork::new(self.blockchain.clone(), module).await?];
- Ok(())
- }
- /// Validate a set of [`BlockInfo`] in sequence and apply them if
- /// all are valid.
- ///
- /// Note: this function should only be used in tests when we don't
- /// want to perform consensus logic and always remember to purge
- /// new trees from the database if not needed.
- pub async fn add_test_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
- debug!(target: "validator::add_test_blocks", "Instantiating BlockchainOverlay");
- let overlay = BlockchainOverlay::new(&self.blockchain)?;
- // Retrieve last block
- let mut previous = &overlay.lock().unwrap().last_block()?;
- // Retrieve last block difficulty to access current ranks
- let last_difficulty = self.blockchain.last_block_difficulty()?;
- let mut current_targets_rank = last_difficulty.ranks.targets_rank;
- let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
- // Grab current PoW module to validate each block
- let mut module = self.consensus.module.clone();
- // Keep track of all blocks transactions to remove them from
- // pending txs store.
- let mut removed_txs = vec![];
- // Keep track of all block database state diffs and their
- // inverse.
- let mut diffs_heights = vec![];
- let mut diffs = vec![];
- let mut inverse_diffs = vec![];
- // All blocks must be before the future timestamp upper bound
- let timestamp_bound = Some(module.future_timestamp_upper_bound()?);
- // Validate and insert each block
- for block in blocks {
- // Verify block
- match verify_block(
- &overlay,
- &diffs,
- &mut module,
- block,
- previous,
- timestamp_bound,
- self.verify_fees,
- )
- .await
- {
- Ok(()) => { /* Do nothing */ }
- // Skip already existing block
- Err(Error::BlockAlreadyExists(_)) => {
- previous = block;
- continue
- }
- Err(e) => {
- error!(target: "validator::add_test_blocks", "Erroneous block found in set: {e}");
- return Err(Error::BlockIsInvalid(block.hash().as_string()))
- }
- };
- // Calculate block rank
- let (next_difficulty, target_distance_sq, hash_distance_sq) =
- block_rank(&mut module, block)?;
- // Update current ranks
- current_targets_rank += target_distance_sq.clone();
- current_hashes_rank += hash_distance_sq.clone();
- // Generate block difficulty and update PoW module
- let cumulative_difficulty =
- module.cumulative_difficulty.clone() + next_difficulty.clone();
- let ranks = BlockRanks::new(
- target_distance_sq,
- current_targets_rank.clone(),
- hash_distance_sq,
- current_hashes_rank.clone(),
- );
- let block_difficulty = BlockDifficulty::new(
- block.header.height,
- block.header.timestamp,
- next_difficulty,
- cumulative_difficulty,
- ranks,
- );
- module.append_difficulty(&overlay, &block.header, block_difficulty)?;
- // Store block transactions
- for tx in &block.txs {
- removed_txs.push(tx.clone());
- }
- // Store block database state diff and its inverse
- diffs_heights.push(block.header.height);
- let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
- inverse_diffs.push(diff.inverse());
- diffs.push(diff);
- // Use last inserted block as next iteration previous
- previous = block;
- }
- debug!(target: "validator::add_test_blocks", "Applying overlay changes");
- overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
- // Store the block diffs
- self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
- // Remove blocks transactions from pending txs store
- self.blockchain.remove_pending_txs(&removed_txs)?;
- // Update PoW module
- self.consensus.module = module;
- Ok(())
- }
- /// Validate a set of [`Transaction`] in sequence and apply them if
- /// all are valid. In case any of the transactions fail, they will
- /// be returned to the caller. The function takes a boolean called
- /// `write` which tells it to actually write the state transitions
- /// to the database, and a boolean called `verify_fees` to
- /// overwrite the nodes configured `verify_fees` flag.
- ///
- /// Returns the total gas used and total paid fees for the given
- /// transactions.
- ///
- /// Note: This function should only be used in tests and always
- /// remember to purge new trees from the database if not needed.
- pub async fn add_test_transactions(
- &self,
- txs: &[Transaction],
- verifying_block_height: u32,
- block_target: u32,
- write: bool,
- verify_fees: bool,
- ) -> Result<(u64, u64)> {
- debug!(target: "validator::add_transactions", "Instantiating BlockchainOverlay");
- let overlay = BlockchainOverlay::new(&self.blockchain)?;
- // Verify all transactions and get erroneous ones
- let verify_result = verify_transactions(
- &overlay,
- verifying_block_height,
- block_target,
- txs,
- &mut MerkleTree::new(1),
- verify_fees,
- )
- .await;
- let lock = overlay.lock().unwrap();
- let mut overlay = lock.overlay.lock().unwrap();
- let gas_values = verify_result?;
- if !write {
- debug!(target: "validator::add_transactions", "Skipping apply of state updates because write=false");
- return Ok(gas_values)
- }
- debug!(target: "validator::add_transactions", "Applying overlay changes");
- overlay.apply()?;
- Ok(gas_values)
- }
- /// Validate a producer `Transaction` and apply it if valid. In
- /// case the transactions fail, ir will be returned to the caller.
- /// The function takes a boolean called `write` which tells it to
- /// actually write the state transitions to the database.
- ///
- /// Note: This function should only be used in tests and always
- /// remember to purge new trees from the database if not needed.
- pub async fn add_test_producer_transaction(
- &self,
- tx: &Transaction,
- verifying_block_height: u32,
- block_target: u32,
- write: bool,
- ) -> Result<()> {
- debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
- let overlay = BlockchainOverlay::new(&self.blockchain)?;
- // Verify transaction
- let mut erroneous_txs = vec![];
- if let Err(e) = verify_producer_transaction(
- &overlay,
- verifying_block_height,
- block_target,
- tx,
- &mut MerkleTree::new(1),
- )
- .await
- {
- warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {e}");
- erroneous_txs.push(tx.clone());
- }
- let lock = overlay.lock().unwrap();
- let mut overlay = lock.overlay.lock().unwrap();
- if !erroneous_txs.is_empty() {
- warn!(target: "validator::add_test_producer_transaction", "Erroneous transactions found in set");
- return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
- }
- if !write {
- debug!(target: "validator::add_test_producer_transaction", "Skipping apply of state updates because write=false");
- return Ok(())
- }
- debug!(target: "validator::add_test_producer_transaction", "Applying overlay changes");
- overlay.apply()?;
- Ok(())
- }
- /// Retrieve all existing blocks and try to apply them
- /// to an in memory overlay to verify their correctness.
- /// Be careful as this will try to load everything in memory.
- ///
- /// Note: Always remember to purge new trees from the database if
- /// not needed.
- pub async fn validate_blockchain(
- &self,
- pow_target: u32,
- pow_fixed_difficulty: Option<BigUint>,
- ) -> Result<()> {
- // An empty blockchain is considered valid
- let mut blocks_count = self.blockchain.len()? as u32;
- info!(target: "validator::validate_blockchain", "Validating {blocks_count} blocks...");
- if blocks_count == 0 {
- info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
- return Ok(())
- }
- // Create an in memory blockchain overlay
- let (kvdb, _folder) = Database::open_temp()?;
- let blockchain = Blockchain::new(&kvdb)?;
- let overlay = BlockchainOverlay::new(&blockchain)?;
- // Set previous
- let mut previous = self.blockchain.genesis_block()?;
- // Deploy native wasm contracts
- deploy_native_contracts(&overlay, pow_target).await?;
- // Update the contracts states monotree
- let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
- overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
- // Validate genesis block
- verify_genesis_block(&overlay, &[diff], &previous, pow_target).await?;
- info!(target: "validator::validate_blockchain", "Genesis block validated successfully!");
- // Write the changes to the in memory db
- overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
- // Create a PoW module to validate each block
- let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, Some(0))?;
- // Keep track of all block database state diffs
- let mut diffs = vec![];
- // All blocks must be before the future timestamp upper bound
- let timestamp_bound = Some(module.future_timestamp_upper_bound()?);
- // Validate and insert each block
- info!(target: "validator::validate_blockchain", "Validating rest blocks...");
- blocks_count -= 1;
- let mut index = 1;
- while index <= blocks_count {
- // Grab block
- let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
- // Verify block
- if let Err(e) = verify_block(
- &overlay,
- &diffs,
- &mut module,
- &block,
- &previous,
- timestamp_bound,
- self.verify_fees,
- )
- .await
- {
- error!(target: "validator::validate_blockchain", "Erroneous block found in set: {e}");
- return Err(Error::BlockIsInvalid(block.hash().as_string()))
- };
- // Update PoW module
- module.append(&block.header, &module.next_difficulty()?)?;
- // Store block database state diff
- let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
- diffs.push(diff);
- // Use last inserted block as next iteration previous
- previous = block;
- info!(target: "validator::validate_blockchain", "Block {index}/{blocks_count} validated successfully!");
- index += 1;
- }
- info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
- Ok(())
- }
- /// Auxiliary function to grab current mining RandomX key,
- /// based on next block height.
- /// If no forks exist, returns the canonical key.
- pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
- self.consensus.current_mining_randomx_key().await
- }
- /// Auxiliary function to grab best current fork full clone.
- pub async fn best_current_fork(&self) -> Result<Fork> {
- self.consensus.best_current_fork().await
- }
- /// Auxiliary function to retrieve current best fork next block
- /// height.
- pub async fn best_fork_next_block_height(&self) -> Result<u32> {
- let index = best_fork_index(&self.consensus.forks)?;
- let fork = &self.consensus.forks[index];
- let next_block_height = fork.get_next_block_height()?;
- Ok(next_block_height)
- }
- /// Auxiliary function to reset the validator blockchain and
- /// consensus states to the provided block height.
- pub async fn reset_to_height(&mut self, height: u32) -> Result<()> {
- info!(target: "validator::reset_to_height", "Resetting validator to height: {height}");
- // Reset our databasse to provided height
- self.blockchain.reset_to_height(height)?;
- // Reset consensus PoW module
- self.consensus.reset_pow_module().await?;
- // Purge current forks
- self.consensus.purge_forks().await?;
- info!(target: "validator::reset_to_height", "Validator reset successfully!");
- Ok(())
- }
- /// Auxiliary function to rebuild the block difficulties database
- /// based on current validator blockchain.
- /// Be careful as this will try to load everything in memory.
- pub async fn rebuild_block_difficulties(
- &self,
- pow_target: u32,
- pow_fixed_difficulty: Option<BigUint>,
- ) -> Result<()> {
- info!(target: "validator::rebuild_block_difficulties", "Rebuilding validator block difficulties...");
- // Clear the block difficulties tree
- self.blockchain.blocks.difficulty.clear()?;
- // An empty blockchain doesn't have difficulty records
- let mut blocks_count = self.blockchain.len()? as u32;
- info!(target: "validator::rebuild_block_difficulties", "Rebuilding {blocks_count} block difficulties...");
- if blocks_count == 0 {
- info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
- return Ok(())
- }
- // Create a PoW module and an in memory overlay to compute each
- // block difficulty.
- let mut module =
- PoWModule::new(self.blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
- // Grab genesis block difficulty to access current ranks
- let genesis_block = self.blockchain.genesis_block()?;
- let last_difficulty = BlockDifficulty::genesis(genesis_block.header.timestamp);
- let mut targets_rank = last_difficulty.ranks.targets_rank;
- let mut hashes_rank = last_difficulty.ranks.hashes_rank;
- // Grab each block to compute its difficulty
- blocks_count -= 1;
- let mut index = 1;
- while index <= blocks_count {
- // Grab block
- let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
- // Calculate block rank
- let (next_difficulty, target_distance_sq, hash_distance_sq) =
- block_rank(&mut module, &block)?;
- // Update chain ranks
- targets_rank += target_distance_sq.clone();
- hashes_rank += hash_distance_sq.clone();
- // Generate block difficulty and update PoW module
- let cumulative_difficulty =
- module.cumulative_difficulty.clone() + next_difficulty.clone();
- let ranks = BlockRanks::new(
- target_distance_sq,
- targets_rank.clone(),
- hash_distance_sq,
- hashes_rank.clone(),
- );
- let block_difficulty = BlockDifficulty::new(
- block.header.height,
- block.header.timestamp,
- next_difficulty,
- cumulative_difficulty,
- ranks,
- );
- module.append(&block.header, &block_difficulty.difficulty)?;
- // Add difficulty to database
- self.blockchain.blocks.insert_difficulty(&[block_difficulty])?;
- info!(target: "validator::rebuild_block_difficulties", "Block {index}/{blocks_count} difficulty added successfully!");
- index += 1;
- }
- // Flush the database
- self.blockchain.kvdb.flush_default_mode()?;
- info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
- Ok(())
- }
- }
|