1use crate::{
2 budget::DEFAULT_BUDGET_PRIORITIZED_EVENTS,
3 listener::{ConnectionListener, ListenerEvent},
4 message::PeerMessage,
5 peers::{InboundConnectionError, PeersManager},
6 protocol::IntoRlpxSubProtocol,
7 session::{Direction, PendingSessionHandshakeError, SessionEvent, SessionId, SessionManager},
8 state::{NetworkState, SessionActivation, StateAction},
9};
10use futures::Stream;
11use reth_eth_wire::{
12 errors::EthStreamError, Capabilities, DisconnectReason, EthNetworkPrimitives, EthVersion,
13 NetworkPrimitives, UnifiedStatus,
14};
15use reth_network_api::{PeerRequest, PeerRequestSender};
16use reth_network_peers::PeerId;
17use std::{
18 io,
19 net::SocketAddr,
20 pin::Pin,
21 sync::Arc,
22 task::{Context, Poll},
23};
24use tracing::trace;
25
26#[cfg_attr(doc, aquamarine::aquamarine)]
27#[derive(Debug)]
51#[must_use = "Swarm does nothing unless polled"]
52pub(crate) struct Swarm<N: NetworkPrimitives = EthNetworkPrimitives> {
53 incoming: ConnectionListener,
55 sessions: SessionManager<N>,
57 state: NetworkState<N>,
59 session_events_in_row: u32,
61}
62
63impl<N: NetworkPrimitives> Swarm<N> {
66 pub(crate) const fn new(
68 incoming: ConnectionListener,
69 sessions: SessionManager<N>,
70 state: NetworkState<N>,
71 ) -> Self {
72 Self { incoming, sessions, state, session_events_in_row: 0 }
73 }
74
75 pub(crate) fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
77 self.sessions_mut().add_rlpx_sub_protocol(protocol);
78 }
79
80 pub(crate) const fn state(&self) -> &NetworkState<N> {
82 &self.state
83 }
84
85 pub(crate) const fn state_mut(&mut self) -> &mut NetworkState<N> {
87 &mut self.state
88 }
89
90 pub(crate) const fn listener(&self) -> &ConnectionListener {
92 &self.incoming
93 }
94
95 pub(crate) const fn sessions(&self) -> &SessionManager<N> {
97 &self.sessions
98 }
99
100 pub(crate) const fn sessions_mut(&mut self) -> &mut SessionManager<N> {
102 &mut self.sessions
103 }
104
105 pub(crate) const fn peers(&self) -> &PeersManager {
107 self.state.peers()
108 }
109
110 pub(crate) const fn peers_mut(&mut self) -> &mut PeersManager {
112 self.state.peers_mut()
113 }
114}
115
116impl<N: NetworkPrimitives> Swarm<N> {
117 pub(crate) fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_id: PeerId) -> bool {
119 if let Err(err) = self.sessions.try_dial_outbound(remote_addr, remote_id) {
120 trace!(target: "net", %err, "Outgoing connection rejected, capacity already reached.");
121 self.peers_mut().on_outgoing_pending_session_gracefully_closed(&remote_id);
123 return false
124 }
125 true
126 }
127
128 fn on_session_event(&mut self, event: SessionEvent<N>) -> Option<SwarmEvent<N>> {
133 match event {
134 SessionEvent::SessionEstablished {
135 peer_id,
136 remote_addr,
137 client_version,
138 capabilities,
139 version,
140 status,
141 messages,
142 direction,
143 timeout,
144 range_info,
145 supports_snap,
146 } => {
147 self.state.on_session_activated(SessionActivation {
148 peer: peer_id,
149 capabilities: capabilities.clone(),
150 version,
151 status: status.clone(),
152 request_tx: messages.clone(),
153 timeout,
154 range_info,
155 supports_snap,
156 });
157 Some(SwarmEvent::SessionEstablished {
158 peer_id,
159 remote_addr,
160 client_version,
161 capabilities,
162 version,
163 messages,
164 status,
165 direction,
166 })
167 }
168 SessionEvent::AlreadyConnected { peer_id, remote_addr, direction } => {
169 trace!(target: "net", ?peer_id, ?remote_addr, ?direction, "already connected");
170 self.state.peers_mut().on_already_connected(direction);
171 None
172 }
173 SessionEvent::ValidMessage { peer_id, message } => {
174 Some(SwarmEvent::ValidMessage { peer_id, message })
175 }
176 SessionEvent::IncomingPendingSessionClosed { remote_addr, error } => {
177 Some(SwarmEvent::IncomingPendingSessionClosed { remote_addr, error })
178 }
179 SessionEvent::OutgoingPendingSessionClosed { remote_addr, peer_id, error } => {
180 Some(SwarmEvent::OutgoingPendingSessionClosed { remote_addr, peer_id, error })
181 }
182 SessionEvent::Disconnected { peer_id, remote_addr } => {
183 self.state.on_session_closed(peer_id);
184 Some(SwarmEvent::SessionClosed { peer_id, remote_addr, error: None })
185 }
186 SessionEvent::SessionClosedOnConnectionError { peer_id, remote_addr, error } => {
187 self.state.on_session_closed(peer_id);
188 Some(SwarmEvent::SessionClosed { peer_id, remote_addr, error: Some(error) })
189 }
190 SessionEvent::OutgoingConnectionError { remote_addr, peer_id, error } => {
191 Some(SwarmEvent::OutgoingConnectionError { peer_id, remote_addr, error })
192 }
193 SessionEvent::BadMessage { peer_id } => Some(SwarmEvent::BadMessage { peer_id }),
194 SessionEvent::ProtocolBreach { peer_id } => {
195 Some(SwarmEvent::ProtocolBreach { peer_id })
196 }
197 }
198 }
199
200 fn on_connection(&mut self, event: ListenerEvent) -> Option<SwarmEvent<N>> {
204 match event {
205 ListenerEvent::Error(err) => return Some(SwarmEvent::TcpListenerError(err)),
206 ListenerEvent::ListenerClosed { local_address: address } => {
207 return Some(SwarmEvent::TcpListenerClosed { remote_addr: address })
208 }
209 ListenerEvent::Incoming { stream, remote_addr } => {
210 if self.is_shutting_down() {
212 return None
213 }
214 if let Err(err) = self.peers_mut().on_incoming_pending_session(remote_addr.ip()) {
216 match err {
217 InboundConnectionError::IpBanned => {
218 trace!(target: "net", ?remote_addr, "The incoming ip address is in the ban list");
219 }
220 InboundConnectionError::ExceedsCapacity => {
221 trace!(target: "net", ?remote_addr, "No capacity for incoming connection");
222 self.sessions.try_disconnect_incoming_connection(
223 stream,
224 DisconnectReason::TooManyPeers,
225 );
226 }
227 }
228 return None
229 }
230
231 match self.sessions.on_incoming(stream, remote_addr) {
232 Ok(session_id) => {
233 trace!(target: "net", ?remote_addr, "Incoming connection");
234 return Some(SwarmEvent::IncomingTcpConnection { session_id, remote_addr })
235 }
236 Err(err) => {
237 trace!(target: "net", %err, "Incoming connection rejected, capacity already reached.");
238 self.state_mut()
239 .peers_mut()
240 .on_incoming_pending_session_rejected_internally();
241 }
242 }
243 }
244 }
245 None
246 }
247
248 fn on_state_action(&mut self, event: StateAction<N>) -> Option<SwarmEvent<N>> {
250 match event {
251 StateAction::Connect { remote_addr, peer_id } => {
252 if self.dial_outbound(remote_addr, peer_id) {
253 return Some(SwarmEvent::OutgoingTcpConnection { remote_addr, peer_id })
254 }
255 }
256 StateAction::Disconnect { peer_id, reason } => {
257 self.sessions.disconnect(peer_id, reason);
258 }
259 StateAction::NewBlock { peer_id, block: msg } => {
260 let msg = PeerMessage::NewBlock(msg);
261 self.sessions.send_message(&peer_id, msg);
262 }
263 StateAction::NewBlockHashes { peer_id, hashes } => {
264 let msg = PeerMessage::NewBlockHashes(hashes);
265 self.sessions.send_message(&peer_id, msg);
266 }
267 StateAction::PeerAdded(peer_id) => return Some(SwarmEvent::PeerAdded(peer_id)),
268 StateAction::PeerRemoved(peer_id) => return Some(SwarmEvent::PeerRemoved(peer_id)),
269 StateAction::DiscoveredNode { peer_id, addr, fork_id } => {
270 if self.is_shutting_down() {
271 return None
272 }
273
274 let enforce = self.peers().enforce_enr_fork_id();
281 let allow = match fork_id {
282 Some(f) => self.sessions.is_valid_fork_id(f),
283 None => !enforce,
284 };
285 if allow {
286 self.peers_mut().add_peer(peer_id, addr, fork_id);
287 }
288 }
289 StateAction::DiscoveredEnrForkId { peer_id, addr, fork_id } => {
290 if self.sessions.is_valid_fork_id(fork_id) {
291 self.peers_mut().add_peer(peer_id, addr, Some(fork_id));
292 } else {
293 trace!(target: "net", ?peer_id, remote_fork_id=?fork_id, our_fork_id=?self.sessions.fork_id(), "fork id mismatch, removing peer");
294 self.peers_mut().remove_peer(peer_id);
295 }
296 }
297 }
298 None
299 }
300
301 pub(crate) const fn on_shutdown_requested(&mut self) {
303 self.peers_mut().on_shutdown();
304 }
305
306 #[inline]
308 pub(crate) const fn is_shutting_down(&self) -> bool {
309 self.peers().connection_state().is_shutting_down()
310 }
311
312 pub(crate) const fn on_network_state_change(&mut self, network_state: NetworkConnectionState) {
314 self.peers_mut().on_network_state_change(network_state);
315 }
316}
317
318impl<N: NetworkPrimitives> Stream for Swarm<N> {
319 type Item = SwarmEvent<N>;
320
321 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
329 let this = self.get_mut();
330
331 loop {
337 while let Poll::Ready(action) = this.state.poll(cx) {
338 if let Some(event) = this.on_state_action(action) {
339 return Poll::Ready(Some(event))
340 }
341 }
342
343 if this.session_events_in_row >= DEFAULT_BUDGET_PRIORITIZED_EVENTS {
344 this.session_events_in_row = 0;
345 if let Poll::Ready(event) = Pin::new(&mut this.incoming).poll(cx) {
346 if let Some(event) = this.on_connection(event) {
347 return Poll::Ready(Some(event))
348 }
349 continue
350 }
351 }
352
353 match this
355 .sessions
356 .poll(cx, |peer_id| this.state.peers().inbound_session_rejection(peer_id))
357 {
358 Poll::Pending => {}
359 Poll::Ready(event) => {
360 this.session_events_in_row += 1;
361 if let Some(event) = this.on_session_event(event) {
362 return Poll::Ready(Some(event))
363 }
364 continue
365 }
366 }
367
368 this.session_events_in_row = 0;
370 match Pin::new(&mut this.incoming).poll(cx) {
371 Poll::Pending => {}
372 Poll::Ready(event) => {
373 if let Some(event) = this.on_connection(event) {
374 return Poll::Ready(Some(event))
375 }
376 continue
377 }
378 }
379
380 return Poll::Pending
381 }
382 }
383}
384
385pub(crate) enum SwarmEvent<N: NetworkPrimitives = EthNetworkPrimitives> {
388 ValidMessage {
390 peer_id: PeerId,
392 message: PeerMessage<N>,
394 },
395 BadMessage {
397 peer_id: PeerId,
399 },
400 ProtocolBreach {
402 peer_id: PeerId,
404 },
405 TcpListenerClosed {
407 remote_addr: SocketAddr,
409 },
410 TcpListenerError(io::Error),
412 IncomingTcpConnection {
417 session_id: SessionId,
419 remote_addr: SocketAddr,
421 },
422 OutgoingTcpConnection {
424 peer_id: PeerId,
426 remote_addr: SocketAddr,
427 },
428 SessionEstablished {
429 peer_id: PeerId,
430 remote_addr: SocketAddr,
431 client_version: Arc<str>,
432 capabilities: Arc<Capabilities>,
433 version: EthVersion,
435 messages: PeerRequestSender<PeerRequest<N>>,
436 status: Arc<UnifiedStatus>,
437 direction: Direction,
438 },
439 SessionClosed {
440 peer_id: PeerId,
441 remote_addr: SocketAddr,
442 error: Option<EthStreamError>,
444 },
445 PeerAdded(PeerId),
447 PeerRemoved(PeerId),
449 IncomingPendingSessionClosed {
451 remote_addr: SocketAddr,
452 error: Option<PendingSessionHandshakeError>,
453 },
454 OutgoingPendingSessionClosed {
456 remote_addr: SocketAddr,
457 peer_id: PeerId,
458 error: Option<PendingSessionHandshakeError>,
459 },
460 OutgoingConnectionError { remote_addr: SocketAddr, peer_id: PeerId, error: io::Error },
462}
463
464#[derive(Debug, Default)]
469pub enum NetworkConnectionState {
470 #[default]
472 Active,
473 ShuttingDown,
475 Hibernate,
477}
478
479impl NetworkConnectionState {
480 pub(crate) const fn is_active(&self) -> bool {
482 matches!(self, Self::Active)
483 }
484
485 pub(crate) const fn is_shutting_down(&self) -> bool {
487 matches!(self, Self::ShuttingDown)
488 }
489}