node/transition_frontier/
transition_frontier_reducer.rs

1use super::{
2    sync::{SyncError, TransitionFrontierSyncState},
3    TransitionFrontierAction, TransitionFrontierActionWithMetaRef, TransitionFrontierState,
4};
5use openmina_core::block::AppliedBlock;
6
7impl TransitionFrontierState {
8    pub fn reducer(
9        mut state_context: crate::Substate<Self>,
10        action: TransitionFrontierActionWithMetaRef<'_>,
11    ) {
12        let Ok(state) = state_context.get_substate_mut() else {
13            // TODO: log or propagate
14            return;
15        };
16        let (action, meta) = action.split();
17
18        // Drop the diff, it's been processed in the effect
19        state.chain_diff.take();
20
21        match action {
22            TransitionFrontierAction::Genesis(a) => {
23                super::genesis::TransitionFrontierGenesisState::reducer(
24                    openmina_core::Substate::from_compatible_substate(state_context),
25                    meta.with_action(a),
26                )
27            }
28            TransitionFrontierAction::GenesisEffect(_) => {}
29            TransitionFrontierAction::GenesisInject => {
30                let Some(genesis) = state.genesis.block_with_real_or_dummy_proof() else {
31                    return;
32                };
33                let genesis = AppliedBlock {
34                    block: genesis,
35                    just_emitted_a_proof: true,
36                };
37                state.best_chain = vec![genesis];
38                state.sync = TransitionFrontierSyncState::Synced { time: meta.time() };
39            }
40            TransitionFrontierAction::GenesisProvenInject => {
41                let Some(genesis) = state.genesis.proven_block() else {
42                    return;
43                };
44                if let Some(block) = state.best_chain.get_mut(0) {
45                    block.block = genesis.clone();
46                } else {
47                    let genesis = AppliedBlock {
48                        block: genesis.clone(),
49                        just_emitted_a_proof: true,
50                    };
51                    state.best_chain = vec![genesis];
52                }
53                if !state.sync.is_pending() {
54                    state.sync = TransitionFrontierSyncState::Synced { time: meta.time() };
55                }
56            }
57            TransitionFrontierAction::Candidate(a) => {
58                super::candidate::TransitionFrontierCandidatesState::reducer(
59                    openmina_core::Substate::from_compatible_substate(state_context),
60                    meta.with_action(a),
61                );
62            }
63            TransitionFrontierAction::Sync(a) => {
64                let best_chain = state.best_chain.clone();
65                super::sync::TransitionFrontierSyncState::reducer(
66                    openmina_core::Substate::from_compatible_substate(state_context),
67                    meta.with_action(a),
68                    &best_chain,
69                );
70            }
71            TransitionFrontierAction::Synced {
72                needed_protocol_states: needed_protocol_state_hashes,
73            } => {
74                let TransitionFrontierSyncState::CommitSuccess {
75                    chain,
76                    needed_protocol_states,
77                    ..
78                } = &mut state.sync
79                else {
80                    return;
81                };
82                let mut needed_protocol_state_hashes = needed_protocol_state_hashes.clone();
83                let new_chain = std::mem::take(chain);
84                let needed_protocol_states = std::mem::take(needed_protocol_states);
85
86                state.needed_protocol_states.extend(needed_protocol_states);
87                state
88                    .needed_protocol_states
89                    .retain(|k, _| needed_protocol_state_hashes.remove(k));
90
91                for hash in needed_protocol_state_hashes {
92                    let block = state
93                        .best_chain
94                        .iter()
95                        .find(|b| b.hash() == &hash)
96                        .or_else(|| new_chain.iter().find(|b| b.hash() == &hash));
97                    // TODO(binier): error log instead.
98                    let block = block.expect("we lack needed block!");
99                    let protocol_state = block.header().protocol_state.clone();
100                    state.needed_protocol_states.insert(hash, protocol_state);
101                }
102
103                state.blacklist.retain(|_, height| {
104                    // prune blocks from black list that can't end up
105                    // into transition frontier anymore due to consensus
106                    // reasons.
107                    let tip = new_chain.last().unwrap();
108                    height
109                        .checked_add(tip.constants().k.as_u32())
110                        .expect("overflow")
111                        > tip.height()
112                });
113                state.chain_diff = state.maybe_make_chain_diff(&new_chain);
114                state.best_chain = new_chain;
115                state.sync = TransitionFrontierSyncState::Synced { time: meta.time() };
116            }
117            TransitionFrontierAction::SyncFailed { error, .. } => {
118                match error {
119                    SyncError::BlockApplyFailed(block, _) => {
120                        state.blacklist.insert(block.hash().clone(), block.height());
121                    }
122                }
123                state.sync = TransitionFrontierSyncState::Synced { time: meta.time() };
124            }
125        }
126    }
127}