openmina_node_invariants/transition_frontier/
only_syncs_to_better_blocks.rs1use node::{
2 core::{block::ArcBlockWithHash, consensus::consensus_take},
3 ActionKind, ActionWithMeta, Store,
4};
5
6use crate::{Invariant, InvariantResult};
7
8#[derive(documented::Documented, Default, Clone, Copy)]
11pub struct TransitionFrontierOnlySyncsToBetterBlocks;
12
13impl Invariant for TransitionFrontierOnlySyncsToBetterBlocks {
14 type InternalState = (Option<ArcBlockWithHash>, Option<ArcBlockWithHash>);
15 fn triggers(&self) -> &[ActionKind] {
16 &[
17 ActionKind::TransitionFrontierSyncInit,
18 ActionKind::TransitionFrontierSyncBestTipUpdate,
19 ActionKind::TransitionFrontierSynced,
20 ]
21 }
22
23 fn check<S: redux::Service>(
24 self,
25 (prev_best_tip, prev_target_best_tip): &mut Self::InternalState,
26 store: &Store<S>,
27 _action: &ActionWithMeta,
28 ) -> InvariantResult {
29 let transition_frontier = &store.state().transition_frontier;
30 let best_tip = transition_frontier.best_tip();
31 let target_best_tip = transition_frontier.sync.best_tip();
32 let mut checked = false;
33
34 match (best_tip, prev_best_tip.as_ref()) {
36 (Some(best_tip), None) => {
37 *prev_best_tip = Some(best_tip.clone());
38 }
39 (Some(best_tip), Some(prev_tip)) if best_tip.hash() != prev_tip.hash() => {
40 checked = true;
41 if !consensus_take(
42 prev_tip.consensus_state(),
43 best_tip.consensus_state(),
44 prev_tip.hash(),
45 best_tip.hash(),
46 ) {
47 return InvariantResult::Violation(format!(
48 "best tip got downgraded!\nprev({}): {}\nnew({}): {}",
49 prev_tip.hash(),
50 serde_json::to_string(prev_tip.consensus_state()).unwrap(),
51 best_tip.hash(),
52 serde_json::to_string(best_tip.consensus_state()).unwrap(),
53 ));
54 }
55 *prev_best_tip = Some(best_tip.clone());
56 }
57 _ => {}
58 }
59
60 if let (Some(target_best_tip), Some(best_tip)) = (target_best_tip, best_tip) {
62 checked = true;
63 if !best_tip.is_genesis()
64 && !consensus_take(
65 best_tip.consensus_state(),
66 target_best_tip.consensus_state(),
67 best_tip.hash(),
68 target_best_tip.hash(),
69 )
70 {
71 return InvariantResult::Violation(format!(
72 "best tip target not better than current best tip!\nprev({}): {}\nnew({}): {}",
73 best_tip.hash(),
74 serde_json::to_string(best_tip.consensus_state()).unwrap(),
75 target_best_tip.hash(),
76 serde_json::to_string(target_best_tip.consensus_state()).unwrap(),
77 ));
78 }
79 }
80
81 match (target_best_tip, prev_target_best_tip.as_ref()) {
83 (Some(new_target), None) => {
84 *prev_target_best_tip = Some(new_target.clone());
85 }
86 (Some(new_target), Some(prev_target)) if new_target.hash() != prev_target.hash() => {
87 checked = true;
88 if !consensus_take(
89 prev_target.consensus_state(),
90 new_target.consensus_state(),
91 prev_target.hash(),
92 new_target.hash(),
93 ) {
94 return InvariantResult::Violation(format!(
95 "best tip target got downgraded!\nprev({}): {}\nnew({}): {}",
96 prev_target.hash(),
97 serde_json::to_string(prev_target.consensus_state()).unwrap(),
98 new_target.hash(),
99 serde_json::to_string(new_target.consensus_state()).unwrap(),
100 ));
101 }
102 *prev_target_best_tip = Some(new_target.clone());
103 }
104 _ => {}
105 }
106
107 if checked {
108 InvariantResult::Ok
109 } else {
110 InvariantResult::Updated
111 }
112 }
113}