1use std::collections::{BTreeSet, HashMap, HashSet};
20
21use darkfi_sdk::{crypto::MerkleTree, tx::TransactionHash};
22use darkfi_serial::{async_trait, deserialize, SerialDecodable, SerialEncodable};
23use num_bigint::BigUint;
24use sled_overlay::{database::SledDbOverlayStateDiff, sled::IVec};
25use smol::lock::RwLock;
26use tracing::{debug, info, warn};
27
28use crate::{
29 blockchain::{
30 block_store::{BlockDifficulty, BlockRanks},
31 BlockInfo, Blockchain, BlockchainOverlay, BlockchainOverlayPtr, Header, HeaderHash,
32 },
33 runtime::vm_runtime::GAS_LIMIT,
34 tx::{Transaction, MAX_TX_CALLS},
35 validator::{
36 pow::{PoWModule, RANDOMX_KEY_CHANGE_DELAY, RANDOMX_KEY_CHANGING_HEIGHT},
37 utils::{best_fork_index, block_rank, find_extended_fork_index},
38 verification::{verify_proposal, verify_transaction},
39 },
40 zk::VerifyingKey,
41 Error, Result,
42};
43
44pub const BLOCK_GAS_LIMIT: u64 = GAS_LIMIT * MAX_TX_CALLS as u64 * 50;
46
47pub struct Consensus {
49 pub blockchain: Blockchain,
51 pub confirmation_threshold: usize,
53 pub forks: RwLock<Vec<Fork>>,
55 pub module: RwLock<PoWModule>,
57 pub append_lock: RwLock<()>,
59}
60
61impl Consensus {
62 pub fn new(
64 blockchain: Blockchain,
65 confirmation_threshold: usize,
66 pow_target: u32,
67 pow_fixed_difficulty: Option<BigUint>,
68 ) -> Result<Self> {
69 let forks = RwLock::new(vec![]);
70
71 let module = RwLock::new(PoWModule::new(
72 blockchain.clone(),
73 pow_target,
74 pow_fixed_difficulty,
75 None,
76 )?);
77
78 let append_lock = RwLock::new(());
79
80 Ok(Self { blockchain, confirmation_threshold, forks, module, append_lock })
81 }
82
83 pub async fn generate_empty_fork(&self) -> Result<()> {
85 debug!(target: "validator::consensus::generate_empty_fork", "Generating new empty fork...");
86 let mut forks = self.forks.write().await;
87 for fork in forks.iter() {
89 if fork.proposals.is_empty() {
90 debug!(target: "validator::consensus::generate_empty_fork", "An empty fork already exists.");
91 drop(forks);
92 return Ok(())
93 }
94 }
95 let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
96 forks.push(fork);
97 drop(forks);
98 debug!(target: "validator::consensus::generate_empty_fork", "Fork generated!");
99 Ok(())
100 }
101
102 pub async fn append_proposal(&self, proposal: &Proposal, verify_fees: bool) -> Result<()> {
105 debug!(target: "validator::consensus::append_proposal", "Appending proposal {}", proposal.hash);
106
107 let lock = self.forks.read().await;
109 for fork in lock.iter() {
110 for p in fork.proposals.iter().rev() {
111 if p == &proposal.hash {
112 drop(lock);
113 debug!(target: "validator::consensus::append_proposal", "Proposal {} already exists", proposal.hash);
114 return Err(Error::ProposalAlreadyExists)
115 }
116 }
117 }
118 if let Ok(canonical_headers) =
120 self.blockchain.blocks.get_order(&[proposal.block.header.height], true)
121 {
122 if canonical_headers[0].unwrap() == proposal.hash {
123 drop(lock);
124 debug!(target: "validator::consensus::append_proposal", "Proposal {} already exists", proposal.hash);
125 return Err(Error::ProposalAlreadyExists)
126 }
127 }
128 drop(lock);
129
130 let (mut fork, index) = verify_proposal(self, proposal, verify_fees).await?;
132
133 fork.append_proposal(proposal).await?;
135
136 let mut lock = self.forks.write().await;
142 match index {
143 Some(i) => {
144 if i < lock.len() && lock[i].proposals == fork.proposals[..fork.proposals.len() - 1]
145 {
146 lock[i] = fork;
147 } else {
148 lock.push(fork);
149 }
150 }
151 None => {
152 lock.push(fork);
153 }
154 }
155 drop(lock);
156
157 info!(target: "validator::consensus::append_proposal", "Appended proposal {}", proposal.hash);
158
159 Ok(())
160 }
161
162 pub async fn find_extended_fork(&self, proposal: &Proposal) -> Result<(Fork, Option<usize>)> {
168 let forks = self.forks.read().await;
170
171 let found = find_extended_fork_index(&forks, proposal);
173 if found.is_err() {
174 if let Err(Error::ProposalAlreadyExists) = found {
175 return Err(Error::ProposalAlreadyExists)
176 }
177
178 let (last_height, last_block) = self.blockchain.last()?;
180 if proposal.block.header.previous != last_block ||
181 proposal.block.header.height <= last_height
182 {
183 return Err(Error::ExtendedChainIndexNotFound)
184 }
185
186 for (f_index, fork) in forks.iter().enumerate() {
188 if fork.proposals.is_empty() {
189 return Ok((forks[f_index].full_clone()?, Some(f_index)))
190 }
191 }
192
193 let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
195 return Ok((fork, None))
196 }
197
198 let (f_index, p_index) = found.unwrap();
199 let original_fork = &forks[f_index];
200 if p_index == (original_fork.proposals.len() - 1) {
202 return Ok((original_fork.full_clone()?, Some(f_index)))
203 }
204
205 let mut fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
207 fork.proposals = original_fork.proposals[..p_index + 1].to_vec();
208 fork.diffs = original_fork.diffs[..p_index + 1].to_vec();
209
210 let blocks = &original_fork.overlay.lock().unwrap().get_blocks_by_hash(&fork.proposals)?;
212 for (index, block) in blocks.iter().enumerate() {
213 fork.overlay.lock().unwrap().overlay.lock().unwrap().add_diff(&fork.diffs[index])?;
215
216 let (next_target, next_difficulty) = fork.module.next_mine_target_and_difficulty()?;
218
219 let (target_distance_sq, hash_distance_sq) = block_rank(block, &next_target)?;
221
222 fork.module.append(&block.header, &next_difficulty)?;
224
225 fork.targets_rank += target_distance_sq;
227 fork.hashes_rank += hash_distance_sq;
228 }
229
230 drop(forks);
232
233 Ok((fork, None))
234 }
235
236 pub async fn confirmation(&self) -> Result<Option<usize>> {
245 debug!(target: "validator::consensus::confirmation", "Started confirmation check");
246
247 let forks = self.forks.read().await;
249 let index = best_fork_index(&forks)?;
250 let fork = &forks[index];
251
252 let length = fork.proposals.len();
254 if length < self.confirmation_threshold {
255 debug!(target: "validator::consensus::confirmation", "Nothing to confirme yet, best fork size: {length}");
256 drop(forks);
257 return Ok(None)
258 }
259
260 drop(forks);
262
263 Ok(Some(index))
264 }
265
266 pub async fn get_fork_header_hash(
269 &self,
270 height: u32,
271 fork_header: &HeaderHash,
272 ) -> Result<Option<HeaderHash>> {
273 let forks = self.forks.read().await;
275
276 let mut found = None;
278 'outer: for (index, fork) in forks.iter().enumerate() {
279 for p in fork.proposals.iter().rev() {
280 if p == fork_header {
281 found = Some(index);
282 break 'outer
283 }
284 }
285 }
286 if found.is_none() {
287 drop(forks);
288 return Ok(None)
289 }
290 let index = found.unwrap();
291
292 let header = forks[index].overlay.lock().unwrap().blocks.get_order(&[height], false)?[0];
294
295 drop(forks);
297
298 Ok(header)
299 }
300
301 pub async fn get_fork_headers(
305 &self,
306 headers: &[HeaderHash],
307 fork_header: &HeaderHash,
308 ) -> Result<Vec<Header>> {
309 let forks = self.forks.read().await;
311
312 let mut found = None;
314 'outer: for (index, fork) in forks.iter().enumerate() {
315 for p in fork.proposals.iter().rev() {
316 if p == fork_header {
317 found = Some(index);
318 break 'outer
319 }
320 }
321 }
322 let Some(index) = found else {
323 drop(forks);
324 return Ok(vec![])
325 };
326
327 let headers = forks[index].overlay.lock().unwrap().get_headers_by_hash(headers)?;
329
330 drop(forks);
332
333 Ok(headers)
334 }
335
336 pub async fn get_fork_proposals(
340 &self,
341 headers: &[HeaderHash],
342 fork_header: &HeaderHash,
343 ) -> Result<Vec<Proposal>> {
344 let forks = self.forks.read().await;
346
347 let mut found = None;
349 'outer: for (index, fork) in forks.iter().enumerate() {
350 for p in fork.proposals.iter().rev() {
351 if p == fork_header {
352 found = Some(index);
353 break 'outer
354 }
355 }
356 }
357 let Some(index) = found else {
358 drop(forks);
359 return Ok(vec![])
360 };
361
362 let blocks = forks[index].overlay.lock().unwrap().get_blocks_by_hash(headers)?;
364 let mut proposals = Vec::with_capacity(blocks.len());
365 for block in blocks {
366 proposals.push(Proposal::new(block));
367 }
368
369 drop(forks);
371
372 Ok(proposals)
373 }
374
375 pub async fn get_fork_proposals_after(
380 &self,
381 tip: HeaderHash,
382 fork_tip: Option<HeaderHash>,
383 limit: u32,
384 ) -> Result<Vec<Proposal>> {
385 let forks = self.forks.read().await;
387
388 let mut proposals = vec![];
390
391 let index = match fork_tip {
393 Some(fork_tip) => {
394 let mut found = None;
395 'outer: for (index, fork) in forks.iter().enumerate() {
396 for p in fork.proposals.iter().rev() {
397 if p == &fork_tip {
398 found = Some(index);
399 break 'outer
400 }
401 }
402 }
403 if found.is_none() {
404 drop(forks);
405 return Ok(proposals)
406 }
407 found.unwrap()
408 }
409 None => best_fork_index(&forks)?,
410 };
411
412 let Ok(existing_tips) = forks[index].overlay.lock().unwrap().get_blocks_by_hash(&[tip])
414 else {
415 drop(forks);
416 return Ok(proposals)
417 };
418
419 let last_block_height = forks[index].overlay.lock().unwrap().last()?.0;
421 if last_block_height - existing_tips[0].header.height >= limit {
422 drop(forks);
423 return Ok(proposals)
424 }
425
426 let headers = self.blockchain.blocks.get_all_after(existing_tips[0].header.height)?;
428 let blocks = self.blockchain.get_blocks_by_hash(&headers)?;
429 for block in blocks {
430 proposals.push(Proposal::new(block));
431 }
432 let blocks =
433 forks[index].overlay.lock().unwrap().get_blocks_by_hash(&forks[index].proposals)?;
434 for block in blocks {
435 proposals.push(Proposal::new(block));
436 }
437
438 drop(forks);
440
441 Ok(proposals)
442 }
443
444 pub async fn current_mining_randomx_key(&self) -> Result<HeaderHash> {
448 let forks = self.forks.read().await;
450
451 let (next_block_height, rx_keys) = if forks.is_empty() {
454 let (next_block_height, _) = self.blockchain.last()?;
455 (next_block_height + 1, self.module.read().await.darkfi_rx_keys)
456 } else {
457 let fork = &forks[best_fork_index(&forks)?];
459 let last = fork.last_proposal()?;
460 (last.block.header.height + 1, fork.module.darkfi_rx_keys)
461 };
462
463 if next_block_height > RANDOMX_KEY_CHANGING_HEIGHT &&
466 next_block_height % RANDOMX_KEY_CHANGING_HEIGHT == RANDOMX_KEY_CHANGE_DELAY
467 {
468 Ok(rx_keys.1.unwrap())
470 } else {
471 Ok(rx_keys.0)
472 }
473 }
474
475 pub async fn best_current_fork(&self) -> Result<Fork> {
477 let forks = self.forks.read().await;
478 let index = best_fork_index(&forks)?;
479 forks[index].full_clone()
480 }
481
482 pub async fn best_fork_last_header(&self) -> Result<(u32, HeaderHash)> {
485 let forks = self.forks.read().await;
487
488 if forks.is_empty() {
490 drop(forks);
491 return self.blockchain.last()
492 }
493
494 let fork = &forks[best_fork_index(&forks)?];
496
497 let last = fork.last_proposal()?;
499 drop(forks);
500 Ok((last.block.header.height, last.hash))
501 }
502
503 pub async fn reset_forks(
513 &self,
514 prefix: &[HeaderHash],
515 confirmed_fork_index: &usize,
516 confirmed_txs: &[Transaction],
517 ) -> Result<()> {
518 let mut forks = self.forks.write().await;
520
521 let excess = prefix.len();
526 let prefix_last_index = excess - 1;
527 let prefix_last = prefix.last().unwrap();
528 let mut keep = vec![true; forks.len()];
529 let confirmed_txs_hashes: Vec<TransactionHash> =
530 confirmed_txs.iter().map(|tx| tx.hash()).collect();
531 for (index, fork) in forks.iter_mut().enumerate() {
532 if &index == confirmed_fork_index {
533 fork.mempool.retain(|tx| !confirmed_txs_hashes.contains(tx));
535 continue
536 }
537
538 if fork.proposals.is_empty() ||
539 prefix_last_index >= fork.proposals.len() ||
540 &fork.proposals[prefix_last_index] != prefix_last
541 {
542 keep[index] = false;
543 continue
544 }
545
546 fork.mempool.retain(|tx| !confirmed_txs_hashes.contains(tx));
548
549 let rest_proposals = fork.proposals.split_off(excess);
551 let rest_diffs = fork.diffs.split_off(excess);
552 let mut diffs = fork.diffs.clone();
553 fork.proposals = rest_proposals;
554 fork.diffs = rest_diffs;
555 for diff in diffs.iter_mut() {
556 fork.overlay.lock().unwrap().overlay.lock().unwrap().remove_diff(diff);
557 }
558 }
559
560 let mut iter = keep.iter();
562 forks.retain(|_| *iter.next().unwrap());
563
564 self.blockchain.remove_pending_txs_hashes(&confirmed_txs_hashes)?;
566
567 drop(forks);
569
570 Ok(())
571 }
572
573 pub async fn purge_forks(&self) -> Result<()> {
575 debug!(target: "validator::consensus::purge_forks", "Purging current forks...");
576 let mut forks = self.forks.write().await;
577 *forks = vec![Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?];
578 drop(forks);
579 debug!(target: "validator::consensus::purge_forks", "Forks purged!");
580 Ok(())
581 }
582
583 pub async fn reset_pow_module(&self) -> Result<()> {
585 debug!(target: "validator::consensus::reset_pow_module", "Resetting PoW module...");
586
587 let mut module = self.module.write().await;
588 *module = PoWModule::new(
589 self.blockchain.clone(),
590 module.target,
591 module.fixed_difficulty.clone(),
592 None,
593 )?;
594 drop(module);
595 debug!(target: "validator::consensus::reset_pow_module", "PoW module reset successfully!");
596 Ok(())
597 }
598
599 pub async fn healthcheck(&self) -> Result<()> {
602 let lock = self.forks.read().await;
604
605 let state_root = self.blockchain.contracts.get_state_monotree_root()?;
607
608 let last_block_state_root = self.blockchain.last_header()?.state_root;
610 if state_root != last_block_state_root {
611 return Err(Error::ContractsStatesRootError(
612 blake3::Hash::from_bytes(state_root).to_string(),
613 blake3::Hash::from_bytes(last_block_state_root).to_string(),
614 ));
615 }
616
617 for fork in lock.iter() {
619 fork.healthcheck()?;
620 }
621
622 Ok(())
623 }
624
625 pub async fn purge_unreferenced_trees(
628 &self,
629 referenced_trees: &mut BTreeSet<IVec>,
630 ) -> Result<()> {
631 let lock = self.forks.read().await;
633
634 if lock.is_empty() {
636 let fork = Fork::new(self.blockchain.clone(), self.module.read().await.clone()).await?;
639 fork.referenced_trees(referenced_trees);
640 } else {
641 for fork in lock.iter() {
643 fork.referenced_trees(referenced_trees);
644 }
645 }
646
647 let current_trees = self.blockchain.sled_db.tree_names();
649
650 for tree in current_trees {
653 if referenced_trees.contains(&tree) {
655 continue
656 }
657
658 let Ok(tree) = deserialize::<[u8; 32]>(&tree) else { continue };
660
661 debug!(target: "validator::consensus::purge_unreferenced_trees", "Dropping unreferenced tree: {}", blake3::Hash::from(tree));
663 self.blockchain.sled_db.drop_tree(tree)?;
664 }
665
666 Ok(())
667 }
668
669 pub async fn purge_unproposed_pending_txs(
672 &self,
673 mut proposed_txs: HashSet<TransactionHash>,
674 ) -> Result<()> {
675 let mut forks = self.forks.write().await;
677
678 for fork in forks.iter() {
680 let proposals_txs =
682 fork.overlay.lock().unwrap().get_blocks_txs_hashes(&fork.proposals)?;
683 for tx in proposals_txs {
684 proposed_txs.insert(tx);
685 }
686 }
687
688 for fork in forks.iter_mut() {
691 fork.mempool.retain(|tx| proposed_txs.contains(tx));
692 }
693
694 let proposed_txs: Vec<TransactionHash> = proposed_txs.into_iter().collect();
696 self.blockchain.reset_pending_txs(&proposed_txs)?;
697
698 Ok(())
699 }
700}
701
702#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
704pub struct Proposal {
705 pub hash: HeaderHash,
707 pub block: BlockInfo,
709}
710
711impl Proposal {
712 pub fn new(block: BlockInfo) -> Self {
713 let hash = block.hash();
714 Self { hash, block }
715 }
716}
717
718impl From<Proposal> for BlockInfo {
719 fn from(proposal: Proposal) -> BlockInfo {
720 proposal.block
721 }
722}
723
724#[derive(Clone)]
731pub struct Fork {
732 pub blockchain: Blockchain,
734 pub overlay: BlockchainOverlayPtr,
736 pub module: PoWModule,
738 pub proposals: Vec<HeaderHash>,
740 pub diffs: Vec<SledDbOverlayStateDiff>,
742 pub mempool: Vec<TransactionHash>,
744 pub targets_rank: BigUint,
746 pub hashes_rank: BigUint,
748}
749
750impl Fork {
751 pub async fn new(blockchain: Blockchain, module: PoWModule) -> Result<Self> {
752 let mempool = blockchain.get_pending_txs()?.iter().map(|tx| tx.hash()).collect();
753 let overlay = BlockchainOverlay::new(&blockchain)?;
754 let last_difficulty = blockchain.last_block_difficulty()?;
756 let targets_rank = last_difficulty.ranks.targets_rank;
757 let hashes_rank = last_difficulty.ranks.hashes_rank;
758 Ok(Self {
759 blockchain,
760 overlay,
761 module,
762 proposals: vec![],
763 diffs: vec![],
764 mempool,
765 targets_rank,
766 hashes_rank,
767 })
768 }
769
770 pub async fn append_proposal(&mut self, proposal: &Proposal) -> Result<()> {
772 let (next_target, next_difficulty) = self.module.next_mine_target_and_difficulty()?;
774
775 let (target_distance_sq, hash_distance_sq) = block_rank(&proposal.block, &next_target)?;
777
778 self.targets_rank += target_distance_sq.clone();
780 self.hashes_rank += hash_distance_sq.clone();
781
782 let cumulative_difficulty =
784 self.module.cumulative_difficulty.clone() + next_difficulty.clone();
785 let ranks = BlockRanks::new(
786 target_distance_sq,
787 self.targets_rank.clone(),
788 hash_distance_sq,
789 self.hashes_rank.clone(),
790 );
791 let block_difficulty = BlockDifficulty::new(
792 proposal.block.header.height,
793 proposal.block.header.timestamp,
794 next_difficulty,
795 cumulative_difficulty,
796 ranks,
797 );
798 self.module.append_difficulty(&self.overlay, &proposal.block.header, block_difficulty)?;
799
800 self.proposals.push(proposal.hash);
802
803 self.diffs.push(self.overlay.lock().unwrap().overlay.lock().unwrap().diff(&self.diffs)?);
805
806 Ok(())
807 }
808
809 pub fn last_proposal(&self) -> Result<Proposal> {
811 let block = if let Some(last) = self.proposals.last() {
812 self.overlay.lock().unwrap().get_blocks_by_hash(&[*last])?[0].clone()
813 } else {
814 self.overlay.lock().unwrap().last_block()?
815 };
816
817 Ok(Proposal::new(block))
818 }
819
820 pub fn get_next_block_height(&self) -> Result<u32> {
822 let proposal = self.last_proposal()?;
823 Ok(proposal.block.header.height + 1)
824 }
825
826 pub async fn unproposed_txs(
832 &mut self,
833 verifying_block_height: u32,
834 verify_fees: bool,
835 ) -> Result<(Vec<Transaction>, u64, u64)> {
836 if self.mempool.is_empty() {
838 return Ok((vec![], 0, 0))
839 }
840
841 let mut tree = MerkleTree::new(1);
843
844 let mut total_gas_used = 0;
846 let mut total_gas_paid = 0;
847
848 let mut vks: HashMap<[u8; 32], HashMap<String, VerifyingKey>> = HashMap::new();
850
851 let proposals_txs = self.overlay.lock().unwrap().get_blocks_txs_hashes(&self.proposals)?;
853
854 let mut unproposed_txs = vec![];
856 let mut erroneous_txs = vec![];
857 for tx in &self.mempool {
858 if proposals_txs.contains(tx) {
860 continue
861 }
862
863 let unproposed_tx = match self.blockchain.transactions.get_pending(&[*tx], true) {
865 Ok(txs) => txs[0].clone().unwrap(),
866 Err(e) => {
867 debug!(target: "validator::consensus::unproposed_txs", "Transaction retrieval failed: {e}");
868 erroneous_txs.push(*tx);
869 continue
870 }
871 };
872
873 for call in &unproposed_tx.calls {
875 vks.entry(call.data.contract_id.to_bytes()).or_default();
876 }
877
878 self.overlay.lock().unwrap().checkpoint();
880 let gas_data = match verify_transaction(
881 &self.overlay,
882 verifying_block_height,
883 self.module.target,
884 &unproposed_tx,
885 &mut tree,
886 &mut vks,
887 verify_fees,
888 )
889 .await
890 {
891 Ok(gas_values) => gas_values,
892 Err(e) => {
893 debug!(target: "validator::consensus::unproposed_txs", "Transaction verification failed: {e}");
894 self.overlay.lock().unwrap().revert_to_checkpoint();
895 erroneous_txs.push(*tx);
896 continue
897 }
898 };
899
900 let tx_gas_used = gas_data.total_gas_used();
902
903 let accumulated_gas_usage = total_gas_used + tx_gas_used;
905
906 if accumulated_gas_usage > BLOCK_GAS_LIMIT {
908 warn!(
909 target: "validator::consensus::unproposed_txs",
910 "Retrieving transaction {tx} would exceed configured unproposed transaction gas limit: {accumulated_gas_usage} - {BLOCK_GAS_LIMIT}"
911 );
912 self.overlay.lock().unwrap().revert_to_checkpoint();
913 break
914 }
915
916 total_gas_used += tx_gas_used;
918 total_gas_paid += gas_data.paid;
919
920 unproposed_txs.push(unproposed_tx);
922 }
923
924 self.mempool.retain(|tx| !erroneous_txs.contains(tx));
926
927 Ok((unproposed_txs, total_gas_used, total_gas_paid))
928 }
929
930 pub fn full_clone(&self) -> Result<Self> {
935 let blockchain = self.blockchain.clone();
936 let overlay = self.overlay.lock().unwrap().full_clone()?;
937 let module = self.module.clone();
938 let proposals = self.proposals.clone();
939 let diffs = self.diffs.clone();
940 let mempool = self.mempool.clone();
941 let targets_rank = self.targets_rank.clone();
942 let hashes_rank = self.hashes_rank.clone();
943
944 Ok(Self {
945 blockchain,
946 overlay,
947 module,
948 proposals,
949 diffs,
950 mempool,
951 targets_rank,
952 hashes_rank,
953 })
954 }
955
956 pub fn healthcheck(&self) -> Result<()> {
963 let state_root = self.overlay.lock().unwrap().contracts.get_state_monotree_root()?;
965
966 let last_block_state_root = self.last_proposal()?.block.header.state_root;
968 if state_root != last_block_state_root {
969 return Err(Error::ContractsStatesRootError(
970 blake3::Hash::from_bytes(state_root).to_string(),
971 blake3::Hash::from_bytes(last_block_state_root).to_string(),
972 ));
973 }
974
975 Ok(())
976 }
977
978 pub fn referenced_trees(&self, trees: &mut BTreeSet<IVec>) {
981 let fork_overlay = self.overlay.lock().unwrap();
983 let overlay = fork_overlay.overlay.lock().unwrap();
984
985 for initial_tree in &overlay.state.initial_tree_names {
987 trees.insert(initial_tree.clone());
988 }
989
990 for new_tree in &overlay.state.new_tree_names {
992 trees.insert(new_tree.clone());
993 }
994
995 for dropped_tree in overlay.state.dropped_trees.keys() {
997 trees.insert(dropped_tree.clone());
998 }
999
1000 for protected_tree in &overlay.state.protected_tree_names {
1002 trees.insert(protected_tree.clone());
1003 }
1004 }
1005}