p2p/connection/incoming/
p2p_connection_incoming_actions.rs

1use super::{
2    P2pConnectionIncomingError, P2pConnectionIncomingInitOpts, P2pConnectionIncomingState,
3};
4use crate::{
5    connection::{P2pConnectionAction, P2pConnectionState},
6    webrtc, P2pAction, P2pPeerStatus, P2pState, PeerId,
7};
8use openmina_core::{requests::RpcId, ActionEvent};
9use serde::{Deserialize, Serialize};
10use std::net::SocketAddr;
11
12#[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)]
13#[action_event(fields(debug(opts), display(peer_id), display(error)))]
14pub enum P2pConnectionIncomingAction {
15    /// Incoming connection is initialized.
16    Init {
17        opts: P2pConnectionIncomingInitOpts,
18        rpc_id: Option<RpcId>,
19    },
20    #[action_event(level = trace)]
21    AnswerSdpCreatePending {
22        peer_id: PeerId,
23    },
24    AnswerSdpCreateError {
25        peer_id: PeerId,
26        error: String,
27    },
28    AnswerSdpCreateSuccess {
29        peer_id: PeerId,
30        sdp: String,
31    },
32    AnswerReady {
33        peer_id: PeerId,
34        answer: Box<webrtc::Answer>,
35    },
36    AnswerSendSuccess {
37        peer_id: PeerId,
38    },
39    /// Pending incoming connection finalization.
40    #[action_event(level = trace)]
41    FinalizePending {
42        peer_id: PeerId,
43    },
44    /// Error finalizing incoming connection.
45    FinalizeError {
46        peer_id: PeerId,
47        error: String,
48    },
49    /// Incoming connection finalized.
50    FinalizeSuccess {
51        peer_id: PeerId,
52        remote_auth: webrtc::ConnectionAuthEncrypted,
53    },
54    /// Timeout establishing incoming connection.
55    Timeout {
56        peer_id: PeerId,
57    },
58    /// Error establishing incoming connection.
59    #[action_event(level = warn, fields(display(peer_id), display(error)))]
60    Error {
61        peer_id: PeerId,
62        error: P2pConnectionIncomingError,
63    },
64    /// Incoming connection is successful.
65    #[action_event(level = info)]
66    Success {
67        peer_id: PeerId,
68    },
69    /// Detected incoming connection from this peer.
70    FinalizePendingLibp2p {
71        peer_id: PeerId,
72        addr: SocketAddr,
73    },
74    /// Incoming libp2p connection is successful.
75    Libp2pReceived {
76        peer_id: PeerId,
77    },
78}
79
80impl P2pConnectionIncomingAction {
81    pub fn peer_id(&self) -> &PeerId {
82        match self {
83            Self::Init { opts, .. } => &opts.peer_id,
84            Self::AnswerSdpCreatePending { peer_id }
85            | Self::AnswerSdpCreateError { peer_id, .. }
86            | Self::AnswerSdpCreateSuccess { peer_id, .. }
87            | Self::AnswerReady { peer_id, .. }
88            | Self::AnswerSendSuccess { peer_id }
89            | Self::FinalizePending { peer_id }
90            | Self::FinalizeError { peer_id, .. }
91            | Self::FinalizeSuccess { peer_id, .. }
92            | Self::Timeout { peer_id }
93            | Self::Error { peer_id, .. }
94            | Self::Success { peer_id }
95            | Self::FinalizePendingLibp2p { peer_id, .. }
96            | Self::Libp2pReceived { peer_id } => peer_id,
97        }
98    }
99}
100
101impl redux::EnablingCondition<P2pState> for P2pConnectionIncomingAction {
102    fn is_enabled(&self, state: &P2pState, time: redux::Timestamp) -> bool {
103        match self {
104            P2pConnectionIncomingAction::Init { opts, .. } => {
105                state.incoming_accept(opts.peer_id, &opts.offer).is_ok()
106            }
107            P2pConnectionIncomingAction::AnswerSdpCreatePending { peer_id } => {
108                state.peers.get(peer_id).is_some_and(|peer| {
109                    matches!(
110                        &peer.status,
111                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
112                            P2pConnectionIncomingState::Init { .. },
113                        ))
114                    )
115                })
116            }
117            P2pConnectionIncomingAction::AnswerSdpCreateError { peer_id, .. } => {
118                state.peers.get(peer_id).is_some_and(|peer| {
119                    matches!(
120                        &peer.status,
121                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
122                            P2pConnectionIncomingState::AnswerSdpCreatePending { .. },
123                        ))
124                    )
125                })
126            }
127            P2pConnectionIncomingAction::AnswerSdpCreateSuccess { peer_id, .. } => {
128                state.peers.get(peer_id).is_some_and(|peer| {
129                    matches!(
130                        &peer.status,
131                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
132                            P2pConnectionIncomingState::AnswerSdpCreatePending { .. },
133                        ))
134                    )
135                })
136            }
137            P2pConnectionIncomingAction::AnswerReady { peer_id, .. } => {
138                state.peers.get(peer_id).is_some_and(|peer| {
139                    matches!(
140                        &peer.status,
141                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
142                            P2pConnectionIncomingState::AnswerSdpCreateSuccess { .. },
143                        ))
144                    )
145                })
146            }
147            P2pConnectionIncomingAction::AnswerSendSuccess { peer_id } => {
148                state.peers.get(peer_id).is_some_and(|peer| {
149                    matches!(
150                        &peer.status,
151                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
152                            P2pConnectionIncomingState::AnswerReady { .. },
153                        ))
154                    )
155                })
156            }
157            P2pConnectionIncomingAction::FinalizePending { peer_id } => {
158                state.peers.get(peer_id).is_some_and(|peer| {
159                    matches!(
160                        &peer.status,
161                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
162                            P2pConnectionIncomingState::AnswerSendSuccess { .. },
163                        ))
164                    )
165                })
166            }
167            P2pConnectionIncomingAction::FinalizeError { peer_id, .. } => {
168                state.peers.get(peer_id).is_some_and(|peer| {
169                    matches!(
170                        &peer.status,
171                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
172                            P2pConnectionIncomingState::FinalizePending { .. },
173                        ))
174                    )
175                })
176            }
177            P2pConnectionIncomingAction::FinalizeSuccess { peer_id, .. } => {
178                state.peers.get(peer_id).is_some_and(|peer| {
179                    matches!(
180                        &peer.status,
181                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
182                            P2pConnectionIncomingState::FinalizePending { .. },
183                        ))
184                    )
185                })
186            }
187            P2pConnectionIncomingAction::Timeout { peer_id } => state
188                .peers
189                .get(peer_id)
190                .and_then(|peer| peer.status.as_connecting()?.as_incoming())
191                .is_some_and(|s| s.is_timed_out(time, &state.config.timeouts)),
192            P2pConnectionIncomingAction::Error { peer_id, error } => state
193                .peers
194                .get(peer_id)
195                .is_some_and(|peer| match &peer.status {
196                    P2pPeerStatus::Connecting(P2pConnectionState::Incoming(s)) => match error {
197                        P2pConnectionIncomingError::SdpCreateError(_) => {
198                            matches!(s, P2pConnectionIncomingState::AnswerSdpCreatePending { .. })
199                        }
200                        P2pConnectionIncomingError::FinalizeError(_) => {
201                            matches!(s, P2pConnectionIncomingState::FinalizePending { .. })
202                        }
203                        P2pConnectionIncomingError::ConnectionAuthError => {
204                            matches!(s, P2pConnectionIncomingState::FinalizeSuccess { .. })
205                        }
206                        P2pConnectionIncomingError::Timeout => true,
207                    },
208                    _ => false,
209                }),
210            P2pConnectionIncomingAction::Success { peer_id } => {
211                state.peers.get(peer_id).is_some_and(|peer| {
212                    matches!(
213                        &peer.status,
214                        P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
215                            P2pConnectionIncomingState::FinalizeSuccess { .. },
216                        ))
217                    )
218                })
219            }
220            P2pConnectionIncomingAction::FinalizePendingLibp2p { .. } => {
221                cfg!(feature = "p2p-libp2p")
222            }
223            P2pConnectionIncomingAction::Libp2pReceived { peer_id, .. } => {
224                cfg!(feature = "p2p-libp2p")
225                    && state.peers.get(peer_id).is_some_and(|peer| {
226                        matches!(
227                            &peer.status,
228                            P2pPeerStatus::Connecting(P2pConnectionState::Incoming(
229                                P2pConnectionIncomingState::FinalizePendingLibp2p { .. },
230                            ))
231                        )
232                    })
233            }
234        }
235    }
236}
237
238impl From<P2pConnectionIncomingAction> for P2pAction {
239    fn from(a: P2pConnectionIncomingAction) -> Self {
240        Self::Connection(P2pConnectionAction::Incoming(a))
241    }
242}