Skip to main content

reth_network/
protocol.rs

1//! Support for handling additional RLPx-based application-level protocols.
2//!
3//! See also <https://github.com/ethereum/devp2p/blob/master/README.md>
4
5use alloy_primitives::bytes::BytesMut;
6use futures::Stream;
7use reth_eth_wire::{
8    capability::SharedCapabilities,
9    multiplex::ProtocolConnection,
10    protocol::{Protocol, ProtocolIngressLimits},
11};
12use reth_network_api::{Direction, PeerId};
13use std::{
14    fmt,
15    net::SocketAddr,
16    ops::{Deref, DerefMut},
17    pin::Pin,
18};
19
20/// A trait that allows to offer additional RLPx-based application-level protocols when establishing
21/// a peer-to-peer connection.
22pub trait ProtocolHandler: fmt::Debug + Send + Sync + 'static {
23    /// The type responsible for negotiating the protocol with the remote.
24    type ConnectionHandler: ConnectionHandler;
25
26    /// Invoked when a new incoming connection from the remote is requested
27    ///
28    /// If protocols for this outgoing should be announced to the remote, return a connection
29    /// handler.
30    fn on_incoming(&self, socket_addr: SocketAddr) -> Option<Self::ConnectionHandler>;
31
32    /// Invoked when a new outgoing connection to the remote is requested.
33    ///
34    /// If protocols for this outgoing should be announced to the remote, return a connection
35    /// handler.
36    fn on_outgoing(
37        &self,
38        socket_addr: SocketAddr,
39        peer_id: PeerId,
40    ) -> Option<Self::ConnectionHandler>;
41}
42
43/// A trait that allows to authenticate a protocol after the `RLPx` connection was established.
44pub trait ConnectionHandler: Send + Sync + 'static {
45    /// The connection that yields messages to send to the remote.
46    ///
47    /// The connection will be closed when this stream resolves.
48    type Connection: Stream<Item = BytesMut> + Send + 'static;
49
50    /// Returns the protocol to announce when the `RLPx` connection will be established.
51    ///
52    /// This will be negotiated with the remote peer.
53    fn protocol(&self) -> Protocol;
54
55    /// Returns local resource limits for inbound messages of this protocol.
56    fn inbound_limits(&self) -> ProtocolIngressLimits {
57        ProtocolIngressLimits::default()
58    }
59
60    /// Invoked when the `RLPx` connection has been established by the peer does not share the
61    /// protocol.
62    fn on_unsupported_by_peer(
63        self,
64        supported: &SharedCapabilities,
65        direction: Direction,
66        peer_id: PeerId,
67    ) -> OnNotSupported;
68
69    /// Invoked when the `RLPx` connection was established.
70    ///
71    /// The returned future should resolve when the connection should disconnect.
72    fn into_connection(
73        self,
74        direction: Direction,
75        peer_id: PeerId,
76        conn: ProtocolConnection,
77    ) -> Self::Connection;
78}
79
80/// What to do when a protocol is not supported by the remote.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
82pub enum OnNotSupported {
83    /// Proceed with the connection and ignore the protocol.
84    #[default]
85    KeepAlive,
86    /// Disconnect the connection.
87    Disconnect,
88}
89
90/// A wrapper type for a `RLPx` sub-protocol.
91#[derive(Debug)]
92pub struct RlpxSubProtocol(Box<dyn DynProtocolHandler>);
93
94/// A helper trait to convert a [`ProtocolHandler`] into a dynamic type
95pub trait IntoRlpxSubProtocol {
96    /// Converts the type into a [`RlpxSubProtocol`].
97    fn into_rlpx_sub_protocol(self) -> RlpxSubProtocol;
98}
99
100impl<T> IntoRlpxSubProtocol for T
101where
102    T: ProtocolHandler + Send + Sync + 'static,
103{
104    fn into_rlpx_sub_protocol(self) -> RlpxSubProtocol {
105        RlpxSubProtocol(Box::new(self))
106    }
107}
108
109impl IntoRlpxSubProtocol for RlpxSubProtocol {
110    fn into_rlpx_sub_protocol(self) -> RlpxSubProtocol {
111        self
112    }
113}
114
115/// Additional RLPx-based sub-protocols.
116#[derive(Debug, Default)]
117pub struct RlpxSubProtocols {
118    /// All extra protocols
119    protocols: Vec<RlpxSubProtocol>,
120}
121
122impl RlpxSubProtocols {
123    /// Adds a new protocol.
124    pub fn push(&mut self, protocol: impl IntoRlpxSubProtocol) {
125        self.protocols.push(protocol.into_rlpx_sub_protocol());
126    }
127
128    /// Returns all additional protocol handlers that should be announced to the remote during the
129    /// Rlpx handshake on an incoming connection.
130    pub(crate) fn on_incoming(&self, socket_addr: SocketAddr) -> RlpxSubProtocolHandlers {
131        RlpxSubProtocolHandlers(
132            self.protocols
133                .iter()
134                .filter_map(|protocol| protocol.0.on_incoming(socket_addr))
135                .collect(),
136        )
137    }
138
139    /// Returns all additional protocol handlers that should be announced to the remote during the
140    /// Rlpx handshake on an outgoing connection.
141    pub(crate) fn on_outgoing(
142        &self,
143        socket_addr: SocketAddr,
144        peer_id: PeerId,
145    ) -> RlpxSubProtocolHandlers {
146        RlpxSubProtocolHandlers(
147            self.protocols
148                .iter()
149                .filter_map(|protocol| protocol.0.on_outgoing(socket_addr, peer_id))
150                .collect(),
151        )
152    }
153}
154
155/// A set of additional RLPx-based sub-protocol connection handlers.
156#[derive(Default)]
157pub(crate) struct RlpxSubProtocolHandlers(pub(crate) Vec<Box<dyn DynConnectionHandler>>);
158
159impl RlpxSubProtocolHandlers {
160    /// Returns all handlers.
161    pub(crate) fn into_iter(self) -> impl Iterator<Item = Box<dyn DynConnectionHandler>> {
162        self.0.into_iter()
163    }
164}
165
166impl Deref for RlpxSubProtocolHandlers {
167    type Target = Vec<Box<dyn DynConnectionHandler>>;
168
169    fn deref(&self) -> &Self::Target {
170        &self.0
171    }
172}
173
174impl DerefMut for RlpxSubProtocolHandlers {
175    fn deref_mut(&mut self) -> &mut Self::Target {
176        &mut self.0
177    }
178}
179
180pub(crate) trait DynProtocolHandler: fmt::Debug + Send + Sync + 'static {
181    fn on_incoming(&self, socket_addr: SocketAddr) -> Option<Box<dyn DynConnectionHandler>>;
182
183    fn on_outgoing(
184        &self,
185        socket_addr: SocketAddr,
186        peer_id: PeerId,
187    ) -> Option<Box<dyn DynConnectionHandler>>;
188}
189
190impl<T: ProtocolHandler> DynProtocolHandler for T {
191    fn on_incoming(&self, socket_addr: SocketAddr) -> Option<Box<dyn DynConnectionHandler>> {
192        T::on_incoming(self, socket_addr)
193            .map(|handler| Box::new(handler) as Box<dyn DynConnectionHandler>)
194    }
195
196    fn on_outgoing(
197        &self,
198        socket_addr: SocketAddr,
199        peer_id: PeerId,
200    ) -> Option<Box<dyn DynConnectionHandler>> {
201        T::on_outgoing(self, socket_addr, peer_id)
202            .map(|handler| Box::new(handler) as Box<dyn DynConnectionHandler>)
203    }
204}
205
206/// Wrapper trait for internal ease of use.
207pub(crate) trait DynConnectionHandler: Send + Sync + 'static {
208    fn protocol(&self) -> Protocol;
209
210    fn inbound_limits(&self) -> ProtocolIngressLimits;
211
212    fn on_unsupported_by_peer(
213        self: Box<Self>,
214        supported: &SharedCapabilities,
215        direction: Direction,
216        peer_id: PeerId,
217    ) -> OnNotSupported;
218
219    fn into_connection(
220        self: Box<Self>,
221        direction: Direction,
222        peer_id: PeerId,
223        conn: ProtocolConnection,
224    ) -> Pin<Box<dyn Stream<Item = BytesMut> + Send + 'static>>;
225}
226
227impl<T: ConnectionHandler> DynConnectionHandler for T {
228    fn protocol(&self) -> Protocol {
229        T::protocol(self)
230    }
231
232    fn inbound_limits(&self) -> ProtocolIngressLimits {
233        T::inbound_limits(self)
234    }
235
236    fn on_unsupported_by_peer(
237        self: Box<Self>,
238        supported: &SharedCapabilities,
239        direction: Direction,
240        peer_id: PeerId,
241    ) -> OnNotSupported {
242        T::on_unsupported_by_peer(*self, supported, direction, peer_id)
243    }
244
245    fn into_connection(
246        self: Box<Self>,
247        direction: Direction,
248        peer_id: PeerId,
249        conn: ProtocolConnection,
250    ) -> Pin<Box<dyn Stream<Item = BytesMut> + Send + 'static>> {
251        Box::pin(T::into_connection(*self, direction, peer_id, conn))
252    }
253}