p2p/connection/outgoing/
p2p_connection_outgoing_actions.rs

1use openmina_core::ActionEvent;
2use redux::Callback;
3use serde::{Deserialize, Serialize};
4
5use openmina_core::requests::RpcId;
6
7use crate::{connection::P2pConnectionErrorResponse, webrtc, P2pState, PeerId};
8
9use super::{P2pConnectionOutgoingError, P2pConnectionOutgoingInitOpts};
10
11#[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)]
12#[action_event(fields(display(opts), display(peer_id), display(error)))]
13pub enum P2pConnectionOutgoingAction {
14    /// Initialize connection to a random peer.
15    #[action_event(level = trace)]
16    RandomInit,
17    /// Initialize connection to a new peer.
18    Init {
19        opts: P2pConnectionOutgoingInitOpts,
20        rpc_id: Option<RpcId>,
21        on_success: Option<Callback<(PeerId, Option<RpcId>)>>,
22    },
23    /// Reconnect to an existing peer.
24    // TODO: rename `Init` and `Reconnect` to `New` and `Connect` or something
25    Reconnect {
26        opts: P2pConnectionOutgoingInitOpts,
27        rpc_id: Option<RpcId>,
28    },
29    #[action_event(level = trace)]
30    OfferSdpCreatePending {
31        peer_id: PeerId,
32    },
33    OfferSdpCreateError {
34        peer_id: PeerId,
35        error: String,
36    },
37    OfferSdpCreateSuccess {
38        peer_id: PeerId,
39        sdp: String,
40    },
41    OfferReady {
42        peer_id: PeerId,
43        offer: Box<webrtc::Offer>,
44    },
45    OfferSendSuccess {
46        peer_id: PeerId,
47    },
48    #[action_event(level = trace)]
49    AnswerRecvPending {
50        peer_id: PeerId,
51    },
52    AnswerRecvError {
53        peer_id: PeerId,
54        error: P2pConnectionErrorResponse,
55    },
56    AnswerRecvSuccess {
57        peer_id: PeerId,
58        answer: Box<webrtc::Answer>,
59    },
60    #[action_event(level = trace)]
61    FinalizePending {
62        peer_id: PeerId,
63    },
64    /// Error finalizing outgoing connection.
65    #[action_event(level = debug)]
66    FinalizeError {
67        peer_id: PeerId,
68        error: String,
69    },
70    /// Outgoing connection successfully finalized.
71    FinalizeSuccess {
72        peer_id: PeerId,
73        remote_auth: Option<webrtc::ConnectionAuthEncrypted>,
74    },
75    /// Timeout establishing connection to a peer.
76    Timeout {
77        peer_id: PeerId,
78    },
79    /// Error connecting to a peer.
80    #[action_event(level = debug)]
81    Error {
82        peer_id: PeerId,
83        error: P2pConnectionOutgoingError,
84    },
85    /// Outgoing connection is successful.
86    Success {
87        peer_id: PeerId,
88    },
89}
90
91impl redux::EnablingCondition<P2pState> for P2pConnectionOutgoingAction {
92    fn is_enabled(&self, state: &P2pState, time: redux::Timestamp) -> bool {
93        match self {
94            P2pConnectionOutgoingAction::RandomInit =>  !state.already_has_min_peers() && state.disconnected_peers().next().is_some(),
95            P2pConnectionOutgoingAction::Init { opts, .. } => {
96                !state.already_has_min_peers() &&
97                &state.my_id() != opts.peer_id() &&
98                state
99                    .peers
100                    .get(opts.peer_id())
101                    .is_none_or(|peer| !peer.status.is_connected_or_connecting())
102            }
103            P2pConnectionOutgoingAction::Reconnect { opts, .. } => {
104                !state.already_has_min_peers()
105                    && state.peers.get(opts.peer_id()).is_some_and( |peer| {
106                        peer.can_reconnect(time, &state.config.timeouts)
107                    })
108            }
109            P2pConnectionOutgoingAction::OfferSdpCreatePending { peer_id } => state
110                .peers
111                .get(peer_id)
112                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
113                        P2pConnectionOutgoingState::Init { .. },
114                    )))),
115            P2pConnectionOutgoingAction::OfferSdpCreateError { peer_id, .. } => state
116                .peers
117                .get(peer_id)
118                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
119                        P2pConnectionOutgoingState::OfferSdpCreatePending { .. },
120                    )))),
121            P2pConnectionOutgoingAction::OfferSdpCreateSuccess { peer_id, .. } => state
122                .peers
123                .get(peer_id)
124                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
125                        P2pConnectionOutgoingState::OfferSdpCreatePending { .. },
126                    )))),
127            P2pConnectionOutgoingAction::OfferReady { peer_id, .. } => state
128                .peers
129                .get(peer_id)
130                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
131                        P2pConnectionOutgoingState::OfferSdpCreateSuccess { .. },
132                    )))),
133            P2pConnectionOutgoingAction::OfferSendSuccess { peer_id } => state
134                .peers
135                .get(peer_id)
136                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
137                        P2pConnectionOutgoingState::OfferReady { .. },
138                    )))),
139            P2pConnectionOutgoingAction::AnswerRecvPending { peer_id } => state
140                .peers
141                .get(peer_id)
142                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
143                        P2pConnectionOutgoingState::OfferSendSuccess { .. },
144                    )))),
145            P2pConnectionOutgoingAction::AnswerRecvError { peer_id, .. } => state
146                .peers
147                .get(peer_id)
148                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
149                        P2pConnectionOutgoingState::AnswerRecvPending { .. },
150                    )))),
151            P2pConnectionOutgoingAction::AnswerRecvSuccess { peer_id, .. } => state
152                .peers
153                .get(peer_id)
154                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
155                        P2pConnectionOutgoingState::AnswerRecvPending { .. },
156                    )))),
157            P2pConnectionOutgoingAction::FinalizePending { peer_id } => state
158                .peers
159                .get(peer_id)
160                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(v)) if match v {
161                        P2pConnectionOutgoingState::Init { opts, .. } => opts.is_libp2p(),
162                        P2pConnectionOutgoingState::AnswerRecvSuccess { .. } => true,
163                        _ => false,
164                    })),
165            P2pConnectionOutgoingAction::FinalizeError { peer_id, .. } => state
166                .peers
167                .get(peer_id)
168                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
169                        P2pConnectionOutgoingState::FinalizePending { .. },
170                    )))),
171            P2pConnectionOutgoingAction::FinalizeSuccess { peer_id, remote_auth: auth } => state
172                .peers
173                .get(peer_id)
174                .filter(|p| auth.is_some() || p.is_libp2p())
175                .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
176                        P2pConnectionOutgoingState::FinalizePending { .. },
177                    )))),
178            P2pConnectionOutgoingAction::Timeout { peer_id } => state
179                .peers
180                .get(peer_id)
181                .and_then(|peer| peer.status.as_connecting()?.as_outgoing())
182                .is_some_and( |s| s.is_timed_out(time, &state.config.timeouts)),
183            P2pConnectionOutgoingAction::Error { peer_id, error } => state
184                .peers
185                .get(peer_id)
186                .is_some_and( |peer| match &peer.status {
187                    P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(s)) => match error {
188                        P2pConnectionOutgoingError::SdpCreateError(_) => {
189                            matches!(s, P2pConnectionOutgoingState::OfferSdpCreatePending { .. })
190                        }
191                        P2pConnectionOutgoingError::Rejected(_)
192                        | P2pConnectionOutgoingError::RemoteSignalDecryptionFailed
193                        | P2pConnectionOutgoingError::RemoteInternalError => {
194                            matches!(s, P2pConnectionOutgoingState::AnswerRecvPending { .. })
195                        }
196                        P2pConnectionOutgoingError::FinalizeError(_) => {
197                            matches!(s, P2pConnectionOutgoingState::FinalizePending { .. })
198                        }
199                        P2pConnectionOutgoingError::ConnectionAuthError => {
200                            matches!(s, P2pConnectionOutgoingState::FinalizeSuccess { .. })
201                        }
202                        P2pConnectionOutgoingError::Timeout => true,
203                    },
204                    _ => false,
205                }),
206            P2pConnectionOutgoingAction::Success { peer_id } => {
207                state
208                    .peers
209                    .get(peer_id)
210                    .is_some_and( |peer| matches!(&peer.status, P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(
211                            P2pConnectionOutgoingState::FinalizeSuccess { .. },
212                        ))))
213            }
214        }
215    }
216}
217
218// --- From<LeafAction> for Action impls.
219use crate::{
220    connection::{P2pConnectionAction, P2pConnectionState},
221    P2pPeerStatus,
222};
223
224use super::P2pConnectionOutgoingState;
225
226impl From<P2pConnectionOutgoingAction> for crate::P2pAction {
227    fn from(a: P2pConnectionOutgoingAction) -> Self {
228        Self::Connection(P2pConnectionAction::Outgoing(a))
229    }
230}