1use crate::{
2 listener::{ConnectionListener, ListenerEvent},
3 message::PeerMessage,
4 peers::{InboundConnectionError, PeersManager},
5 protocol::IntoRlpxSubProtocol,
6 session::{Direction, PendingSessionHandshakeError, SessionEvent, SessionId, SessionManager},
7 state::{NetworkState, SessionActivation, StateAction},
8};
9use futures::Stream;
10use reth_eth_wire::{
11 errors::EthStreamError, Capabilities, DisconnectReason, EthNetworkPrimitives, EthVersion,
12 NetworkPrimitives, UnifiedStatus,
13};
14use reth_network_api::{PeerRequest, PeerRequestSender};
15use reth_network_peers::PeerId;
16use std::{
17 io,
18 net::SocketAddr,
19 pin::Pin,
20 sync::Arc,
21 task::{Context, Poll},
22};
23use tracing::trace;
24
25#[cfg_attr(doc, aquamarine::aquamarine)]
26#[derive(Debug)]
50#[must_use = "Swarm does nothing unless polled"]
51pub(crate) struct Swarm<N: NetworkPrimitives = EthNetworkPrimitives> {
52 incoming: ConnectionListener,
54 sessions: SessionManager<N>,
56 state: NetworkState<N>,
58}
59
60impl<N: NetworkPrimitives> Swarm<N> {
63 pub(crate) const fn new(
65 incoming: ConnectionListener,
66 sessions: SessionManager<N>,
67 state: NetworkState<N>,
68 ) -> Self {
69 Self { incoming, sessions, state }
70 }
71
72 pub(crate) fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
74 self.sessions_mut().add_rlpx_sub_protocol(protocol);
75 }
76
77 pub(crate) const fn state(&self) -> &NetworkState<N> {
79 &self.state
80 }
81
82 pub(crate) const fn state_mut(&mut self) -> &mut NetworkState<N> {
84 &mut self.state
85 }
86
87 pub(crate) const fn listener(&self) -> &ConnectionListener {
89 &self.incoming
90 }
91
92 pub(crate) const fn sessions(&self) -> &SessionManager<N> {
94 &self.sessions
95 }
96
97 pub(crate) const fn sessions_mut(&mut self) -> &mut SessionManager<N> {
99 &mut self.sessions
100 }
101
102 pub(crate) const fn peers(&self) -> &PeersManager {
104 self.state.peers()
105 }
106
107 pub(crate) const fn peers_mut(&mut self) -> &mut PeersManager {
109 self.state.peers_mut()
110 }
111}
112
113impl<N: NetworkPrimitives> Swarm<N> {
114 pub(crate) fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_id: PeerId) {
116 self.sessions.dial_outbound(remote_addr, remote_id)
117 }
118
119 fn on_session_event(&mut self, event: SessionEvent<N>) -> Option<SwarmEvent<N>> {
124 match event {
125 SessionEvent::SessionEstablished {
126 peer_id,
127 remote_addr,
128 client_version,
129 capabilities,
130 version,
131 status,
132 messages,
133 direction,
134 timeout,
135 range_info,
136 supports_snap,
137 } => {
138 self.state.on_session_activated(SessionActivation {
139 peer: peer_id,
140 capabilities: capabilities.clone(),
141 status: status.clone(),
142 request_tx: messages.clone(),
143 timeout,
144 range_info,
145 supports_snap,
146 });
147 Some(SwarmEvent::SessionEstablished {
148 peer_id,
149 remote_addr,
150 client_version,
151 capabilities,
152 version,
153 messages,
154 status,
155 direction,
156 })
157 }
158 SessionEvent::AlreadyConnected { peer_id, remote_addr, direction } => {
159 trace!(target: "net", ?peer_id, ?remote_addr, ?direction, "already connected");
160 self.state.peers_mut().on_already_connected(direction);
161 None
162 }
163 SessionEvent::ValidMessage { peer_id, message } => {
164 Some(SwarmEvent::ValidMessage { peer_id, message })
165 }
166 SessionEvent::IncomingPendingSessionClosed { remote_addr, error } => {
167 Some(SwarmEvent::IncomingPendingSessionClosed { remote_addr, error })
168 }
169 SessionEvent::OutgoingPendingSessionClosed { remote_addr, peer_id, error } => {
170 Some(SwarmEvent::OutgoingPendingSessionClosed { remote_addr, peer_id, error })
171 }
172 SessionEvent::Disconnected { peer_id, remote_addr } => {
173 self.state.on_session_closed(peer_id);
174 Some(SwarmEvent::SessionClosed { peer_id, remote_addr, error: None })
175 }
176 SessionEvent::SessionClosedOnConnectionError { peer_id, remote_addr, error } => {
177 self.state.on_session_closed(peer_id);
178 Some(SwarmEvent::SessionClosed { peer_id, remote_addr, error: Some(error) })
179 }
180 SessionEvent::OutgoingConnectionError { remote_addr, peer_id, error } => {
181 Some(SwarmEvent::OutgoingConnectionError { peer_id, remote_addr, error })
182 }
183 SessionEvent::BadMessage { peer_id } => Some(SwarmEvent::BadMessage { peer_id }),
184 SessionEvent::ProtocolBreach { peer_id } => {
185 Some(SwarmEvent::ProtocolBreach { peer_id })
186 }
187 }
188 }
189
190 fn on_connection(&mut self, event: ListenerEvent) -> Option<SwarmEvent<N>> {
194 match event {
195 ListenerEvent::Error(err) => return Some(SwarmEvent::TcpListenerError(err)),
196 ListenerEvent::ListenerClosed { local_address: address } => {
197 return Some(SwarmEvent::TcpListenerClosed { remote_addr: address })
198 }
199 ListenerEvent::Incoming { stream, remote_addr } => {
200 if self.is_shutting_down() {
202 return None
203 }
204 if let Err(err) = self.peers_mut().on_incoming_pending_session(remote_addr.ip()) {
206 match err {
207 InboundConnectionError::IpBanned => {
208 trace!(target: "net", ?remote_addr, "The incoming ip address is in the ban list");
209 }
210 InboundConnectionError::ExceedsCapacity => {
211 trace!(target: "net", ?remote_addr, "No capacity for incoming connection");
212 self.sessions.try_disconnect_incoming_connection(
213 stream,
214 DisconnectReason::TooManyPeers,
215 );
216 }
217 }
218 return None
219 }
220
221 match self.sessions.on_incoming(stream, remote_addr) {
222 Ok(session_id) => {
223 trace!(target: "net", ?remote_addr, "Incoming connection");
224 return Some(SwarmEvent::IncomingTcpConnection { session_id, remote_addr })
225 }
226 Err(err) => {
227 trace!(target: "net", %err, "Incoming connection rejected, capacity already reached.");
228 self.state_mut()
229 .peers_mut()
230 .on_incoming_pending_session_rejected_internally();
231 }
232 }
233 }
234 }
235 None
236 }
237
238 fn on_state_action(&mut self, event: StateAction<N>) -> Option<SwarmEvent<N>> {
240 match event {
241 StateAction::Connect { remote_addr, peer_id } => {
242 self.dial_outbound(remote_addr, peer_id);
243 return Some(SwarmEvent::OutgoingTcpConnection { remote_addr, peer_id })
244 }
245 StateAction::Disconnect { peer_id, reason } => {
246 self.sessions.disconnect(peer_id, reason);
247 }
248 StateAction::NewBlock { peer_id, block: msg } => {
249 let msg = PeerMessage::NewBlock(msg);
250 self.sessions.send_message(&peer_id, msg);
251 }
252 StateAction::NewBlockHashes { peer_id, hashes } => {
253 let msg = PeerMessage::NewBlockHashes(hashes);
254 self.sessions.send_message(&peer_id, msg);
255 }
256 StateAction::PeerAdded(peer_id) => return Some(SwarmEvent::PeerAdded(peer_id)),
257 StateAction::PeerRemoved(peer_id) => return Some(SwarmEvent::PeerRemoved(peer_id)),
258 StateAction::DiscoveredNode { peer_id, addr, fork_id } => {
259 if self.is_shutting_down() {
260 return None
261 }
262
263 let enforce = self.peers().enforce_enr_fork_id();
270 let allow = match fork_id {
271 Some(f) => self.sessions.is_valid_fork_id(f),
272 None => !enforce,
273 };
274 if allow {
275 self.peers_mut().add_peer(peer_id, addr, fork_id);
276 }
277 }
278 StateAction::DiscoveredEnrForkId { peer_id, addr, fork_id } => {
279 if self.sessions.is_valid_fork_id(fork_id) {
280 self.peers_mut().add_peer(peer_id, addr, Some(fork_id));
281 } else {
282 trace!(target: "net", ?peer_id, remote_fork_id=?fork_id, our_fork_id=?self.sessions.fork_id(), "fork id mismatch, removing peer");
283 self.peers_mut().remove_peer(peer_id);
284 }
285 }
286 }
287 None
288 }
289
290 pub(crate) const fn on_shutdown_requested(&mut self) {
292 self.peers_mut().on_shutdown();
293 }
294
295 #[inline]
297 pub(crate) const fn is_shutting_down(&self) -> bool {
298 self.peers().connection_state().is_shutting_down()
299 }
300
301 pub(crate) const fn on_network_state_change(&mut self, network_state: NetworkConnectionState) {
303 self.peers_mut().on_network_state_change(network_state);
304 }
305}
306
307impl<N: NetworkPrimitives> Stream for Swarm<N> {
308 type Item = SwarmEvent<N>;
309
310 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
318 let this = self.get_mut();
319
320 loop {
324 while let Poll::Ready(action) = this.state.poll(cx) {
325 if let Some(event) = this.on_state_action(action) {
326 return Poll::Ready(Some(event))
327 }
328 }
329
330 match this.sessions.poll(cx) {
332 Poll::Pending => {}
333 Poll::Ready(event) => {
334 if let Some(event) = this.on_session_event(event) {
335 return Poll::Ready(Some(event))
336 }
337 continue
338 }
339 }
340
341 match Pin::new(&mut this.incoming).poll(cx) {
343 Poll::Pending => {}
344 Poll::Ready(event) => {
345 if let Some(event) = this.on_connection(event) {
346 return Poll::Ready(Some(event))
347 }
348 continue
349 }
350 }
351
352 return Poll::Pending
353 }
354 }
355}
356
357pub(crate) enum SwarmEvent<N: NetworkPrimitives = EthNetworkPrimitives> {
360 ValidMessage {
362 peer_id: PeerId,
364 message: PeerMessage<N>,
366 },
367 BadMessage {
369 peer_id: PeerId,
371 },
372 ProtocolBreach {
374 peer_id: PeerId,
376 },
377 TcpListenerClosed {
379 remote_addr: SocketAddr,
381 },
382 TcpListenerError(io::Error),
384 IncomingTcpConnection {
389 session_id: SessionId,
391 remote_addr: SocketAddr,
393 },
394 OutgoingTcpConnection {
396 peer_id: PeerId,
398 remote_addr: SocketAddr,
399 },
400 SessionEstablished {
401 peer_id: PeerId,
402 remote_addr: SocketAddr,
403 client_version: Arc<str>,
404 capabilities: Arc<Capabilities>,
405 version: EthVersion,
407 messages: PeerRequestSender<PeerRequest<N>>,
408 status: Arc<UnifiedStatus>,
409 direction: Direction,
410 },
411 SessionClosed {
412 peer_id: PeerId,
413 remote_addr: SocketAddr,
414 error: Option<EthStreamError>,
416 },
417 PeerAdded(PeerId),
419 PeerRemoved(PeerId),
421 IncomingPendingSessionClosed {
423 remote_addr: SocketAddr,
424 error: Option<PendingSessionHandshakeError>,
425 },
426 OutgoingPendingSessionClosed {
428 remote_addr: SocketAddr,
429 peer_id: PeerId,
430 error: Option<PendingSessionHandshakeError>,
431 },
432 OutgoingConnectionError { remote_addr: SocketAddr, peer_id: PeerId, error: io::Error },
434}
435
436#[derive(Debug, Default)]
441pub enum NetworkConnectionState {
442 #[default]
444 Active,
445 ShuttingDown,
447 Hibernate,
449}
450
451impl NetworkConnectionState {
452 pub(crate) const fn is_active(&self) -> bool {
454 matches!(self, Self::Active)
455 }
456
457 pub(crate) const fn is_shutting_down(&self) -> bool {
459 matches!(self, Self::ShuttingDown)
460 }
461}