1use crate::p2p::P2pEffectfulAction;
2use mina_core::bug_condition;
3use serde::{Deserialize, Serialize};
4
5pub type ActionWithMeta = redux::ActionWithMeta<Action>;
6pub type ActionWithMetaRef<'a> = redux::ActionWithMeta<&'a Action>;
7
8pub use crate::{
9 block_producer::BlockProducerAction,
10 block_producer_effectful::BlockProducerEffectfulAction,
11 event_source::EventSourceAction,
12 external_snark_worker::ExternalSnarkWorkerAction,
13 ledger::LedgerAction,
14 p2p::P2pAction,
15 rpc::RpcAction,
16 snark::SnarkAction,
17 snark_pool::{SnarkPoolAction, SnarkPoolEffectfulAction},
18 transaction_pool::TransactionPoolAction,
19 transition_frontier::TransitionFrontierAction,
20 watched_accounts::WatchedAccountsAction,
21};
22use crate::{
23 external_snark_worker_effectful::ExternalSnarkWorkerEffectfulAction,
24 ledger_effectful::LedgerEffectfulAction, p2p::callbacks::P2pCallbacksAction,
25 rpc_effectful::RpcEffectfulAction, transaction_pool::TransactionPoolEffectfulAction,
26};
27
28pub trait ActionKindGet {
29 fn kind(&self) -> crate::ActionKind;
30}
31
32static_assertions::const_assert!(std::mem::size_of::<Action>() <= 512);
35
36#[derive(derive_more::From, Serialize, Deserialize, Debug, Clone)]
37pub enum Action {
38 CheckTimeouts(CheckTimeoutsAction),
39 CheckInvalidPeersAction(CheckInvalidPeersAction),
40 EventSource(EventSourceAction),
41
42 P2p(P2pAction),
43 P2pEffectful(P2pEffectfulAction),
44 P2pCallbacks(P2pCallbacksAction),
45
46 Ledger(LedgerAction),
47 LedgerEffects(LedgerEffectfulAction),
48 Snark(SnarkAction),
49 TransitionFrontier(TransitionFrontierAction),
50 SnarkPool(SnarkPoolAction),
51 SnarkPoolEffect(SnarkPoolEffectfulAction),
52 TransactionPool(TransactionPoolAction),
53 TransactionPoolEffect(TransactionPoolEffectfulAction),
54 ExternalSnarkWorker(ExternalSnarkWorkerAction),
55 ExternalSnarkWorkerEffects(ExternalSnarkWorkerEffectfulAction),
56 BlockProducer(BlockProducerAction),
57 BlockProducerEffectful(BlockProducerEffectfulAction),
58 Rpc(RpcAction),
59 RpcEffectful(RpcEffectfulAction),
60
61 WatchedAccounts(WatchedAccountsAction),
62}
63
64impl Action {
65 pub fn kind(&self) -> crate::ActionKind {
66 ActionKindGet::kind(self)
67 }
68}
69
70#[derive(Serialize, Deserialize, Debug, Clone)]
74pub struct CheckTimeoutsAction {}
75
76impl redux::EnablingCondition<crate::State> for CheckTimeoutsAction {}
77
78#[derive(Serialize, Deserialize, Debug, Clone)]
80pub struct CheckInvalidPeersAction {}
81
82impl redux::EnablingCondition<crate::State> for CheckInvalidPeersAction {
83 fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool {
84 const STARTING_BLOCK_HEIGHT: u32 = 296_372;
86 const GRACE_PERIOD: u64 = 60 * 1_000 * 1_000 * 1000;
87
88 let Some(grace_period) = state.start_time().checked_add(GRACE_PERIOD) else {
89 bug_condition!("Failed to add duration");
90 return false;
91 };
92 if grace_period > time {
93 return false;
94 }
95
96 let Some(p2p_state) = state.p2p.ready() else {
97 return false;
98 };
99
100 let Some(kad_state) = p2p_state.network.scheduler.discovery_state() else {
101 return false;
102 };
103
104 let has_bootstrapped = kad_state.has_bootstrapped && p2p_state.ready_peers().is_empty();
105 if !has_bootstrapped {
106 return false;
107 }
108
109 let Some(tip) = &state.transition_frontier.best_tip_breadcrumb() else {
110 return false;
111 };
112
113 tip.height() == STARTING_BLOCK_HEIGHT
114 }
115}
116
117impl redux::EnablingCondition<crate::State> for Action {
118 fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool {
119 match self {
120 Action::CheckTimeouts(a) => a.is_enabled(state, time),
121 Action::CheckInvalidPeersAction(a) => a.is_enabled(state, time),
122 Action::EventSource(a) => a.is_enabled(state, time),
123 Action::P2p(a) => match a {
124 P2pAction::Initialization(a) => a.is_enabled(state, time),
125 other => state
126 .p2p
127 .ready()
128 .is_some_and(|p2p| other.is_enabled(p2p, time)),
129 },
130 Action::P2pEffectful(a) => state
131 .p2p
132 .ready()
133 .is_some_and(|state| a.is_enabled(state, time)),
134 Action::Ledger(a) => a.is_enabled(state, time),
135 Action::LedgerEffects(a) => a.is_enabled(state, time),
136 Action::Snark(a) => a.is_enabled(&state.snark, time),
137 Action::TransitionFrontier(a) => a.is_enabled(state, time),
138 Action::SnarkPool(a) => a.is_enabled(state, time),
139 Action::SnarkPoolEffect(a) => a.is_enabled(state, time),
140 Action::ExternalSnarkWorker(a) => a.is_enabled(state, time),
141 Action::ExternalSnarkWorkerEffects(a) => a.is_enabled(state, time),
142 Action::BlockProducer(a) => a.is_enabled(state, time),
143 Action::BlockProducerEffectful(a) => a.is_enabled(state, time),
144 Action::Rpc(a) => a.is_enabled(state, time),
145 Action::WatchedAccounts(a) => a.is_enabled(state, time),
146 Action::TransactionPool(a) => a.is_enabled(state, time),
147 Action::TransactionPoolEffect(a) => a.is_enabled(state, time),
148 Action::P2pCallbacks(a) => a.is_enabled(state, time),
149 Action::RpcEffectful(a) => a.is_enabled(state, time),
150 }
151 }
152}
153
154impl From<redux::AnyAction> for Action {
155 fn from(action: redux::AnyAction) -> Self {
156 match action.0.downcast() {
157 Ok(action) => *action,
158 Err(action) => Self::P2p(*action.downcast().expect("Downcast failed")),
159 }
160 }
161}