p2p/channels/transaction/
p2p_channels_transaction_actions.rs1use openmina_core::{p2p::P2pNetworkPubsubMessageCacheId, transaction::Transaction, ActionEvent};
2use serde::{Deserialize, Serialize};
3
4use crate::{channels::P2pChannelsAction, P2pState, PeerId};
5
6use super::{P2pChannelsTransactionState, TransactionInfo, TransactionPropagationState};
7
8pub type P2pChannelsTransactionActionWithMetaRef<'a> =
9 redux::ActionWithMeta<&'a P2pChannelsTransactionAction>;
10
11#[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)]
12#[action_event(fields(display(peer_id)))]
13pub enum P2pChannelsTransactionAction {
14 Init {
15 peer_id: PeerId,
16 },
17 Pending {
18 peer_id: PeerId,
19 },
20 Ready {
21 peer_id: PeerId,
22 },
23 RequestSend {
24 peer_id: PeerId,
25 limit: u8,
26 },
27 PromiseReceived {
28 peer_id: PeerId,
29 promised_count: u8,
30 },
31 Received {
33 peer_id: PeerId,
34 transaction: Box<TransactionInfo>,
35 },
36 RequestReceived {
37 peer_id: PeerId,
38 limit: u8,
39 },
40 ResponseSend {
41 peer_id: PeerId,
42 transactions: Vec<TransactionInfo>,
43 first_index: u64,
44 last_index: u64,
45 },
46 Libp2pReceived {
47 peer_id: PeerId,
48 transactions: Vec<Transaction>,
49 nonce: u32,
50 message_id: P2pNetworkPubsubMessageCacheId,
51 },
52 Libp2pBroadcast {
53 transaction: Box<Transaction>,
54 nonce: u32,
55 is_local: bool,
56 },
57}
58
59impl P2pChannelsTransactionAction {
60 pub fn peer_id(&self) -> Option<&PeerId> {
61 match self {
62 Self::Init { peer_id }
63 | Self::Pending { peer_id }
64 | Self::Ready { peer_id }
65 | Self::RequestSend { peer_id, .. }
66 | Self::PromiseReceived { peer_id, .. }
67 | Self::Received { peer_id, .. }
68 | Self::RequestReceived { peer_id, .. }
69 | Self::ResponseSend { peer_id, .. } => Some(peer_id),
70 Self::Libp2pReceived { peer_id, .. } => Some(peer_id),
71 Self::Libp2pBroadcast { .. } => None,
72 }
73 }
74}
75
76impl redux::EnablingCondition<P2pState> for P2pChannelsTransactionAction {
77 fn is_enabled(&self, state: &P2pState, _time: redux::Timestamp) -> bool {
78 match self {
79 P2pChannelsTransactionAction::Init { peer_id } => {
80 state.get_ready_peer(peer_id).is_some_and(|p| {
81 matches!(
82 &p.channels.transaction,
83 P2pChannelsTransactionState::Enabled
84 )
85 })
86 }
87 P2pChannelsTransactionAction::Pending { peer_id } => {
88 state.get_ready_peer(peer_id).is_some_and(|p| {
89 matches!(
90 &p.channels.transaction,
91 P2pChannelsTransactionState::Init { .. }
92 )
93 })
94 }
95 P2pChannelsTransactionAction::Ready { peer_id } => {
96 state.get_ready_peer(peer_id).is_some_and(|p| {
97 matches!(
98 &p.channels.transaction,
99 P2pChannelsTransactionState::Pending { .. }
100 )
101 })
102 }
103 P2pChannelsTransactionAction::RequestSend { peer_id, .. } => {
104 state.get_ready_peer(peer_id).is_some_and(|p| {
105 matches!(
106 &p.channels.transaction,
107 P2pChannelsTransactionState::Ready {
108 local: TransactionPropagationState::WaitingForRequest { .. }
109 | TransactionPropagationState::Responded { .. },
110 ..
111 }
112 )
113 })
114 }
115 P2pChannelsTransactionAction::PromiseReceived {
116 peer_id,
117 promised_count,
118 } => state.get_ready_peer(peer_id).is_some_and(|p| {
119 matches!(
120 &p.channels.transaction,
121 P2pChannelsTransactionState::Ready {
122 local: TransactionPropagationState::Requested {
123 requested_limit, ..
124 }, ..
125 } if *promised_count > 0 && promised_count <= requested_limit
126 )
127 }),
128 P2pChannelsTransactionAction::Received { peer_id, .. } => {
129 state.get_ready_peer(peer_id).is_some_and(|p| {
130 matches!(
131 &p.channels.transaction,
132 P2pChannelsTransactionState::Ready {
133 local: TransactionPropagationState::Responding { .. },
134 ..
135 }
136 )
137 })
138 }
139 P2pChannelsTransactionAction::RequestReceived { peer_id, limit } => {
140 *limit > 0
141 && state.get_ready_peer(peer_id).is_some_and(|p| {
142 matches!(
143 &p.channels.transaction,
144 P2pChannelsTransactionState::Ready {
145 remote: TransactionPropagationState::WaitingForRequest { .. }
146 | TransactionPropagationState::Responded { .. },
147 ..
148 }
149 )
150 })
151 }
152 P2pChannelsTransactionAction::ResponseSend {
153 peer_id,
154 transactions,
155 first_index,
156 last_index,
157 } => {
158 !transactions.is_empty()
159 && first_index <= last_index
160 && state.get_ready_peer(peer_id).is_some_and(|p| {
161 match &p.channels.transaction {
162 P2pChannelsTransactionState::Ready {
163 remote,
164 next_send_index,
165 ..
166 } => {
167 if first_index < next_send_index {
168 return false;
169 }
170 match remote {
171 TransactionPropagationState::Requested {
172 requested_limit,
173 ..
174 } => transactions.len() <= *requested_limit as usize,
175 _ => false,
176 }
177 }
178 _ => false,
179 }
180 })
181 }
182 P2pChannelsTransactionAction::Libp2pReceived { peer_id, .. } => {
183 cfg!(feature = "p2p-libp2p")
184 && state
185 .peers
186 .get(peer_id)
187 .filter(|p| p.is_libp2p())
188 .and_then(|p| p.status.as_ready())
189 .is_some_and(|p| p.channels.transaction.is_ready())
190 }
191 P2pChannelsTransactionAction::Libp2pBroadcast { .. } => {
192 cfg!(feature = "p2p-libp2p")
193 && state
194 .peers
195 .iter()
196 .any(|(_, p)| p.is_libp2p() && p.status.as_ready().is_some())
197 }
198 }
199 }
200}
201
202impl From<P2pChannelsTransactionAction> for crate::P2pAction {
203 fn from(action: P2pChannelsTransactionAction) -> Self {
204 Self::Channels(P2pChannelsAction::Transaction(action))
205 }
206}