1use std::net::{IpAddr, SocketAddr};
2
3use mina_p2p::{
4 channels::{rpc::P2pChannelsRpcAction, P2pChannelsAction},
5 connection::{incoming::P2pConnectionIncomingAction, outgoing::P2pConnectionOutgoingAction},
6 disconnection::P2pDisconnectionAction,
7 identify::P2pIdentifyAction,
8 network::identify::P2pNetworkIdentify,
9 peer::P2pPeerAction,
10 MioEvent, P2pAction, P2pEvent, PeerId,
11};
12
13use crate::cluster::ClusterEvent;
14
15#[derive(Debug)]
16pub enum RustNodeEvent {
17 Interface {
18 addr: IpAddr,
19 },
20 ListenerReady {
21 addr: SocketAddr,
22 },
23 ListenerError {
24 addr: SocketAddr,
25 error: String,
26 },
27 PeerConnected {
28 peer_id: PeerId,
29 incoming: bool,
30 },
31 PeerConnectionError {
32 peer_id: Option<PeerId>,
33 incoming: bool,
34 error: String,
35 },
36 PeerDisconnected {
37 peer_id: PeerId,
38 reason: String,
39 },
40 RpcChannelReady {
41 peer_id: PeerId,
42 },
43 RpcChannelRequestReceived {
44 peer_id: PeerId,
45 id: mina_p2p::channels::rpc::P2pRpcId,
46 request: mina_p2p::channels::rpc::P2pRpcRequest,
47 },
48 RpcChannelResponseReceived {
49 peer_id: PeerId,
50 id: mina_p2p::channels::rpc::P2pRpcId,
51 response: Option<mina_p2p::channels::rpc::P2pRpcResponse>,
52 },
53 Identify {
54 peer_id: PeerId,
55 info: Box<P2pNetworkIdentify>,
56 },
57 KadBootstrapFinished,
58 KadUpdateFindNodeRequest {
59 peer_id: PeerId,
60 closest_peers: Vec<mina_p2p::network::kad::P2pNetworkKadEntry>,
61 },
62 P2p {
64 event: P2pEvent,
65 },
66 Idle,
68}
69
70pub(super) trait RustNodeEventStore {
71 fn store_event(&mut self, event: RustNodeEvent);
72}
73
74pub fn event_mapper_effect(store: &mut super::redux::Store, action: P2pAction) {
75 let store_event = |store: &mut super::redux::Store, event| store.service().store_event(event);
76 match action {
77 P2pAction::Peer(P2pPeerAction::Ready { peer_id, incoming }) => {
78 store_event(store, RustNodeEvent::PeerConnected { peer_id, incoming })
79 }
80 P2pAction::Connection(action) => match action {
81 mina_p2p::connection::P2pConnectionAction::Outgoing(
82 P2pConnectionOutgoingAction::Error { peer_id, error },
83 ) => store_event(
84 store,
85 RustNodeEvent::PeerConnectionError {
86 peer_id: Some(peer_id),
87 incoming: false,
88 error: error.to_string(),
89 },
90 ),
91 mina_p2p::connection::P2pConnectionAction::Incoming(
92 P2pConnectionIncomingAction::Error { peer_id, error },
93 ) => store_event(
94 store,
95 RustNodeEvent::PeerConnectionError {
96 peer_id: Some(peer_id),
97 incoming: true,
98 error: error.to_string(),
99 },
100 ),
101 _ => {}
102 },
103
104 P2pAction::Disconnection(P2pDisconnectionAction::Init { peer_id, reason }) => store_event(
105 store,
106 RustNodeEvent::PeerDisconnected {
107 peer_id,
108 reason: reason.to_string(),
109 },
110 ),
111 P2pAction::Network(mina_p2p::P2pNetworkAction::Kad(
112 mina_p2p::P2pNetworkKadAction::System(
113 mina_p2p::P2pNetworkKademliaAction::BootstrapFinished,
114 ),
115 )) => {
116 store_event(store, RustNodeEvent::KadBootstrapFinished);
117 }
118 P2pAction::Network(mina_p2p::P2pNetworkAction::Kad(
119 mina_p2p::P2pNetworkKadAction::System(
120 mina_p2p::P2pNetworkKademliaAction::UpdateFindNodeRequest {
121 peer_id,
122 closest_peers,
123 ..
124 },
125 ),
126 )) => {
127 store_event(
128 store,
129 RustNodeEvent::KadUpdateFindNodeRequest {
130 peer_id,
131 closest_peers,
132 },
133 );
134 }
135 P2pAction::Channels(P2pChannelsAction::Rpc(action)) => match action {
136 P2pChannelsRpcAction::Ready { peer_id } => {
137 store_event(store, RustNodeEvent::RpcChannelReady { peer_id })
138 }
139 P2pChannelsRpcAction::RequestReceived {
140 peer_id,
141 id,
142 request,
143 } => {
144 store_event(
149 store,
150 RustNodeEvent::RpcChannelRequestReceived {
151 peer_id,
152 id,
153 request: *request,
154 },
155 )
156 }
157 P2pChannelsRpcAction::ResponseReceived {
158 peer_id,
159 id,
160 response,
161 } => store_event(
162 store,
163 RustNodeEvent::RpcChannelResponseReceived {
164 peer_id,
165 id,
166 response: response.map(|v| *v),
167 },
168 ),
169 _ => {}
170 },
171 P2pAction::Identify(P2pIdentifyAction::UpdatePeerInformation { peer_id, info, .. }) => {
172 store_event(store, RustNodeEvent::Identify { peer_id, info })
173 }
174
175 P2pAction::Network(mina_p2p::P2pNetworkAction::Scheduler(action)) => match action {
176 mina_p2p::P2pNetworkSchedulerAction::InterfaceDetected { ip } => {
177 store_event(store, RustNodeEvent::Interface { addr: ip })
178 }
179 mina_p2p::P2pNetworkSchedulerAction::ListenerReady { listener } => {
180 store_event(store, RustNodeEvent::ListenerReady { addr: listener })
181 }
182 mina_p2p::P2pNetworkSchedulerAction::ListenerError { listener, error } => store_event(
183 store,
184 RustNodeEvent::ListenerError {
185 addr: listener,
186 error,
187 },
188 ),
189 mina_p2p::P2pNetworkSchedulerAction::Error { addr, error } => {
190 if let Some(conn_state) = store.state().0.network.scheduler.connections.get(&addr) {
191 if conn_state.peer_id().is_none() {
192 let error = error.to_string();
193 let incoming = conn_state.incoming;
194 store_event(
195 store,
196 RustNodeEvent::PeerConnectionError {
197 peer_id: None,
198 incoming,
199 error,
200 },
201 );
202 }
203 }
204 }
205 _ => {}
206 },
207 _ => {}
208 }
209}
210
211pub fn is_error(event: &ClusterEvent) -> bool {
212 let ClusterEvent::Rust { event, .. } = event else {
213 return false;
214 };
215 match event {
216 RustNodeEvent::ListenerError { .. } => true,
217 RustNodeEvent::PeerConnectionError { .. } => true,
218 RustNodeEvent::PeerDisconnected { .. } => true,
219 RustNodeEvent::P2p { event } => match event {
220 P2pEvent::Connection(_event) => false, P2pEvent::Channel(_event) => false, P2pEvent::MioEvent(event) => matches!(
223 event,
224 MioEvent::ListenerError { .. }
225 | MioEvent::IncomingConnectionDidAccept(_, Err(_))
226 | MioEvent::IncomingDataDidReceive(_, Err(_))
227 | MioEvent::OutgoingConnectionDidConnect(_, Err(_))
228 | MioEvent::OutgoingDataDidSend(_, Err(_))
229 | MioEvent::ConnectionDidClose(_, Err(_))
230 ),
231 },
232 _ => false,
233 }
234}
235
236pub fn allow_disconnections(event: &ClusterEvent) -> bool {
237 let ClusterEvent::Rust { event, .. } = event else {
238 return false;
239 };
240 match event {
241 RustNodeEvent::ListenerError { .. } => true,
242 RustNodeEvent::PeerConnectionError { .. } => false,
243 RustNodeEvent::PeerDisconnected { .. } => true,
244 RustNodeEvent::P2p { event } => match event {
245 P2pEvent::Connection(_event) => false, P2pEvent::Channel(_event) => false, P2pEvent::MioEvent(event) => matches!(
248 event,
249 MioEvent::ListenerError { .. } | MioEvent::IncomingConnectionDidAccept(_, Err(_)) ),
254 },
255 _ => false,
256 }
257}