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 }