node/watched_accounts/
watched_accounts_actions.rs

1use 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            // TODO(binier)
85            WatchedAccountLedgerInitialState::Success { .. } => false,
86            // WatchedAccountLedgerInitialState::Success { block, .. } => !state
87            //     .consensus
88            //     .is_part_of_main_chain(block.level, &block.hash)
89            //     .unwrap_or(true),
90        })
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                // TODO(binier)
167                let p2p_rpc_is_pending = false;
168                // let p2p_rpc_is_pending = None
169                //     .or_else(|| {
170                //         let peer = state.p2p.get_ready_peer(&self.peer_id)?;
171                //         peer.rpc.outgoing.get(self.p2p_rpc_id)
172                //     })
173                //     .is_some_and( |v| v.is_init() || v.is_pending());
174
175                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}