node/transition_frontier/
transition_frontier_actions.rs

1use std::{collections::BTreeSet, sync::Arc};
2
3use mina_p2p_messages::v2::StateHash;
4use openmina_core::{block::ArcBlockWithHash, ActionEvent};
5use serde::{Deserialize, Serialize};
6
7use super::{
8    candidate::TransitionFrontierCandidateAction,
9    genesis::TransitionFrontierGenesisAction,
10    genesis_effectful::TransitionFrontierGenesisEffectfulAction,
11    sync::{SyncError, TransitionFrontierSyncAction, TransitionFrontierSyncState},
12};
13
14pub type TransitionFrontierActionWithMeta = redux::ActionWithMeta<TransitionFrontierAction>;
15pub type TransitionFrontierActionWithMetaRef<'a> =
16    redux::ActionWithMeta<&'a TransitionFrontierAction>;
17
18#[derive(derive_more::From, Serialize, Deserialize, Debug, Clone, ActionEvent)]
19pub enum TransitionFrontierAction {
20    Genesis(TransitionFrontierGenesisAction),
21    GenesisEffect(TransitionFrontierGenesisEffectfulAction),
22    /// Inject genesis block into the transition frontier.
23    ///
24    /// Unless we already have a better block there.
25    ///
26    /// If this node is block producer, we produce proof for the genesis
27    /// block, otherwise we don't need it so we use dummy proof instead.
28    #[action_event(level = info)]
29    GenesisInject,
30    #[action_event(level = info)]
31    GenesisProvenInject,
32
33    Candidate(TransitionFrontierCandidateAction),
34    Sync(TransitionFrontierSyncAction),
35    /// Transition frontier synced.
36    Synced {
37        /// Required protocol states for root block.
38        needed_protocol_states: BTreeSet<StateHash>,
39    },
40    SyncFailed {
41        best_tip: ArcBlockWithHash,
42        error: SyncError,
43    },
44}
45
46impl redux::EnablingCondition<crate::State> for TransitionFrontierAction {
47    fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool {
48        match self {
49            TransitionFrontierAction::Genesis(a) => a.is_enabled(state, time),
50            TransitionFrontierAction::GenesisEffect(a) => a.is_enabled(state, time),
51            TransitionFrontierAction::GenesisInject => {
52                state.transition_frontier.root().is_none()
53                    && state
54                        .transition_frontier
55                        .genesis
56                        .block_with_real_or_dummy_proof()
57                        .is_some()
58            }
59            TransitionFrontierAction::GenesisProvenInject => {
60                let Some(genesis) = state.transition_frontier.genesis.proven_block() else {
61                    return false;
62                };
63                state
64                    .transition_frontier
65                    .root()
66                    .is_none_or(|b| b.is_genesis() && !Arc::ptr_eq(&genesis.block, &b.block))
67            }
68            TransitionFrontierAction::Candidate(a) => a.is_enabled(state, time),
69            TransitionFrontierAction::Sync(a) => a.is_enabled(state, time),
70            TransitionFrontierAction::Synced { .. } => matches!(
71                state.transition_frontier.sync,
72                TransitionFrontierSyncState::CommitSuccess { .. }
73            ),
74            TransitionFrontierAction::SyncFailed { best_tip, error } => {
75                let sync = &state.transition_frontier.sync;
76                sync.best_tip().is_some_and(|b| b.hash() == best_tip.hash())
77                    && match error {
78                        SyncError::BlockApplyFailed(block, _) => sync
79                            .block_state(block.hash())
80                            .is_some_and(|s| s.is_apply_error()),
81                    }
82            }
83        }
84    }
85}