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 and the eth `Status` handshake
70    /// succeeded.
71    ///
72    /// Messages arriving before `Status` completes are queued within [`Self::inbound_limits`]
73    /// and an aggregate handshake budget of 256 KiB or 64 messages, whichever is reached first.
74    /// The callback runs when the authenticated connection is first polled.
75    ///
76    /// The returned future should resolve when the connection should disconnect.
77    fn into_connection(
78        self,
79        direction: Direction,
80        peer_id: PeerId,
81        conn: ProtocolConnection,
82    ) -> Self::Connection;
83}
84
85/// What to do when a protocol is not supported by the remote.
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
87pub enum OnNotSupported {
88    /// Proceed with the connection and ignore the protocol.
89    #[default]
90    KeepAlive,
91    /// Disconnect the connection.
92    Disconnect,
93}
94
95/// A wrapper type for a `RLPx` sub-protocol.
96#[derive(Debug)]
97pub struct RlpxSubProtocol(Box<dyn DynProtocolHandler>);
98
99/// A helper trait to convert a [`ProtocolHandler`] into a dynamic type
100pub trait IntoRlpxSubProtocol {
101    /// Converts the type into a [`RlpxSubProtocol`].
102    fn into_rlpx_sub_protocol(self) -> RlpxSubProtocol;
103}
104
105impl<T> IntoRlpxSubProtocol for T
106where
107    T: ProtocolHandler,
108{
109    fn into_rlpx_sub_protocol(self) -> RlpxSubProtocol {
110        RlpxSubProtocol(Box::new(self))
111    }
112}
113
114impl IntoRlpxSubProtocol for RlpxSubProtocol {
115    fn into_rlpx_sub_protocol(self) -> RlpxSubProtocol {
116        self
117    }
118}
119
120/// Additional RLPx-based sub-protocols.
121#[derive(Debug, Default)]
122pub struct RlpxSubProtocols {
123    /// All extra protocols
124    protocols: Vec<RlpxSubProtocol>,
125}
126
127impl RlpxSubProtocols {
128    /// Adds a new protocol.
129    pub fn push(&mut self, protocol: impl IntoRlpxSubProtocol) {
130        self.protocols.push(protocol.into_rlpx_sub_protocol());
131    }
132
133    /// Returns all additional protocol handlers that should be announced to the remote during the
134    /// Rlpx handshake on an incoming connection.
135    pub(crate) fn on_incoming(&self, socket_addr: SocketAddr) -> RlpxSubProtocolHandlers {
136        RlpxSubProtocolHandlers(
137            self.protocols
138                .iter()
139                .filter_map(|protocol| protocol.0.on_incoming(socket_addr))
140                .collect(),
141        )
142    }
143
144    /// Returns all additional protocol handlers that should be announced to the remote during the
145    /// Rlpx handshake on an outgoing connection.
146    pub(crate) fn on_outgoing(
147        &self,
148        socket_addr: SocketAddr,
149        peer_id: PeerId,
150    ) -> RlpxSubProtocolHandlers {
151        RlpxSubProtocolHandlers(
152            self.protocols
153                .iter()
154                .filter_map(|protocol| protocol.0.on_outgoing(socket_addr, peer_id))
155                .collect(),
156        )
157    }
158}
159
160/// A set of additional RLPx-based sub-protocol connection handlers.
161#[derive(Default)]
162pub(crate) struct RlpxSubProtocolHandlers(pub(crate) Vec<Box<dyn DynConnectionHandler>>);
163
164impl RlpxSubProtocolHandlers {
165    /// Returns all handlers.
166    pub(crate) fn into_iter(self) -> impl Iterator<Item = Box<dyn DynConnectionHandler>> {
167        self.0.into_iter()
168    }
169}
170
171impl Deref for RlpxSubProtocolHandlers {
172    type Target = Vec<Box<dyn DynConnectionHandler>>;
173
174    fn deref(&self) -> &Self::Target {
175        &self.0
176    }
177}
178
179impl DerefMut for RlpxSubProtocolHandlers {
180    fn deref_mut(&mut self) -> &mut Self::Target {
181        &mut self.0
182    }
183}
184
185pub(crate) trait DynProtocolHandler: fmt::Debug + Send + Sync + 'static {
186    fn on_incoming(&self, socket_addr: SocketAddr) -> Option<Box<dyn DynConnectionHandler>>;
187
188    fn on_outgoing(
189        &self,
190        socket_addr: SocketAddr,
191        peer_id: PeerId,
192    ) -> Option<Box<dyn DynConnectionHandler>>;
193}
194
195impl<T: ProtocolHandler> DynProtocolHandler for T {
196    fn on_incoming(&self, socket_addr: SocketAddr) -> Option<Box<dyn DynConnectionHandler>> {
197        T::on_incoming(self, socket_addr)
198            .map(|handler| Box::new(handler) as Box<dyn DynConnectionHandler>)
199    }
200
201    fn on_outgoing(
202        &self,
203        socket_addr: SocketAddr,
204        peer_id: PeerId,
205    ) -> Option<Box<dyn DynConnectionHandler>> {
206        T::on_outgoing(self, socket_addr, peer_id)
207            .map(|handler| Box::new(handler) as Box<dyn DynConnectionHandler>)
208    }
209}
210
211/// Wrapper trait for internal ease of use.
212pub(crate) trait DynConnectionHandler: Send + Sync + 'static {
213    fn protocol(&self) -> Protocol;
214
215    fn inbound_limits(&self) -> ProtocolIngressLimits;
216
217    fn on_unsupported_by_peer(
218        self: Box<Self>,
219        supported: &SharedCapabilities,
220        direction: Direction,
221        peer_id: PeerId,
222    ) -> OnNotSupported;
223
224    fn into_connection(
225        self: Box<Self>,
226        direction: Direction,
227        peer_id: PeerId,
228        conn: ProtocolConnection,
229    ) -> Pin<Box<dyn Stream<Item = BytesMut> + Send + 'static>>;
230}
231
232impl<T: ConnectionHandler> DynConnectionHandler for T {
233    fn protocol(&self) -> Protocol {
234        T::protocol(self)
235    }
236
237    fn inbound_limits(&self) -> ProtocolIngressLimits {
238        T::inbound_limits(self)
239    }
240
241    fn on_unsupported_by_peer(
242        self: Box<Self>,
243        supported: &SharedCapabilities,
244        direction: Direction,
245        peer_id: PeerId,
246    ) -> OnNotSupported {
247        T::on_unsupported_by_peer(*self, supported, direction, peer_id)
248    }
249
250    fn into_connection(
251        self: Box<Self>,
252        direction: Direction,
253        peer_id: PeerId,
254        conn: ProtocolConnection,
255    ) -> Pin<Box<dyn Stream<Item = BytesMut> + Send + 'static>> {
256        Box::pin(T::into_connection(*self, direction, peer_id, conn))
257    }
258}