p2p/channels/transaction/
p2p_channels_transaction_actions.rs

1use 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    // TODO(binier): propagate tx info received to pool
32    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}