node/watched_accounts/
watched_accounts_actions.rs1use std::sync::Arc;
2
3use mina_p2p_messages::v2::{
4 MinaBaseAccountBinableArgStableV2, MinaBlockBlockStableV2, NonZeroCurvePoint, StateHash,
5};
6use openmina_core::block::BlockWithHash;
7use serde::{Deserialize, Serialize};
8
9use crate::p2p::PeerId;
10
11use super::{
12 WatchedAccountBlockInfo, WatchedAccountBlockState, WatchedAccountLedgerInitialState,
13 WatchedAccountsLedgerInitialStateGetError,
14};
15
16pub type WatchedAccountsActionWithMeta = redux::ActionWithMeta<WatchedAccountsAction>;
17pub type WatchedAccountsActionWithMetaRef<'a> = redux::ActionWithMeta<&'a WatchedAccountsAction>;
18
19#[derive(Serialize, Deserialize, Debug, Clone)]
20pub enum WatchedAccountsAction {
21 Add {
22 pub_key: NonZeroCurvePoint,
23 },
24 LedgerInitialStateGetInit {
25 pub_key: NonZeroCurvePoint,
26 },
27 LedgerInitialStateGetPending {
28 pub_key: NonZeroCurvePoint,
29 block: WatchedAccountBlockInfo,
30 peer_id: PeerId,
31 },
32 LedgerInitialStateGetError {
33 pub_key: NonZeroCurvePoint,
34 error: WatchedAccountsLedgerInitialStateGetError,
35 },
36 LedgerInitialStateGetRetry {
37 pub_key: NonZeroCurvePoint,
38 },
39 LedgerInitialStateGetSuccess {
40 pub_key: NonZeroCurvePoint,
41 data: Option<Box<MinaBaseAccountBinableArgStableV2>>,
42 },
43 TransactionsIncludedInBlock {
44 pub_key: NonZeroCurvePoint,
45 block: BlockWithHash<Arc<MinaBlockBlockStableV2>>,
46 },
47 BlockLedgerQueryInit {
48 pub_key: NonZeroCurvePoint,
49 block_hash: StateHash,
50 },
51 BlockLedgerQueryPending {
52 pub_key: NonZeroCurvePoint,
53 block_hash: StateHash,
54 peer_id: PeerId,
55 },
56 BlockLedgerQuerySuccess {
57 pub_key: NonZeroCurvePoint,
58 block_hash: StateHash,
59 ledger_account: Box<MinaBaseAccountBinableArgStableV2>,
60 },
61}
62
63fn should_request_ledger_initial_state(state: &crate::State, pub_key: &NonZeroCurvePoint) -> bool {
64 state
65 .watched_accounts
66 .get(pub_key)
67 .filter(|_| {
68 state
69 .transition_frontier
70 .candidates
71 .best_verified()
72 .is_some()
73 })
74 .is_some_and(|a| match &a.initial_state {
75 WatchedAccountLedgerInitialState::Idle { .. } => true,
76 WatchedAccountLedgerInitialState::Error { .. } => true,
77 WatchedAccountLedgerInitialState::Pending { block, .. } => {
78 let Some(best_tip) = state.transition_frontier.candidates.best_verified_block()
79 else {
80 return false;
81 };
82 &block.hash != best_tip.hash()
83 }
84 WatchedAccountLedgerInitialState::Success { .. } => false,
86 })
91}
92
93impl redux::EnablingCondition<crate::State> for WatchedAccountsAction {
94 fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool {
95 match self {
96 WatchedAccountsAction::Add { pub_key } => state.watched_accounts.get(pub_key).is_none(),
97 WatchedAccountsAction::LedgerInitialStateGetInit { pub_key } => {
98 should_request_ledger_initial_state(state, pub_key)
99 }
100 WatchedAccountsAction::LedgerInitialStateGetPending { pub_key, .. } => {
101 should_request_ledger_initial_state(state, pub_key)
102 }
103 WatchedAccountsAction::LedgerInitialStateGetError { pub_key, .. } => {
104 state.watched_accounts.get(pub_key).is_some_and(|a| {
105 matches!(
106 &a.initial_state,
107 WatchedAccountLedgerInitialState::Pending { .. }
108 )
109 })
110 }
111 WatchedAccountsAction::LedgerInitialStateGetRetry { pub_key } => state
112 .watched_accounts
113 .get(pub_key)
114 .is_some_and(|a| match &a.initial_state {
115 WatchedAccountLedgerInitialState::Error { time: t, .. } => {
116 time.checked_sub(*t).is_some_and(|d| d.as_secs() >= 3)
117 }
118 _ => false,
119 }),
120 WatchedAccountsAction::LedgerInitialStateGetSuccess { pub_key, .. } => {
121 state.watched_accounts.get(pub_key).is_some_and(|a| {
122 matches!(
123 &a.initial_state,
124 WatchedAccountLedgerInitialState::Pending { .. }
125 )
126 })
127 }
128 WatchedAccountsAction::TransactionsIncludedInBlock { pub_key, block } => {
129 let diff = &block.body().staged_ledger_diff.diff;
130 state
131 .watched_accounts
132 .get(pub_key)
133 .is_some_and(|v| v.initial_state.is_success())
134 && super::account_relevant_transactions_in_diff_iter(pub_key, diff)
135 .any(|_| true)
136 }
137 WatchedAccountsAction::BlockLedgerQueryInit {
138 pub_key,
139 block_hash,
140 } => {
141 let Some(acc) = state.watched_accounts.get(pub_key) else {
142 return false;
143 };
144 acc.block_find_by_hash(block_hash)
145 .filter(|b| {
146 matches!(b, WatchedAccountBlockState::TransactionsInBlockBody { .. })
147 })
148 .is_some()
149 }
150 WatchedAccountsAction::BlockLedgerQueryPending {
151 pub_key,
152 block_hash,
153 ..
154 } => {
155 let Some(acc) = state.watched_accounts.get(pub_key) else {
156 return false;
157 };
158
159 let should_req_for_block = acc
160 .block_find_by_hash(block_hash)
161 .filter(|b| {
162 matches!(b, WatchedAccountBlockState::TransactionsInBlockBody { .. })
163 })
164 .is_some();
165
166 let p2p_rpc_is_pending = false;
168 should_req_for_block && p2p_rpc_is_pending
176 }
177 WatchedAccountsAction::BlockLedgerQuerySuccess {
178 pub_key,
179 block_hash,
180 ..
181 } => {
182 let Some(acc) = state.watched_accounts.get(pub_key) else {
183 return false;
184 };
185
186 acc.block_find_by_hash(block_hash)
187 .filter(|b| {
188 matches!(b, WatchedAccountBlockState::LedgerAccountGetPending { .. })
189 })
190 .is_some()
191 }
192 }
193 }
194}