openmina_node_invariants/transition_frontier/
only_syncs_to_better_blocks.rs

1use node::{
2    core::{block::ArcBlockWithHash, consensus::consensus_take},
3    ActionKind, ActionWithMeta, Store,
4};
5
6use crate::{Invariant, InvariantResult};
7
8/// Makes sure transition frontier always only starts syncing and syncs
9/// to a better block than the latest best tip.
10#[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        // make sure new best tip is better than the prev one.
35        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        // make sure new best tip target is better than current best tip.
61        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        // make sure new best tip target is better than the prev one.
82        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}