1use std::collections::{BTreeMap, BTreeSet};
2
3use ledger::{
4 scan_state::transaction_logic::valid,
5 transaction_pool::{
6 diff::{self, BestTipDiff, DiffVerified},
7 ValidCommandWithHash,
8 },
9 Account, AccountId,
10};
11use mina_p2p_messages::{
12 list::List,
13 v2::{self},
14};
15use openmina_core::{
16 transaction::{TransactionPoolMessageSource, TransactionWithHash},
17 ActionEvent,
18};
19use redux::Callback;
20use serde::{Deserialize, Serialize};
21
22use super::{candidate::TransactionPoolCandidateAction, PendingId};
23
24pub type TransactionPoolActionWithMeta = redux::ActionWithMeta<TransactionPoolAction>;
25pub type TransactionPoolActionWithMetaRef<'a> = redux::ActionWithMeta<&'a TransactionPoolAction>;
26
27#[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)]
28#[action_event(level = info)]
29pub enum TransactionPoolAction {
30 Candidate(TransactionPoolCandidateAction),
31 StartVerify {
32 commands: List<TransactionWithHash>,
33 from_source: TransactionPoolMessageSource,
34 },
35 StartVerifyWithAccounts {
36 accounts: BTreeMap<AccountId, Account>,
37 pending_id: PendingId,
38 from_source: TransactionPoolMessageSource,
39 },
40 VerifySuccess {
41 valids: Vec<valid::UserCommand>,
42 from_source: TransactionPoolMessageSource,
43 },
44 #[action_event(level = warn, fields(debug(errors)))]
45 VerifyError {
46 errors: Vec<String>,
47 },
48 BestTipChanged {
49 best_tip_hash: v2::LedgerHash,
50 },
51 BestTipChangedWithAccounts {
52 accounts: BTreeMap<AccountId, Account>,
53 },
54 ApplyVerifiedDiff {
55 best_tip_hash: v2::LedgerHash,
56 diff: DiffVerified,
57 from_source: TransactionPoolMessageSource,
58 },
59 ApplyVerifiedDiffWithAccounts {
60 accounts: BTreeMap<AccountId, Account>,
61 pending_id: PendingId,
62 },
63 ApplyTransitionFrontierDiff {
64 best_tip_hash: v2::LedgerHash,
65 diff: BestTipDiff,
66 },
67 ApplyTransitionFrontierDiffWithAccounts {
68 accounts: BTreeMap<AccountId, Account>,
69 pending_id: PendingId,
70 },
71 Rebroadcast {
74 accepted: Vec<ValidCommandWithHash>,
75 rejected: Vec<(ValidCommandWithHash, diff::Error)>,
76 is_local: bool,
77 },
78 CollectTransactionsByFee,
79 #[action_event(level = trace)]
80 P2pSendAll,
81 #[action_event(level = debug)]
82 P2pSend {
83 peer_id: p2p::PeerId,
84 },
85}
86
87impl redux::EnablingCondition<crate::State> for TransactionPoolAction {
88 fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool {
89 match self {
90 TransactionPoolAction::Candidate(a) => a.is_enabled(state, time),
91 TransactionPoolAction::StartVerify { commands, .. } => {
92 !commands.is_empty()
93 && commands
94 .iter()
95 .any(|cmd| !state.transaction_pool.contains(cmd.hash()))
96 }
97 TransactionPoolAction::P2pSendAll => true,
98 TransactionPoolAction::P2pSend { peer_id } => state
99 .p2p
100 .get_ready_peer(peer_id)
101 .filter(|_| !state.transaction_pool.dpool.is_empty())
103 .and_then(|p| {
106 let peer_best_tip = p.best_tip.as_ref()?;
107 let our_best_tip = state.transition_frontier.best_tip()?.hash();
108 Some(p).filter(|_| {
109 peer_best_tip.hash() == our_best_tip
110 || peer_best_tip.pred_hash() == our_best_tip
111 })
112 })
113 .is_some_and(|p| {
114 let check =
115 |(next_index, limit), last_index| limit > 0 && next_index <= last_index;
116 let last_index = state.transaction_pool.dpool.last_index();
117
118 check(
119 p.channels.transaction.next_send_index_and_limit(),
120 last_index,
121 )
122 }),
123 TransactionPoolAction::Rebroadcast {
124 accepted, rejected, ..
125 } => !(accepted.is_empty() && rejected.is_empty()),
126 _ => true,
127 }
128 }
129}
130
131type TransactionPoolEffectfulActionCallback = Callback<(
132 BTreeMap<AccountId, Account>,
133 Option<PendingId>,
134 TransactionPoolMessageSource,
135)>;
136
137#[derive(Serialize, Deserialize, Debug, Clone)]
138pub enum TransactionPoolEffectfulAction {
139 FetchAccounts {
140 account_ids: BTreeSet<AccountId>,
141 ledger_hash: v2::LedgerHash,
142 on_result: TransactionPoolEffectfulActionCallback,
143 pending_id: Option<PendingId>,
144 from_source: TransactionPoolMessageSource,
145 },
146}
147
148impl redux::EnablingCondition<crate::State> for TransactionPoolEffectfulAction {}