1use std::{collections::HashMap, sync::Arc};
20
21use darkfi_sdk::{blockchain::compute_fee, crypto::MerkleTree};
22use num_bigint::BigUint;
23use sled_overlay::sled;
24use smol::lock::RwLock;
25use tracing::{debug, error, info, warn};
26
27use crate::{
28 blockchain::{
29 block_store::{BlockDifficulty, BlockInfo, BlockRanks},
30 Blockchain, BlockchainOverlay, HeaderHash,
31 },
32 error::TxVerifyFailed,
33 tx::Transaction,
34 zk::VerifyingKey,
35 Error, Result,
36};
37
38pub mod consensus;
40use consensus::{Consensus, Fork, Proposal};
41
42pub mod pow;
44use pow::PoWModule;
45
46pub mod randomx_factory;
48pub use randomx_factory::RandomXFactory;
49
50pub mod verification;
52use verification::{
53 verify_block, verify_checkpoint_block, verify_genesis_block, verify_producer_transaction,
54 verify_transaction, verify_transactions,
55};
56
57pub mod fees;
59
60pub mod utils;
62use utils::{best_fork_index, block_rank, deploy_native_contracts};
63
64#[derive(Clone)]
66pub struct ValidatorConfig {
67 pub confirmation_threshold: usize,
69 pub max_forks: usize,
71 pub pow_target: u32,
73 pub pow_fixed_difficulty: Option<BigUint>,
75 pub genesis_block: BlockInfo,
77 pub verify_fees: bool,
79}
80
81pub type ValidatorPtr = Arc<RwLock<Validator>>;
83
84pub struct Validator {
86 pub blockchain: Blockchain,
88 pub consensus: Consensus,
90 pub synced: bool,
92 pub verify_fees: bool,
94}
95
96impl Validator {
97 pub async fn new(db: &sled::Db, config: &ValidatorConfig) -> Result<ValidatorPtr> {
98 info!(target: "validator::new", "Initializing Validator");
99
100 info!(target: "validator::new", "Initializing Blockchain");
101 let blockchain = Blockchain::new(db)?;
102
103 let overlay = BlockchainOverlay::new(&blockchain)?;
106
107 deploy_native_contracts(&overlay, config.pow_target).await?;
109
110 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
113 overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
114
115 if blockchain.genesis().is_err() {
117 info!(target: "validator::new", "Appending genesis block");
118 verify_genesis_block(&overlay, &[diff], &config.genesis_block, config.pow_target)
119 .await?;
120 };
121
122 overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
124
125 info!(target: "validator::new", "Initializing Consensus");
126 let consensus = Consensus::new(
127 blockchain.clone(),
128 config.confirmation_threshold,
129 config.max_forks,
130 config.pow_target,
131 config.pow_fixed_difficulty.clone(),
132 )?;
133
134 let state = Arc::new(RwLock::new(Self {
136 blockchain,
137 consensus,
138 synced: false,
139 verify_fees: config.verify_fees,
140 }));
141
142 info!(target: "validator::new", "Finished initializing validator");
143 Ok(state)
144 }
145
146 pub async fn calculate_fee(&self, tx: &Transaction, verify_fee: bool) -> Result<u64> {
154 let index = best_fork_index(&self.consensus.forks)?;
156 let fork = self.consensus.forks[index].full_clone()?;
157
158 let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
160 for call in &tx.calls {
161 vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
162 }
163
164 let next_block_height = fork.get_next_block_height()?;
166
167 let verify_result = verify_transaction(
169 &fork.overlay,
170 next_block_height,
171 self.consensus.module.target,
172 tx,
173 &mut MerkleTree::new(1),
174 &mut vks,
175 verify_fee,
176 )
177 .await?;
178
179 Ok(compute_fee(&verify_result.total_gas_used()))
180 }
181
182 pub async fn append_tx(&mut self, tx: &Transaction, write: bool) -> Result<()> {
189 let tx_hash = tx.hash();
190
191 if self.blockchain.transactions.contains_pending(&tx_hash)? {
193 debug!(target: "validator::append_tx", "We have already seen pending tx: {tx_hash}");
194 return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
195 }
196
197 let index = best_fork_index(&self.consensus.forks)?;
199 let fork = self.consensus.forks[index].full_clone()?;
200
201 if fork.overlay.lock().unwrap().transactions.contains(&tx_hash)? {
204 debug!(target: "validator::append_tx", "We have already seen tx: {tx_hash}");
205 return Err(TxVerifyFailed::AlreadySeenTx(tx_hash.as_string()).into())
206 }
207
208 info!(target: "validator::append_tx", "Starting state transition validation for tx: {tx_hash}");
210 let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
212 for call in &tx.calls {
213 vks.insert(call.data.contract_id.to_bytes(), HashMap::new());
214 }
215
216 let next_block_height = fork.get_next_block_height()?;
218
219 verify_transaction(
221 &fork.overlay,
222 next_block_height,
223 self.consensus.module.target,
224 tx,
225 &mut MerkleTree::new(1),
226 &mut vks,
227 self.verify_fees,
228 )
229 .await?;
230
231 if write {
233 self.blockchain.add_pending_txs(std::slice::from_ref(tx))?;
234 info!(target: "validator::append_tx", "Appended tx {tx_hash} to pending txs store");
235 }
236
237 Ok(())
238 }
239
240 pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
243 self.consensus.append_proposal(proposal, self.synced, self.verify_fees).await
244 }
245
246 pub async fn confirmation(&mut self) -> Result<Vec<BlockInfo>> {
250 info!(target: "validator::confirmation", "Performing confirmation check");
251
252 let confirmed_fork = self.consensus.confirmation().await?;
254 if confirmed_fork.is_none() {
255 info!(target: "validator::confirmation", "No proposals can be confirmed");
256 return Ok(vec![])
257 }
258
259 let confirmed_fork = confirmed_fork.unwrap();
261 let fork = &mut self.consensus.forks[confirmed_fork];
262
263 let excess = (fork.proposals.len() - self.consensus.confirmation_threshold) + 1;
265
266 let rest_proposals = fork.proposals.split_off(excess);
268 let rest_diffs = fork.diffs.split_off(excess);
269 let confirmed_proposals = fork.proposals.clone();
270 let diffs = fork.diffs.clone();
271 fork.proposals = rest_proposals;
272 fork.diffs = rest_diffs;
273
274 let confirmed_blocks =
276 fork.overlay.lock().unwrap().get_blocks_by_hash(&confirmed_proposals)?;
277
278 let mut module = self.consensus.module.clone();
280 let mut confirmed_txs = vec![];
281 let mut state_inverse_diffs_heights = vec![];
282 let mut state_inverse_diffs = vec![];
283 info!(target: "validator::confirmation", "Confirming proposals:");
284 for (index, proposal) in confirmed_proposals.iter().enumerate() {
285 info!(target: "validator::confirmation", "\t{proposal} ({}) - {}", confirmed_blocks[index].header.pow_data, confirmed_blocks[index].header.height);
286 fork.overlay.lock().unwrap().overlay.lock().unwrap().apply_diff(&diffs[index])?;
287 let next_difficulty = module.next_difficulty()?;
288 module.append(&confirmed_blocks[index].header, &next_difficulty)?;
289 confirmed_txs.extend_from_slice(&confirmed_blocks[index].txs);
290 state_inverse_diffs_heights.push(confirmed_blocks[index].header.height);
291 state_inverse_diffs.push(diffs[index].inverse());
292 }
293 self.consensus.module = module;
294
295 self.blockchain
297 .blocks
298 .insert_state_inverse_diff(&state_inverse_diffs_heights, &state_inverse_diffs)?;
299
300 self.consensus.reset_forks(&confirmed_proposals, &confirmed_fork, &confirmed_txs).await?;
302 info!(target: "validator::confirmation", "Confirmation completed!");
303
304 Ok(confirmed_blocks)
305 }
306
307 pub async fn add_checkpoint_blocks(
319 &mut self,
320 blocks: &[BlockInfo],
321 headers: &[HeaderHash],
322 ) -> Result<()> {
323 if blocks.len() != headers.len() {
325 return Err(Error::InvalidInputLengths)
326 }
327
328 debug!(target: "validator::add_checkpoint_blocks", "Instantiating BlockchainOverlay");
329 let overlay = BlockchainOverlay::new(&self.blockchain)?;
330
331 let last_difficulty = self.blockchain.last_block_difficulty()?;
333 let mut current_targets_rank = last_difficulty.ranks.targets_rank;
334 let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
335
336 let mut module = self.consensus.module.clone();
338
339 let mut removed_txs = vec![];
342
343 let mut diffs_heights = vec![];
346 let mut diffs = vec![];
347 let mut inverse_diffs = vec![];
348
349 for (index, block) in blocks.iter().enumerate() {
351 match verify_checkpoint_block(&overlay, &diffs, block, &headers[index], module.target)
353 .await
354 {
355 Ok(()) => { }
356 Err(Error::BlockAlreadyExists(_)) => continue,
358 Err(e) => {
359 error!(target: "validator::add_checkpoint_blocks", "Erroneous block found in set: {e}");
360 return Err(Error::BlockIsInvalid(block.hash().as_string()))
361 }
362 };
363
364 let (next_difficulty, target_distance_sq, hash_distance_sq) =
366 block_rank(&mut module, block)?;
367
368 current_targets_rank += target_distance_sq.clone();
370 current_hashes_rank += hash_distance_sq.clone();
371
372 let cumulative_difficulty =
374 module.cumulative_difficulty.clone() + next_difficulty.clone();
375 let ranks = BlockRanks::new(
376 target_distance_sq,
377 current_targets_rank.clone(),
378 hash_distance_sq,
379 current_hashes_rank.clone(),
380 );
381 let block_difficulty = BlockDifficulty::new(
382 block.header.height,
383 block.header.timestamp,
384 next_difficulty,
385 cumulative_difficulty,
386 ranks,
387 );
388 module.append_difficulty(&overlay, &block.header, block_difficulty)?;
389
390 for tx in &block.txs {
392 removed_txs.push(tx.clone());
393 }
394
395 diffs_heights.push(block.header.height);
397 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
398 inverse_diffs.push(diff.inverse());
399 diffs.push(diff);
400 }
401
402 debug!(target: "validator::add_checkpoint_blocks", "Applying overlay changes");
403 overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
404
405 self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
407
408 self.blockchain.remove_pending_txs(&removed_txs)?;
410
411 self.consensus.module = module.clone();
413
414 self.consensus.forks = vec![Fork::new(self.blockchain.clone(), module).await?];
416
417 Ok(())
418 }
419
420 pub async fn add_test_blocks(&mut self, blocks: &[BlockInfo]) -> Result<()> {
427 debug!(target: "validator::add_test_blocks", "Instantiating BlockchainOverlay");
428 let overlay = BlockchainOverlay::new(&self.blockchain)?;
429
430 let mut previous = &overlay.lock().unwrap().last_block()?;
432
433 let last_difficulty = self.blockchain.last_block_difficulty()?;
435 let mut current_targets_rank = last_difficulty.ranks.targets_rank;
436 let mut current_hashes_rank = last_difficulty.ranks.hashes_rank;
437
438 let mut module = self.consensus.module.clone();
440
441 let mut removed_txs = vec![];
444
445 let mut diffs_heights = vec![];
448 let mut diffs = vec![];
449 let mut inverse_diffs = vec![];
450
451 for block in blocks {
453 match verify_block(
455 &overlay,
456 &diffs,
457 &mut module,
458 block,
459 previous,
460 true,
461 self.verify_fees,
462 )
463 .await
464 {
465 Ok(()) => { }
466 Err(Error::BlockAlreadyExists(_)) => {
468 previous = block;
469 continue
470 }
471 Err(e) => {
472 error!(target: "validator::add_test_blocks", "Erroneous block found in set: {e}");
473 return Err(Error::BlockIsInvalid(block.hash().as_string()))
474 }
475 };
476
477 let (next_difficulty, target_distance_sq, hash_distance_sq) =
479 block_rank(&mut module, block)?;
480
481 current_targets_rank += target_distance_sq.clone();
483 current_hashes_rank += hash_distance_sq.clone();
484
485 let cumulative_difficulty =
487 module.cumulative_difficulty.clone() + next_difficulty.clone();
488 let ranks = BlockRanks::new(
489 target_distance_sq,
490 current_targets_rank.clone(),
491 hash_distance_sq,
492 current_hashes_rank.clone(),
493 );
494 let block_difficulty = BlockDifficulty::new(
495 block.header.height,
496 block.header.timestamp,
497 next_difficulty,
498 cumulative_difficulty,
499 ranks,
500 );
501 module.append_difficulty(&overlay, &block.header, block_difficulty)?;
502
503 for tx in &block.txs {
505 removed_txs.push(tx.clone());
506 }
507
508 diffs_heights.push(block.header.height);
510 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
511 inverse_diffs.push(diff.inverse());
512 diffs.push(diff);
513
514 previous = block;
516 }
517
518 debug!(target: "validator::add_test_blocks", "Applying overlay changes");
519 overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
520
521 self.blockchain.blocks.insert_state_inverse_diff(&diffs_heights, &inverse_diffs)?;
523
524 self.blockchain.remove_pending_txs(&removed_txs)?;
526
527 self.consensus.module = module;
529
530 Ok(())
531 }
532
533 pub async fn add_test_transactions(
546 &self,
547 txs: &[Transaction],
548 verifying_block_height: u32,
549 block_target: u32,
550 write: bool,
551 verify_fees: bool,
552 ) -> Result<(u64, u64)> {
553 debug!(target: "validator::add_transactions", "Instantiating BlockchainOverlay");
554 let overlay = BlockchainOverlay::new(&self.blockchain)?;
555
556 let verify_result = verify_transactions(
558 &overlay,
559 verifying_block_height,
560 block_target,
561 txs,
562 &mut MerkleTree::new(1),
563 verify_fees,
564 )
565 .await;
566
567 let lock = overlay.lock().unwrap();
568 let mut overlay = lock.overlay.lock().unwrap();
569
570 let gas_values = verify_result?;
571
572 if !write {
573 debug!(target: "validator::add_transactions", "Skipping apply of state updates because write=false");
574 return Ok(gas_values)
575 }
576
577 debug!(target: "validator::add_transactions", "Applying overlay changes");
578 overlay.apply()?;
579 Ok(gas_values)
580 }
581
582 pub async fn add_test_producer_transaction(
590 &self,
591 tx: &Transaction,
592 verifying_block_height: u32,
593 block_target: u32,
594 write: bool,
595 ) -> Result<()> {
596 debug!(target: "validator::add_test_producer_transaction", "Instantiating BlockchainOverlay");
597 let overlay = BlockchainOverlay::new(&self.blockchain)?;
598
599 let mut erroneous_txs = vec![];
601 if let Err(e) = verify_producer_transaction(
602 &overlay,
603 verifying_block_height,
604 block_target,
605 tx,
606 &mut MerkleTree::new(1),
607 )
608 .await
609 {
610 warn!(target: "validator::add_test_producer_transaction", "Transaction verification failed: {e}");
611 erroneous_txs.push(tx.clone());
612 }
613
614 let lock = overlay.lock().unwrap();
615 let mut overlay = lock.overlay.lock().unwrap();
616 if !erroneous_txs.is_empty() {
617 warn!(target: "validator::add_test_producer_transaction", "Erroneous transactions found in set");
618 return Err(TxVerifyFailed::ErroneousTxs(erroneous_txs).into())
619 }
620
621 if !write {
622 debug!(target: "validator::add_test_producer_transaction", "Skipping apply of state updates because write=false");
623 return Ok(())
624 }
625
626 debug!(target: "validator::add_test_producer_transaction", "Applying overlay changes");
627 overlay.apply()?;
628 Ok(())
629 }
630
631 pub async fn validate_blockchain(
638 &self,
639 pow_target: u32,
640 pow_fixed_difficulty: Option<BigUint>,
641 ) -> Result<()> {
642 let mut blocks_count = self.blockchain.len() as u32;
644 info!(target: "validator::validate_blockchain", "Validating {blocks_count} blocks...");
645 if blocks_count == 0 {
646 info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
647 return Ok(())
648 }
649
650 let sled_db = sled::Config::new().temporary(true).open()?;
652 let blockchain = Blockchain::new(&sled_db)?;
653 let overlay = BlockchainOverlay::new(&blockchain)?;
654
655 let mut previous = self.blockchain.genesis_block()?;
657
658 deploy_native_contracts(&overlay, pow_target).await?;
660
661 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&[])?;
663 overlay.lock().unwrap().contracts.update_state_monotree(&diff)?;
664
665 verify_genesis_block(&overlay, &[diff], &previous, pow_target).await?;
667 info!(target: "validator::validate_blockchain", "Genesis block validated successfully!");
668
669 overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
671
672 let mut module = PoWModule::new(blockchain, pow_target, pow_fixed_difficulty, Some(0))?;
674
675 let mut diffs = vec![];
677
678 info!(target: "validator::validate_blockchain", "Validating rest blocks...");
680 blocks_count -= 1;
681 let mut index = 1;
682 while index <= blocks_count {
683 let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
685
686 if let Err(e) = verify_block(
688 &overlay,
689 &diffs,
690 &mut module,
691 &block,
692 &previous,
693 false,
694 self.verify_fees,
695 )
696 .await
697 {
698 error!(target: "validator::validate_blockchain", "Erroneous block found in set: {e}");
699 return Err(Error::BlockIsInvalid(block.hash().as_string()))
700 };
701
702 module.append(&block.header, &module.next_difficulty()?)?;
704
705 let diff = overlay.lock().unwrap().overlay.lock().unwrap().diff(&diffs)?;
707 diffs.push(diff);
708
709 previous = block;
711
712 info!(target: "validator::validate_blockchain", "Block {index}/{blocks_count} validated successfully!");
713 index += 1;
714 }
715
716 info!(target: "validator::validate_blockchain", "Blockchain validated successfully!");
717 Ok(())
718 }
719
720 pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
724 self.consensus.current_mining_randomx_key().await
725 }
726
727 pub async fn best_current_fork(&self) -> Result<Fork> {
729 self.consensus.best_current_fork().await
730 }
731
732 pub async fn best_fork_next_block_height(&self) -> Result<u32> {
735 let index = best_fork_index(&self.consensus.forks)?;
736 let fork = &self.consensus.forks[index];
737 let next_block_height = fork.get_next_block_height()?;
738
739 Ok(next_block_height)
740 }
741
742 pub async fn reset_to_height(&mut self, height: u32) -> Result<()> {
745 info!(target: "validator::reset_to_height", "Resetting validator to height: {height}");
746 self.blockchain.reset_to_height(height)?;
748
749 self.consensus.reset_pow_module().await?;
751
752 self.consensus.purge_forks().await?;
754
755 info!(target: "validator::reset_to_height", "Validator reset successfully!");
756
757 Ok(())
758 }
759
760 pub async fn rebuild_block_difficulties(
764 &self,
765 pow_target: u32,
766 pow_fixed_difficulty: Option<BigUint>,
767 ) -> Result<()> {
768 info!(target: "validator::rebuild_block_difficulties", "Rebuilding validator block difficulties...");
769 self.blockchain.blocks.difficulty.clear()?;
771
772 let mut blocks_count = self.blockchain.len() as u32;
774 info!(target: "validator::rebuild_block_difficulties", "Rebuilding {blocks_count} block difficulties...");
775 if blocks_count == 0 {
776 info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
777 return Ok(())
778 }
779
780 let mut module =
783 PoWModule::new(self.blockchain.clone(), pow_target, pow_fixed_difficulty, Some(0))?;
784
785 let genesis_block = self.blockchain.genesis_block()?;
787 let last_difficulty = BlockDifficulty::genesis(genesis_block.header.timestamp);
788 let mut targets_rank = last_difficulty.ranks.targets_rank;
789 let mut hashes_rank = last_difficulty.ranks.hashes_rank;
790
791 blocks_count -= 1;
793 let mut index = 1;
794 while index <= blocks_count {
795 let block = self.blockchain.get_blocks_by_heights(&[index])?[0].clone();
797
798 let (next_difficulty, target_distance_sq, hash_distance_sq) =
800 block_rank(&mut module, &block)?;
801
802 targets_rank += target_distance_sq.clone();
804 hashes_rank += hash_distance_sq.clone();
805
806 let cumulative_difficulty =
808 module.cumulative_difficulty.clone() + next_difficulty.clone();
809 let ranks = BlockRanks::new(
810 target_distance_sq,
811 targets_rank.clone(),
812 hash_distance_sq,
813 hashes_rank.clone(),
814 );
815 let block_difficulty = BlockDifficulty::new(
816 block.header.height,
817 block.header.timestamp,
818 next_difficulty,
819 cumulative_difficulty,
820 ranks,
821 );
822 module.append(&block.header, &block_difficulty.difficulty)?;
823
824 self.blockchain.blocks.insert_difficulty(&[block_difficulty])?;
826
827 info!(target: "validator::rebuild_block_difficulties", "Block {index}/{blocks_count} difficulty added successfully!");
828 index += 1;
829 }
830
831 self.blockchain.sled_db.flush()?;
833
834 info!(target: "validator::rebuild_block_difficulties", "Validator block difficulties rebuilt successfully!");
835
836 Ok(())
837 }
838}