Skip to main content

mina_node/transition_frontier/
transition_frontier_reducer.rs

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