Skip to main content

reth_eth_wire/
multiplex.rs

1//! Rlpx protocol multiplexer and satellite stream
2//!
3//! A Satellite is a Stream that primarily drives a single `RLPx` subprotocol but can also handle
4//! additional subprotocols.
5//!
6//! Most of other subprotocols are "dependent satellite" protocols of "eth" and not a fully standalone protocol, for example "snap", See also [snap protocol](https://github.com/ethereum/devp2p/blob/298d7a77c3bf833641579ecbbb5b13f0311eeeea/caps/snap.md?plain=1#L71)
7//! Hence it is expected that the primary protocol is "eth" and the additional protocols are
8//! "dependent satellite" protocols.
9
10use std::{
11    collections::VecDeque,
12    fmt,
13    future::Future,
14    io,
15    pin::{pin, Pin},
16    sync::{
17        atomic::{AtomicUsize, Ordering},
18        Arc,
19    },
20    task::{ready, Context, Poll},
21};
22
23use crate::{
24    capability::{SharedCapabilities, SharedCapability, UnsupportedCapabilityError},
25    errors::{EthStreamError, P2PStreamError},
26    handshake::EthRlpxHandshake,
27    p2pstream::DisconnectP2P,
28    protocol::ProtocolIngressLimits,
29    CanDisconnect, Capability, DisconnectReason, EthStream, P2PStream, UnifiedStatus,
30    HANDSHAKE_TIMEOUT,
31};
32use bytes::{Bytes, BytesMut};
33use futures::{Sink, SinkExt, Stream, StreamExt, TryStream, TryStreamExt};
34use reth_eth_wire_types::NetworkPrimitives;
35use reth_ethereum_forks::ForkFilter;
36use reth_metrics::metrics::counter;
37use tokio::sync::{mpsc, mpsc::UnboundedSender};
38use tokio_stream::wrappers::{ReceiverStream, UnboundedReceiverStream};
39
40/// A Stream and Sink type that wraps a raw rlpx stream [`P2PStream`] and handles message ID
41/// multiplexing.
42#[derive(Debug)]
43pub struct RlpxProtocolMultiplexer<St> {
44    inner: MultiplexInner<St>,
45}
46
47impl<St> RlpxProtocolMultiplexer<St> {
48    /// Wraps the raw p2p stream
49    pub fn new(conn: P2PStream<St>) -> Self {
50        Self {
51            inner: MultiplexInner {
52                conn,
53                protocols: Default::default(),
54                out_buffer: Default::default(),
55                inbound_budget: Arc::new(InboundBudget::new(MAX_MUX_IN_BUFFER_BYTES)),
56            },
57        }
58    }
59
60    /// Installs a new protocol on top of the raw p2p stream.
61    ///
62    /// This accepts a closure that receives a [`ProtocolConnection`] that will yield messages for
63    /// the given capability.
64    pub fn install_protocol<F, Proto>(
65        &mut self,
66        cap: &Capability,
67        f: F,
68    ) -> Result<(), UnsupportedCapabilityError>
69    where
70        F: FnOnce(ProtocolConnection) -> Proto,
71        Proto: Stream<Item = BytesMut> + Send + 'static,
72    {
73        self.inner.install_protocol(cap, ProtocolIngressLimits::default(), f)
74    }
75
76    /// Installs a new protocol with local limits for inbound messages.
77    pub fn install_protocol_with_limits<F, Proto>(
78        &mut self,
79        cap: &Capability,
80        limits: ProtocolIngressLimits,
81        f: F,
82    ) -> Result<(), UnsupportedCapabilityError>
83    where
84        F: FnOnce(ProtocolConnection) -> Proto,
85        Proto: Stream<Item = BytesMut> + Send + 'static,
86    {
87        self.inner.install_protocol(cap, limits, f)
88    }
89
90    /// Returns the [`SharedCapabilities`] of the underlying raw p2p stream
91    pub const fn shared_capabilities(&self) -> &SharedCapabilities {
92        self.inner.shared_capabilities()
93    }
94
95    /// Converts this multiplexer into a [`RlpxSatelliteStream`] with the given primary protocol.
96    pub fn into_satellite_stream<F, Primary>(
97        self,
98        cap: &Capability,
99        primary: F,
100    ) -> Result<RlpxSatelliteStream<St, Primary>, P2PStreamError>
101    where
102        F: FnOnce(ProtocolProxy) -> Primary,
103    {
104        let Ok(shared_cap) = self.shared_capabilities().ensure_matching_capability(cap).cloned()
105        else {
106            return Err(P2PStreamError::CapabilityNotShared)
107        };
108
109        let (to_primary, from_wire) =
110            self.inner.inbound_channel(&shared_cap, ProtocolIngressLimits::default());
111        let (to_wire, from_primary) = mpsc::unbounded_channel();
112        let proxy = ProtocolProxy { shared_cap: shared_cap.clone(), from_wire, to_wire };
113
114        let st = primary(proxy);
115        Ok(RlpxSatelliteStream {
116            inner: self.inner,
117            primary: PrimaryProtocol {
118                to_primary,
119                from_primary: UnboundedReceiverStream::new(from_primary),
120                st,
121                shared_cap,
122            },
123            next_outbound: 0,
124            producer_polls_since_inbound: 0,
125        })
126    }
127
128    /// Converts this multiplexer into a [`RlpxSatelliteStream`] with the given primary protocol.
129    ///
130    /// Returns an error if the primary protocol is not supported by the remote or the handshake
131    /// failed.
132    pub async fn into_satellite_stream_with_handshake<F, Fut, Err, Primary>(
133        self,
134        cap: &Capability,
135        handshake: F,
136    ) -> Result<RlpxSatelliteStream<St, Primary>, Err>
137    where
138        F: FnOnce(ProtocolProxy) -> Fut,
139        Fut: Future<Output = Result<Primary, Err>>,
140        St: Stream<Item = io::Result<BytesMut>> + Sink<Bytes, Error = io::Error> + Unpin,
141        P2PStreamError: Into<Err>,
142    {
143        self.into_satellite_stream_with_tuple_handshake(cap, async move |proxy| {
144            let st = handshake(proxy).await?;
145            Ok((st, ()))
146        })
147        .await
148        .map(|(st, _)| st)
149    }
150
151    /// Converts this multiplexer into a [`RlpxSatelliteStream`] with the given primary protocol.
152    ///
153    /// Returns an error if the primary protocol is not supported by the remote or the handshake
154    /// failed.
155    ///
156    /// This accepts a closure that does a handshake with the remote peer and returns a tuple of the
157    /// primary stream and extra data.
158    ///
159    /// See also [`UnauthedEthStream::handshake`](crate::UnauthedEthStream)
160    pub async fn into_satellite_stream_with_tuple_handshake<F, Fut, Err, Primary, Extra>(
161        mut self,
162        cap: &Capability,
163        handshake: F,
164    ) -> Result<(RlpxSatelliteStream<St, Primary>, Extra), Err>
165    where
166        F: FnOnce(ProtocolProxy) -> Fut,
167        Fut: Future<Output = Result<(Primary, Extra), Err>>,
168        St: Stream<Item = io::Result<BytesMut>> + Sink<Bytes, Error = io::Error> + Unpin,
169        P2PStreamError: Into<Err>,
170    {
171        let Ok(shared_cap) = self.shared_capabilities().ensure_matching_capability(cap).cloned()
172        else {
173            return Err(P2PStreamError::CapabilityNotShared.into())
174        };
175
176        let (to_primary, from_wire) =
177            self.inner.inbound_channel(&shared_cap, ProtocolIngressLimits::default());
178        let (to_wire, mut from_primary) = mpsc::unbounded_channel();
179        let proxy = ProtocolProxy { shared_cap: shared_cap.clone(), from_wire, to_wire };
180
181        let f = handshake(proxy);
182        let mut f = pin!(f);
183        let mut inbound_messages = 0;
184        let mut handshake_satellite_bytes = 0usize;
185        let mut handshake_satellite_messages = 0usize;
186
187        // this polls the connection and the primary stream concurrently until the handshake is
188        // complete
189        loop {
190            tokio::select! {
191                biased;
192                res = &mut f => {
193                    let (st, extra) = res?;
194                    return Ok((
195                        RlpxSatelliteStream {
196                            inner: self.inner,
197                            primary: PrimaryProtocol {
198                                to_primary,
199                                from_primary: UnboundedReceiverStream::new(from_primary),
200                                st,
201                                shared_cap,
202                            },
203                            next_outbound: 0,
204                            producer_polls_since_inbound: 0,
205                        },
206                        extra,
207                    ))
208                }
209                Some(msg) = from_primary.recv() => {
210                    self.inner.conn.send(msg).await.map_err(Into::into)?;
211                }
212                // Polling a satellite drives its inbound consumer even when it has no outbound
213                // message. This must happen before reading another frame from the socket.
214                msg = ProtocolsPoller::new(&mut self.inner.protocols) => {
215                     self.inner.conn.send(msg.map_err(Into::into)?).await.map_err(Into::into)?;
216                }
217                incoming = self.inner.conn.next() => {
218                    let msg = match incoming {
219                        Some(Ok(msg)) => msg,
220                        Some(Err(err)) => return Err(err.into()),
221                        None => {
222                            return Err(P2PStreamError::Io(io::ErrorKind::UnexpectedEof.into()).into())
223                        }
224                    };
225                    // Ensure the message belongs to the primary protocol
226                    let Some(offset) = msg.first().copied()
227                    else {
228                        return Err(P2PStreamError::EmptyProtocolMessage.into())
229                    };
230                    if let Some(cap) = self.shared_capabilities().find_by_relative_offset(offset).cloned() {
231                            if cap == shared_cap {
232                                // delegate to primary
233                                to_primary.try_send(msg).map_err(Into::into)?;
234                            } else {
235                                // Satellite consumers may wait for authentication, so keep their
236                                // aggregate pre-handshake retention much smaller than active queues.
237                                handshake_satellite_bytes = handshake_satellite_bytes
238                                    .saturating_add(msg.capacity().max(msg.len()));
239                                handshake_satellite_messages += 1;
240                                if handshake_satellite_bytes > MAX_HANDSHAKE_SATELLITE_BYTES ||
241                                    handshake_satellite_messages > MAX_HANDSHAKE_SATELLITE_MESSAGES
242                                {
243                                    counter!("p2pstream.subprotocol_inbound_buffer_full").increment(1);
244                                    return Err(P2PStreamError::SubprotocolInboundBufferFull {
245                                        capability: cap.capability().into_owned(),
246                                    }.into())
247                                }
248                                self.inner.delegate_message(&cap, msg).map_err(Into::into)?;
249                            }
250                        } else {
251                           return Err(P2PStreamError::UnknownReservedMessageId(offset).into())
252                        }
253
254                    inbound_messages += 1;
255                    if inbound_messages == MAX_INBOUND_MESSAGES_PER_POLL {
256                        inbound_messages = 0;
257                        yield_once().await;
258                    }
259                }
260            }
261        }
262    }
263
264    /// Converts this multiplexer into a [`RlpxSatelliteStream`] with eth protocol as the given
265    /// primary protocol and the handshake implementation.
266    pub async fn into_eth_satellite_stream<N: NetworkPrimitives>(
267        self,
268        status: UnifiedStatus,
269        fork_filter: ForkFilter,
270        handshake: Arc<dyn EthRlpxHandshake>,
271        eth_max_message_size: usize,
272    ) -> Result<(RlpxSatelliteStream<St, EthStream<ProtocolProxy, N>>, UnifiedStatus), EthStreamError>
273    where
274        St: Stream<Item = io::Result<BytesMut>> + Sink<Bytes, Error = io::Error> + Unpin,
275    {
276        let eth_cap = self.inner.conn.shared_capabilities().eth_version()?;
277        self.into_satellite_stream_with_tuple_handshake(
278            &Capability::eth(eth_cap),
279            async move |proxy| {
280                let handshake = handshake.clone();
281                let mut unauth = UnauthProxy { inner: proxy };
282                let their_status = handshake
283                    .handshake(&mut unauth, status, fork_filter, HANDSHAKE_TIMEOUT)
284                    .await?;
285                let eth_stream = EthStream::with_max_message_size(
286                    eth_cap,
287                    unauth.into_inner(),
288                    eth_max_message_size,
289                );
290                Ok((eth_stream, their_status))
291            },
292        )
293        .await
294    }
295}
296
297#[derive(Debug)]
298struct MultiplexInner<St> {
299    /// The raw p2p stream
300    conn: P2PStream<St>,
301    /// All the subprotocols that are multiplexed on top of the raw p2p stream
302    protocols: VecDeque<ProtocolStream>,
303    /// Buffer for outgoing messages on the wire.
304    out_buffer: OutBuffer,
305    /// Byte budget shared by every inbound protocol queue on this connection.
306    inbound_budget: Arc<InboundBudget>,
307}
308
309impl<St> MultiplexInner<St> {
310    const fn shared_capabilities(&self) -> &SharedCapabilities {
311        self.conn.shared_capabilities()
312    }
313
314    /// Delegates a message to the matching protocol.
315    fn delegate_message(
316        &self,
317        cap: &SharedCapability,
318        msg: BytesMut,
319    ) -> Result<bool, P2PStreamError> {
320        for proto in &self.protocols {
321            if proto.shared_cap == *cap {
322                proto.send_raw(msg)?;
323                return Ok(true)
324            }
325        }
326        Ok(false)
327    }
328
329    fn install_protocol<F, Proto>(
330        &mut self,
331        cap: &Capability,
332        limits: ProtocolIngressLimits,
333        f: F,
334    ) -> Result<(), UnsupportedCapabilityError>
335    where
336        F: FnOnce(ProtocolConnection) -> Proto,
337        Proto: Stream<Item = BytesMut> + Send + 'static,
338    {
339        let shared_cap =
340            self.conn.shared_capabilities().ensure_matching_capability(cap).cloned()?;
341        self.conn.set_protocol_ingress_limits(&shared_cap, limits);
342        let (to_satellite, from_wire) = self.inbound_channel(&shared_cap, limits);
343        let proto_conn = ProtocolConnection { from_wire };
344        let st = f(proto_conn);
345        let st = ProtocolStream { shared_cap, to_satellite, satellite_st: Box::pin(st) };
346        self.protocols.push_back(st);
347        Ok(())
348    }
349
350    fn inbound_channel(
351        &self,
352        capability: &SharedCapability,
353        limits: ProtocolIngressLimits,
354    ) -> (InboundSender, InboundReceiver) {
355        inbound_channel(capability, limits, Arc::clone(&self.inbound_budget))
356    }
357}
358
359/// Represents a protocol in the multiplexer that is used as the primary protocol.
360#[derive(Debug)]
361struct PrimaryProtocol<Primary> {
362    /// Channel to send messages to the primary protocol.
363    to_primary: InboundSender,
364    /// Receiver for messages from the primary protocol.
365    from_primary: UnboundedReceiverStream<Bytes>,
366    /// Shared capability of the primary protocol.
367    shared_cap: SharedCapability,
368    /// The primary stream.
369    st: Primary,
370}
371
372/// A Stream and Sink type that acts as a wrapper around a primary `RLPx` subprotocol (e.g. "eth")
373///
374/// Only emits and sends _non-empty_ messages
375#[derive(Debug)]
376pub struct ProtocolProxy {
377    shared_cap: SharedCapability,
378    /// Receives _non-empty_ messages from the wire
379    from_wire: InboundReceiver,
380    /// Sends _non-empty_ messages from the wire
381    to_wire: UnboundedSender<Bytes>,
382}
383
384impl ProtocolProxy {
385    /// Sends a _non-empty_ message on the wire.
386    fn try_send(&self, msg: Bytes) -> Result<(), io::Error> {
387        if msg.is_empty() {
388            // message must not be empty
389            return Err(io::ErrorKind::InvalidInput.into())
390        }
391        self.to_wire.send(self.mask_msg_id(msg)?).map_err(|_| io::ErrorKind::BrokenPipe.into())
392    }
393
394    /// Masks the message ID of a message to be sent on the wire.
395    #[inline]
396    fn mask_msg_id(&self, msg: Bytes) -> Result<Bytes, io::Error> {
397        if msg.is_empty() {
398            // message must not be empty
399            return Err(io::ErrorKind::InvalidInput.into())
400        }
401
402        let offset = self.shared_cap.relative_message_id_offset();
403        if offset == 0 {
404            return Ok(msg);
405        }
406
407        let mut masked: BytesMut = msg.into();
408        masked[0] = masked[0].checked_add(offset).ok_or(io::ErrorKind::InvalidInput)?;
409        Ok(masked.freeze())
410    }
411
412    /// Unmasks the message ID of a message received from the wire.
413    #[inline]
414    fn unmask_id(&self, mut msg: BytesMut) -> Result<BytesMut, io::Error> {
415        if msg.is_empty() {
416            // message must not be empty
417            return Err(io::ErrorKind::InvalidInput.into())
418        }
419        msg[0] = msg[0]
420            .checked_sub(self.shared_cap.relative_message_id_offset())
421            .ok_or(io::ErrorKind::InvalidInput)?;
422        Ok(msg)
423    }
424}
425
426impl Stream for ProtocolProxy {
427    type Item = Result<BytesMut, io::Error>;
428
429    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
430        let msg = ready!(self.from_wire.poll_next_unpin(cx));
431        Poll::Ready(msg.map(|msg| self.get_mut().unmask_id(msg)))
432    }
433}
434
435impl Sink<Bytes> for ProtocolProxy {
436    type Error = io::Error;
437
438    fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
439        Poll::Ready(Ok(()))
440    }
441
442    fn start_send(self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> {
443        self.get_mut().try_send(item)
444    }
445
446    fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
447        Poll::Ready(Ok(()))
448    }
449
450    fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
451        Poll::Ready(Ok(()))
452    }
453}
454
455impl CanDisconnect<Bytes> for ProtocolProxy {
456    fn disconnect(
457        &mut self,
458        _reason: DisconnectReason,
459    ) -> Pin<Box<dyn Future<Output = Result<(), <Self as Sink<Bytes>>::Error>> + Send + '_>> {
460        Box::pin(async move { Ok(()) })
461    }
462}
463
464/// Adapter so the injected `EthRlpxHandshake` can run over a multiplexed `ProtocolProxy`
465/// using the same error type expectations (`P2PStreamError`).
466#[derive(Debug)]
467struct UnauthProxy {
468    inner: ProtocolProxy,
469}
470
471impl UnauthProxy {
472    fn into_inner(self) -> ProtocolProxy {
473        self.inner
474    }
475}
476
477impl Stream for UnauthProxy {
478    type Item = Result<BytesMut, P2PStreamError>;
479
480    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
481        self.inner.poll_next_unpin(cx).map(|opt| opt.map(|res| res.map_err(P2PStreamError::from)))
482    }
483}
484
485impl Sink<Bytes> for UnauthProxy {
486    type Error = P2PStreamError;
487
488    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
489        self.inner.poll_ready_unpin(cx).map_err(P2PStreamError::from)
490    }
491
492    fn start_send(mut self: Pin<&mut Self>, item: Bytes) -> Result<(), Self::Error> {
493        self.inner.start_send_unpin(item).map_err(P2PStreamError::from)
494    }
495
496    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
497        self.inner.poll_flush_unpin(cx).map_err(P2PStreamError::from)
498    }
499
500    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
501        self.inner.poll_close_unpin(cx).map_err(P2PStreamError::from)
502    }
503}
504
505impl CanDisconnect<Bytes> for UnauthProxy {
506    fn disconnect(
507        &mut self,
508        reason: DisconnectReason,
509    ) -> Pin<Box<dyn Future<Output = Result<(), <Self as Sink<Bytes>>::Error>> + Send + '_>> {
510        let fut = self.inner.disconnect(reason);
511        Box::pin(async move { fut.await.map_err(P2PStreamError::from) })
512    }
513}
514
515#[derive(Debug)]
516struct InboundSender {
517    inner: mpsc::Sender<BudgetedInbound>,
518    connection_budget: Arc<InboundBudget>,
519    protocol_budget: Arc<InboundBudget>,
520    capability: Capability,
521}
522
523impl InboundSender {
524    fn try_send(&self, frame: BytesMut) -> Result<(), P2PStreamError> {
525        let size = frame.capacity().max(frame.len());
526        let Some(connection_guard) = InboundBudget::try_reserve(&self.connection_budget, size)
527        else {
528            return Err(self.buffer_full())
529        };
530        let Some(protocol_guard) = InboundBudget::try_reserve(&self.protocol_budget, size) else {
531            return Err(self.buffer_full())
532        };
533
534        let frame = BudgetedInbound {
535            frame,
536            _connection_guard: connection_guard,
537            _protocol_guard: protocol_guard,
538        };
539        self.inner.try_send(frame).map_err(|err| match err {
540            mpsc::error::TrySendError::Full(_) => self.buffer_full(),
541            mpsc::error::TrySendError::Closed(_) => {
542                P2PStreamError::Io(io::ErrorKind::BrokenPipe.into())
543            }
544        })
545    }
546
547    fn buffer_full(&self) -> P2PStreamError {
548        counter!("p2pstream.subprotocol_inbound_buffer_full").increment(1);
549        P2PStreamError::SubprotocolInboundBufferFull { capability: self.capability.clone() }
550    }
551}
552
553#[derive(Debug)]
554struct InboundReceiver {
555    inner: ReceiverStream<BudgetedInbound>,
556}
557
558impl Stream for InboundReceiver {
559    type Item = BytesMut;
560
561    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
562        self.inner.poll_next_unpin(cx).map(|frame| frame.map(BudgetedInbound::into_frame))
563    }
564}
565
566#[derive(Debug)]
567struct BudgetedInbound {
568    frame: BytesMut,
569    _connection_guard: InboundBudgetGuard,
570    _protocol_guard: InboundBudgetGuard,
571}
572
573impl BudgetedInbound {
574    fn into_frame(self) -> BytesMut {
575        let Self { frame, _connection_guard, _protocol_guard } = self;
576        frame
577    }
578}
579
580#[derive(Debug)]
581struct InboundBudget {
582    used: AtomicUsize,
583    max: usize,
584}
585
586impl InboundBudget {
587    const fn new(max: usize) -> Self {
588        Self { used: AtomicUsize::new(0), max }
589    }
590
591    fn try_reserve(budget: &Arc<Self>, size: usize) -> Option<InboundBudgetGuard> {
592        budget
593            .used
594            .try_update(Ordering::Relaxed, Ordering::Relaxed, |used| {
595                used.checked_add(size).filter(|next| *next <= budget.max)
596            })
597            .ok()
598            .map(|_| InboundBudgetGuard { size, budget: Arc::clone(budget) })
599    }
600}
601
602#[derive(Debug)]
603struct InboundBudgetGuard {
604    size: usize,
605    budget: Arc<InboundBudget>,
606}
607
608impl Drop for InboundBudgetGuard {
609    fn drop(&mut self) {
610        self.budget.used.fetch_sub(self.size, Ordering::Relaxed);
611    }
612}
613
614fn inbound_channel(
615    capability: &SharedCapability,
616    limits: ProtocolIngressLimits,
617    connection_budget: Arc<InboundBudget>,
618) -> (InboundSender, InboundReceiver) {
619    let (tx, rx) = mpsc::channel(limits.max_buffered_messages());
620    let sender = InboundSender {
621        inner: tx,
622        connection_budget,
623        protocol_budget: Arc::new(InboundBudget::new(limits.max_buffered_bytes())),
624        capability: capability.capability().into_owned(),
625    };
626    let receiver = InboundReceiver { inner: ReceiverStream::new(rx) };
627    (sender, receiver)
628}
629
630/// A connection channel to receive _`non_empty`_ messages for the negotiated protocol.
631///
632/// This is a [Stream] that returns raw bytes of the received messages for this protocol.
633#[derive(Debug)]
634pub struct ProtocolConnection {
635    from_wire: InboundReceiver,
636}
637
638impl Stream for ProtocolConnection {
639    type Item = BytesMut;
640
641    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
642        self.from_wire.poll_next_unpin(cx)
643    }
644}
645
646/// A Stream and Sink type that acts as a wrapper around a primary `RLPx` subprotocol (e.g. "eth")
647/// [`EthStream`] and can also handle additional subprotocols.
648#[derive(Debug)]
649pub struct RlpxSatelliteStream<St, Primary> {
650    inner: MultiplexInner<St>,
651    primary: PrimaryProtocol<Primary>,
652    /// Round-robin cursor for the next outbound producer to poll.
653    next_outbound: usize,
654    /// Number of round-robin producers polled since the last inbound frame.
655    producer_polls_since_inbound: usize,
656}
657
658impl<St, Primary> RlpxSatelliteStream<St, Primary> {
659    /// Installs a new protocol on top of the raw p2p stream.
660    ///
661    /// This accepts a closure that receives a [`ProtocolConnection`] that will yield messages for
662    /// the given capability.
663    pub fn install_protocol<F, Proto>(
664        &mut self,
665        cap: &Capability,
666        f: F,
667    ) -> Result<(), UnsupportedCapabilityError>
668    where
669        F: FnOnce(ProtocolConnection) -> Proto,
670        Proto: Stream<Item = BytesMut> + Send + 'static,
671    {
672        let result = self.inner.install_protocol(cap, ProtocolIngressLimits::default(), f);
673        if result.is_ok() {
674            self.producer_polls_since_inbound = 0;
675        }
676        result
677    }
678
679    /// Installs a new protocol with local limits for inbound messages.
680    pub fn install_protocol_with_limits<F, Proto>(
681        &mut self,
682        cap: &Capability,
683        limits: ProtocolIngressLimits,
684        f: F,
685    ) -> Result<(), UnsupportedCapabilityError>
686    where
687        F: FnOnce(ProtocolConnection) -> Proto,
688        Proto: Stream<Item = BytesMut> + Send + 'static,
689    {
690        let result = self.inner.install_protocol(cap, limits, f);
691        if result.is_ok() {
692            self.producer_polls_since_inbound = 0;
693        }
694        result
695    }
696
697    /// Returns the primary protocol.
698    #[inline]
699    pub const fn primary(&self) -> &Primary {
700        &self.primary.st
701    }
702
703    /// Returns mutable access to the primary protocol.
704    #[inline]
705    pub const fn primary_mut(&mut self) -> &mut Primary {
706        &mut self.primary.st
707    }
708
709    /// Returns the underlying [`P2PStream`].
710    #[inline]
711    pub const fn inner(&self) -> &P2PStream<St> {
712        &self.inner.conn
713    }
714
715    /// Returns mutable access to the underlying [`P2PStream`].
716    #[inline]
717    pub const fn inner_mut(&mut self) -> &mut P2PStream<St> {
718        &mut self.inner.conn
719    }
720
721    /// Consumes this type and returns the wrapped [`P2PStream`].
722    #[inline]
723    pub fn into_inner(self) -> P2PStream<St> {
724        self.inner.conn
725    }
726
727    /// Polls primary and satellite outbound producers round-robin until the `OutBuffer` is full or
728    /// every producer is pending.
729    ///
730    /// The cursor advances after each producer poll, so a ready producer cannot drain repeatedly
731    /// before later producers get a turn.
732    fn poll_outbound_producers(&mut self, cx: &mut Context<'_>) -> Result<ProducerPoll, io::Error> {
733        let producers = self.inner.protocols.len() + 1;
734        let mut pending = 0;
735
736        while pending < producers {
737            if self.inner.out_buffer.is_full() {
738                return Ok(ProducerPoll::Full)
739            }
740
741            if self.next_outbound >= producers {
742                self.next_outbound = 0;
743            }
744
745            let producer = self.next_outbound;
746            self.next_outbound = (self.next_outbound + 1) % producers;
747            if self.producer_polls_since_inbound < producers {
748                self.producer_polls_since_inbound += 1;
749            }
750
751            let msg = if producer == 0 {
752                match self.primary.from_primary.poll_next_unpin(cx) {
753                    Poll::Ready(Some(msg)) => msg,
754                    Poll::Ready(None) => return Ok(ProducerPoll::Closed),
755                    Poll::Pending => {
756                        pending += 1;
757                        continue
758                    }
759                }
760            } else {
761                let proto = self
762                    .inner
763                    .protocols
764                    .get_mut(producer - 1)
765                    .expect("outbound producer index checked against protocol count");
766                match proto.poll_next_unpin(cx) {
767                    Poll::Ready(Some(msg)) => msg?,
768                    Poll::Ready(None) => return Ok(ProducerPoll::Closed),
769                    Poll::Pending => {
770                        pending += 1;
771                        continue
772                    }
773                }
774            };
775
776            pending = 0;
777            self.inner.out_buffer.push_back(msg);
778        }
779
780        Ok(ProducerPoll::Pending)
781    }
782}
783
784impl<St, Primary, PrimaryErr> Stream for RlpxSatelliteStream<St, Primary>
785where
786    St: Stream<Item = io::Result<BytesMut>> + Sink<Bytes, Error = io::Error> + Unpin,
787    Primary: TryStream<Error = PrimaryErr> + Unpin,
788    P2PStreamError: Into<PrimaryErr>,
789{
790    type Item = Result<Primary::Ok, Primary::Error>;
791
792    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
793        let this = self.get_mut();
794        let mut inbound_messages = 0;
795
796        loop {
797            // first drain the primary stream
798            if let Poll::Ready(Some(msg)) = this.primary.st.try_poll_next_unpin(cx) {
799                return Poll::Ready(Some(msg))
800            }
801
802            let mut conn_ready = true;
803            loop {
804                match this.inner.conn.poll_ready_unpin(cx) {
805                    Poll::Ready(Ok(())) => {
806                        if let Some(msg) = this.inner.out_buffer.pop_front() {
807                            if let Err(err) = this.inner.conn.start_send_unpin(msg) {
808                                return Poll::Ready(Some(Err(err.into())))
809                            }
810                        } else {
811                            break
812                        }
813                    }
814                    Poll::Ready(Err(err)) => {
815                        if let Err(disconnect_err) =
816                            this.inner.conn.start_disconnect(DisconnectReason::DisconnectRequested)
817                        {
818                            return Poll::Ready(Some(Err(disconnect_err.into())))
819                        }
820                        return Poll::Ready(Some(Err(err.into())))
821                    }
822                    Poll::Pending => {
823                        conn_ready = false;
824                        break
825                    }
826                }
827            }
828            // The connection only buffers frames on `start_send`; `poll_flush` performs the
829            // actual writes and flushes the transport once for the batch handed to it above.
830            // This also resumes a flush that returned pending on an earlier pass; a no-op if
831            // nothing is buffered.
832            match this.inner.conn.poll_flush_unpin(cx) {
833                Poll::Ready(Ok(())) => {}
834                Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
835                Poll::Pending => {
836                    conn_ready = false;
837                }
838            }
839
840            match this.poll_outbound_producers(cx) {
841                Ok(ProducerPoll::Full)
842                    if this.producer_polls_since_inbound < this.inner.protocols.len() + 1 =>
843                {
844                    // A full buffer prevents protocol streams from being polled. Pause ingress
845                    // until every consumer has had a chance to drain the previous frame.
846                    if conn_ready {
847                        cx.waker().wake_by_ref();
848                    }
849                    return Poll::Pending
850                }
851                Ok(ProducerPoll::Pending | ProducerPoll::Full) => {}
852                Ok(ProducerPoll::Closed) => return Poll::Ready(None),
853                Err(err) => return Poll::Ready(Some(Err(P2PStreamError::Io(err).into()))),
854            }
855
856            let mut delegated = false;
857            // Pull one message before returning to the top of the loop, where every protocol gets
858            // another chance to consume its inbound queue.
859            match this.inner.conn.poll_next_unpin(cx) {
860                Poll::Ready(Some(Ok(msg))) => {
861                    delegated = true;
862                    let Some(offset) = msg.first().copied() else {
863                        return Poll::Ready(Some(Err(P2PStreamError::EmptyProtocolMessage.into())))
864                    };
865                    let Some(cap) = this
866                        .inner
867                        .conn
868                        .shared_capabilities()
869                        .find_by_relative_offset(offset)
870                        .cloned()
871                    else {
872                        return Poll::Ready(Some(Err(P2PStreamError::UnknownReservedMessageId(
873                            offset,
874                        )
875                        .into())))
876                    };
877
878                    let result = if cap == this.primary.shared_cap {
879                        this.primary.to_primary.try_send(msg)
880                    } else {
881                        this.inner.delegate_message(&cap, msg).map(|_| ())
882                    };
883                    if let Err(err) = result {
884                        return Poll::Ready(Some(Err(err.into())))
885                    }
886                    this.producer_polls_since_inbound = 0;
887
888                    inbound_messages += 1;
889                    if inbound_messages == MAX_INBOUND_MESSAGES_PER_POLL {
890                        cx.waker().wake_by_ref();
891                        return Poll::Pending
892                    }
893                }
894                Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err.into()))),
895                Poll::Ready(None) => return Poll::Ready(None),
896                Poll::Pending => {}
897            }
898
899            if !conn_ready || (!delegated && this.inner.out_buffer.is_empty()) {
900                return Poll::Pending
901            }
902        }
903    }
904}
905
906impl<St, Primary, T> Sink<T> for RlpxSatelliteStream<St, Primary>
907where
908    St: Stream<Item = io::Result<BytesMut>> + Sink<Bytes, Error = io::Error> + Unpin,
909    Primary: Sink<T> + Unpin,
910    P2PStreamError: Into<<Primary as Sink<T>>::Error>,
911{
912    type Error = <Primary as Sink<T>>::Error;
913
914    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
915        let this = self.get_mut();
916        if let Err(err) = ready!(this.inner.conn.poll_ready_unpin(cx)) {
917            return Poll::Ready(Err(err.into()))
918        }
919        if let Err(err) = ready!(this.primary.st.poll_ready_unpin(cx)) {
920            return Poll::Ready(Err(err))
921        }
922        Poll::Ready(Ok(()))
923    }
924
925    fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {
926        self.get_mut().primary.st.start_send_unpin(item)
927    }
928
929    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
930        self.get_mut().inner.conn.poll_flush_unpin(cx).map_err(Into::into)
931    }
932
933    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
934        self.get_mut().inner.conn.poll_close_unpin(cx).map_err(Into::into)
935    }
936}
937
938/// Wraps a `RLPx` subprotocol and handles message ID multiplexing.
939struct ProtocolStream {
940    shared_cap: SharedCapability,
941    /// the channel shared with the satellite stream
942    to_satellite: InboundSender,
943    satellite_st: Pin<Box<dyn Stream<Item = BytesMut> + Send>>,
944}
945
946impl ProtocolStream {
947    /// Masks the message ID of a message to be sent on the wire.
948    #[inline]
949    fn mask_msg_id(&self, mut msg: BytesMut) -> Result<Bytes, io::Error> {
950        if msg.is_empty() {
951            // message must not be empty
952            return Err(io::ErrorKind::InvalidInput.into())
953        }
954        msg[0] = msg[0]
955            .checked_add(self.shared_cap.relative_message_id_offset())
956            .ok_or(io::ErrorKind::InvalidInput)?;
957        Ok(msg.freeze())
958    }
959
960    /// Unmasks the message ID of a message received from the wire.
961    #[inline]
962    fn unmask_id(&self, mut msg: BytesMut) -> Result<BytesMut, io::Error> {
963        if msg.is_empty() {
964            // message must not be empty
965            return Err(io::ErrorKind::InvalidInput.into())
966        }
967        msg[0] = msg[0]
968            .checked_sub(self.shared_cap.relative_message_id_offset())
969            .ok_or(io::ErrorKind::InvalidInput)?;
970        Ok(msg)
971    }
972
973    /// Sends the message to the satellite stream.
974    fn send_raw(&self, msg: BytesMut) -> Result<(), P2PStreamError> {
975        let msg = self.unmask_id(msg).map_err(P2PStreamError::from)?;
976        self.to_satellite.try_send(msg)
977    }
978}
979
980impl Stream for ProtocolStream {
981    type Item = Result<Bytes, io::Error>;
982
983    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
984        let this = self.get_mut();
985        let msg = ready!(this.satellite_st.as_mut().poll_next(cx));
986        Poll::Ready(msg.map(|msg| this.mask_msg_id(msg)))
987    }
988}
989
990impl fmt::Debug for ProtocolStream {
991    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992        f.debug_struct("ProtocolStream").field("cap", &self.shared_cap).finish_non_exhaustive()
993    }
994}
995
996/// Helper to poll multiple protocol streams in a `tokio::select`! branch
997struct ProtocolsPoller<'a> {
998    protocols: &'a mut VecDeque<ProtocolStream>,
999}
1000
1001impl<'a> ProtocolsPoller<'a> {
1002    const fn new(protocols: &'a mut VecDeque<ProtocolStream>) -> Self {
1003        Self { protocols }
1004    }
1005}
1006
1007impl<'a> Future for ProtocolsPoller<'a> {
1008    type Output = Result<Bytes, P2PStreamError>;
1009
1010    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1011        let protocols = self.protocols.len();
1012        for _ in 0..protocols {
1013            let mut proto = self.protocols.pop_front().expect("protocol count checked");
1014            match proto.poll_next_unpin(cx) {
1015                Poll::Ready(Some(Err(err))) => {
1016                    self.protocols.push_back(proto);
1017                    return Poll::Ready(Err(P2PStreamError::from(err)))
1018                }
1019                Poll::Ready(Some(Ok(msg))) => {
1020                    // Got a message, put protocol back and return the message
1021                    self.protocols.push_back(proto);
1022                    return Poll::Ready(Ok(msg));
1023                }
1024                _ => {
1025                    // push it back because we still want to complete the handshake first
1026                    self.protocols.push_back(proto);
1027                }
1028            }
1029        }
1030
1031        // All protocols processed, nothing ready
1032        Poll::Pending
1033    }
1034}
1035
1036fn yield_once() -> impl Future<Output = ()> {
1037    let mut yielded = false;
1038    std::future::poll_fn(move |cx| {
1039        if yielded {
1040            Poll::Ready(())
1041        } else {
1042            yielded = true;
1043            cx.waker().wake_by_ref();
1044            Poll::Pending
1045        }
1046    })
1047}
1048
1049/// Aggregate byte budget for inbound messages queued across all protocols on one connection.
1050const MAX_MUX_IN_BUFFER_BYTES: usize = 32 * 1024 * 1024;
1051
1052/// Aggregate satellite traffic accepted while the primary handshake is pending. These limits
1053/// bound both payload retention and queue metadata before application consumers may start.
1054const MAX_HANDSHAKE_SATELLITE_BYTES: usize = 256 * 1024;
1055const MAX_HANDSHAKE_SATELLITE_MESSAGES: usize = 64;
1056
1057/// Maximum network messages delegated before yielding the connection task.
1058const MAX_INBOUND_MESSAGES_PER_POLL: usize = 32;
1059
1060/// Soft cap for per-connection outbound `RLPx` messages waiting in the multiplexer.
1061///
1062/// The cap is soft because the next message size is only known after polling a protocol stream.
1063/// The buffer may exceed this by at most one message before producer polling is paused.
1064///
1065/// The lower [`P2PStream`] sink admits two outbound messages and rejects uncompressed payloads
1066/// above 16 MiB, so 32 MiB mirrors the largest payload volume the lower p2p layer is already
1067/// prepared to buffer.
1068const MAX_MUX_OUT_BUFFER_BYTES: usize = 32 * 1024 * 1024;
1069
1070#[derive(Debug)]
1071struct OutBuffer {
1072    messages: VecDeque<Bytes>,
1073    bytes: usize,
1074    max_bytes: usize,
1075}
1076
1077impl Default for OutBuffer {
1078    fn default() -> Self {
1079        Self { messages: Default::default(), bytes: 0, max_bytes: MAX_MUX_OUT_BUFFER_BYTES }
1080    }
1081}
1082
1083impl OutBuffer {
1084    fn push_back(&mut self, msg: Bytes) {
1085        self.bytes += msg.len();
1086        self.messages.push_back(msg);
1087    }
1088
1089    fn pop_front(&mut self) -> Option<Bytes> {
1090        let msg = self.messages.pop_front()?;
1091        self.bytes -= msg.len();
1092        Some(msg)
1093    }
1094
1095    fn is_empty(&self) -> bool {
1096        self.messages.is_empty()
1097    }
1098
1099    const fn is_full(&self) -> bool {
1100        self.bytes >= self.max_bytes
1101    }
1102}
1103
1104/// Result of polling outbound producers into the mux buffer.
1105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1106enum ProducerPoll {
1107    /// All outbound producers are pending.
1108    Pending,
1109    /// The mux buffer reached its soft cap.
1110    Full,
1111    /// An outbound producer closed.
1112    Closed,
1113}
1114
1115#[cfg(test)]
1116mod tests {
1117    use super::*;
1118    use crate::{
1119        handshake::EthHandshake,
1120        message::MAX_MESSAGE_SIZE,
1121        protocol::Protocol,
1122        test_utils::{
1123            connect_passthrough, eth_handshake, eth_hello,
1124            proto::{test_hello, TestProtoMessage},
1125        },
1126        UnauthedEthStream, UnauthedP2PStream,
1127    };
1128    use futures::{stream, task::noop_waker_ref};
1129    use reth_eth_wire_types::EthNetworkPrimitives;
1130    use std::{sync::atomic::AtomicBool, task::Poll};
1131    use tokio::{net::TcpListener, sync::oneshot};
1132    use tokio_util::codec::Decoder;
1133
1134    #[derive(Debug)]
1135    struct PendingPrimary {
1136        _proxy: ProtocolProxy,
1137    }
1138
1139    impl Stream for PendingPrimary {
1140        type Item = Result<(), P2PStreamError>;
1141
1142        fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1143            Poll::Pending
1144        }
1145    }
1146
1147    #[derive(Debug)]
1148    struct BurstingPrimary {
1149        proxy: ProtocolProxy,
1150        remaining: usize,
1151    }
1152
1153    impl Stream for BurstingPrimary {
1154        type Item = Result<(), P2PStreamError>;
1155
1156        fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1157            if self.remaining > 0 {
1158                self.remaining -= 1;
1159                self.proxy.try_send(Bytes::from_static(&[0, 0])).unwrap();
1160            }
1161            Poll::Pending
1162        }
1163    }
1164
1165    #[derive(Debug)]
1166    struct StalledTransport;
1167
1168    impl Stream for StalledTransport {
1169        type Item = io::Result<BytesMut>;
1170
1171        fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1172            Poll::Pending
1173        }
1174    }
1175
1176    impl Sink<Bytes> for StalledTransport {
1177        type Error = io::Error;
1178
1179        fn poll_ready(
1180            self: Pin<&mut Self>,
1181            _cx: &mut Context<'_>,
1182        ) -> Poll<Result<(), Self::Error>> {
1183            Poll::Pending
1184        }
1185
1186        fn start_send(self: Pin<&mut Self>, _item: Bytes) -> Result<(), Self::Error> {
1187            Ok(())
1188        }
1189
1190        fn poll_flush(
1191            self: Pin<&mut Self>,
1192            _cx: &mut Context<'_>,
1193        ) -> Poll<Result<(), Self::Error>> {
1194            Poll::Pending
1195        }
1196
1197        fn poll_close(
1198            self: Pin<&mut Self>,
1199            _cx: &mut Context<'_>,
1200        ) -> Poll<Result<(), Self::Error>> {
1201            Poll::Pending
1202        }
1203    }
1204
1205    #[derive(Debug)]
1206    struct InboundFramesTransport {
1207        frames: VecDeque<BytesMut>,
1208        writable: AtomicBool,
1209    }
1210
1211    impl Stream for InboundFramesTransport {
1212        type Item = io::Result<BytesMut>;
1213
1214        fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1215            Poll::Ready(self.frames.pop_front().map(Ok))
1216        }
1217    }
1218
1219    impl Sink<Bytes> for InboundFramesTransport {
1220        type Error = io::Error;
1221
1222        fn poll_ready(
1223            self: Pin<&mut Self>,
1224            _cx: &mut Context<'_>,
1225        ) -> Poll<Result<(), Self::Error>> {
1226            if self.writable.load(Ordering::Relaxed) {
1227                Poll::Ready(Ok(()))
1228            } else {
1229                Poll::Pending
1230            }
1231        }
1232
1233        fn start_send(self: Pin<&mut Self>, _item: Bytes) -> Result<(), Self::Error> {
1234            Ok(())
1235        }
1236
1237        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1238            self.poll_ready(cx)
1239        }
1240
1241        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1242            self.poll_ready(cx)
1243        }
1244    }
1245
1246    #[derive(Debug)]
1247    struct HoldingProtocol {
1248        _conn: ProtocolConnection,
1249    }
1250
1251    impl Stream for HoldingProtocol {
1252        type Item = BytesMut;
1253
1254        fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1255            Poll::Pending
1256        }
1257    }
1258
1259    #[derive(Debug)]
1260    struct DrainingProtocol {
1261        conn: ProtocolConnection,
1262    }
1263
1264    impl Stream for DrainingProtocol {
1265        type Item = BytesMut;
1266
1267        fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1268            while matches!(self.conn.poll_next_unpin(cx), Poll::Ready(Some(_))) {}
1269            Poll::Pending
1270        }
1271    }
1272
1273    #[derive(Debug)]
1274    struct SignalingDrainingProtocol {
1275        conn: ProtocolConnection,
1276        drained: Option<oneshot::Sender<()>>,
1277    }
1278
1279    impl Stream for SignalingDrainingProtocol {
1280        type Item = BytesMut;
1281
1282        fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1283            if matches!(self.conn.poll_next_unpin(cx), Poll::Ready(Some(_))) &&
1284                let Some(drained) = self.drained.take()
1285            {
1286                let _ = drained.send(());
1287            }
1288            Poll::Pending
1289        }
1290    }
1291
1292    fn compressed_frame(message_id: u8, payload: &[u8]) -> BytesMut {
1293        let mut encoded = vec![0; snap::raw::max_compress_len(payload.len()) + 1];
1294        encoded[0] = message_id;
1295        let len = snap::raw::Encoder::new().compress(payload, &mut encoded[1..]).unwrap();
1296        encoded.truncate(len + 1);
1297        BytesMut::from(encoded.as_slice())
1298    }
1299
1300    fn test_multiplexer(
1301        frame_count: usize,
1302    ) -> (RlpxProtocolMultiplexer<InboundFramesTransport>, SharedCapability, SharedCapability) {
1303        let (hello, _) = test_hello();
1304        let shared_capabilities =
1305            SharedCapabilities::try_new(hello.protocols.clone(), hello.message().capabilities)
1306                .unwrap();
1307        let eth = shared_capabilities.eth().unwrap().clone();
1308        let test = shared_capabilities.find(&TestProtoMessage::capability()).unwrap().clone();
1309        let frames =
1310            (0..frame_count).map(|_| compressed_frame(test.message_id_offset(), &[0])).collect();
1311        let conn = P2PStream::new(
1312            InboundFramesTransport { frames, writable: AtomicBool::new(true) },
1313            shared_capabilities,
1314        );
1315        (RlpxProtocolMultiplexer::new(conn), eth, test)
1316    }
1317
1318    fn shared_test_capability(name: &'static str, offset: u8) -> SharedCapability {
1319        SharedCapability::UnknownCapability {
1320            cap: Capability::new_static(name, 1),
1321            offset,
1322            messages: 1,
1323        }
1324    }
1325
1326    #[tokio::test]
1327    async fn inbound_channel_enforces_and_releases_protocol_limits() {
1328        let capability = shared_test_capability("test", 0x10);
1329        let limits =
1330            ProtocolIngressLimits::new(16).with_max_buffered_bytes(4).with_max_buffered_messages(1);
1331        let aggregate = Arc::new(InboundBudget::new(16));
1332        let (sender, mut receiver) = inbound_channel(&capability, limits, aggregate);
1333
1334        sender.try_send(BytesMut::zeroed(4)).unwrap();
1335        assert!(matches!(
1336            sender.try_send(BytesMut::zeroed(1)),
1337            Err(P2PStreamError::SubprotocolInboundBufferFull { .. })
1338        ));
1339
1340        assert_eq!(receiver.next().await.unwrap().len(), 4);
1341        sender.try_send(BytesMut::zeroed(1)).unwrap();
1342    }
1343
1344    #[tokio::test]
1345    async fn inbound_channels_share_connection_byte_budget() {
1346        let aggregate = Arc::new(InboundBudget::new(6));
1347        let limits = ProtocolIngressLimits::new(16)
1348            .with_max_buffered_bytes(16)
1349            .with_max_buffered_messages(2);
1350        let (first, mut first_receiver) =
1351            inbound_channel(&shared_test_capability("aaa", 0x10), limits, Arc::clone(&aggregate));
1352        let (second, _second_receiver) =
1353            inbound_channel(&shared_test_capability("bbb", 0x11), limits, aggregate);
1354
1355        first.try_send(BytesMut::zeroed(4)).unwrap();
1356        assert!(matches!(
1357            second.try_send(BytesMut::zeroed(4)),
1358            Err(P2PStreamError::SubprotocolInboundBufferFull { .. })
1359        ));
1360
1361        let _ = first_receiver.next().await.unwrap();
1362        second.try_send(BytesMut::zeroed(4)).unwrap();
1363    }
1364
1365    #[tokio::test]
1366    async fn stalled_satellite_fails_when_its_inbound_queue_is_full() {
1367        let (mut mux, eth, test) = test_multiplexer(2);
1368        let limits =
1369            ProtocolIngressLimits::new(2).with_max_buffered_bytes(2).with_max_buffered_messages(1);
1370        mux.install_protocol_with_limits(&TestProtoMessage::capability(), limits, |conn| {
1371            HoldingProtocol { _conn: conn }
1372        })
1373        .unwrap();
1374        let mut stream = mux
1375            .into_satellite_stream(eth.capability().as_ref(), |proxy| PendingPrimary {
1376                _proxy: proxy,
1377            })
1378            .unwrap();
1379
1380        let mut cx = Context::from_waker(noop_waker_ref());
1381        assert!(matches!(
1382            Pin::new(&mut stream).poll_next(&mut cx),
1383            Poll::Ready(Some(Err(P2PStreamError::SubprotocolInboundBufferFull {
1384                capability,
1385            }))) if capability == test.capability().into_owned()
1386        ));
1387    }
1388
1389    #[tokio::test]
1390    async fn satellite_is_polled_between_inbound_frames() {
1391        let (mut mux, eth, _) = test_multiplexer(2 * MAX_INBOUND_MESSAGES_PER_POLL);
1392        let limits =
1393            ProtocolIngressLimits::new(2).with_max_buffered_bytes(2).with_max_buffered_messages(1);
1394        mux.install_protocol_with_limits(&TestProtoMessage::capability(), limits, |conn| {
1395            DrainingProtocol { conn }
1396        })
1397        .unwrap();
1398        let mut stream = mux
1399            .into_satellite_stream(eth.capability().as_ref(), |proxy| PendingPrimary {
1400                _proxy: proxy,
1401            })
1402            .unwrap();
1403
1404        let mut cx = Context::from_waker(noop_waker_ref());
1405        assert!(Pin::new(&mut stream).poll_next(&mut cx).is_pending());
1406        assert_eq!(stream.inner.conn.inner().frames.len(), MAX_INBOUND_MESSAGES_PER_POLL);
1407    }
1408
1409    #[tokio::test]
1410    async fn outbound_backpressure_preserves_inbound_fairness() {
1411        let (hello, _) = test_hello();
1412        let shared_capabilities =
1413            SharedCapabilities::try_new(hello.protocols.clone(), hello.message().capabilities)
1414                .unwrap();
1415        let eth = shared_capabilities.eth().unwrap().clone();
1416        let test = shared_capabilities.find(&TestProtoMessage::capability()).unwrap().clone();
1417        let frames = (0..2)
1418            .map(|_| compressed_frame(test.message_id_offset(), &[0]))
1419            .collect::<VecDeque<_>>();
1420        let conn = P2PStream::new(
1421            InboundFramesTransport { frames, writable: AtomicBool::new(false) },
1422            shared_capabilities,
1423        );
1424        let mut mux = RlpxProtocolMultiplexer::new(conn);
1425        let limits =
1426            ProtocolIngressLimits::new(2).with_max_buffered_bytes(2).with_max_buffered_messages(1);
1427        mux.install_protocol_with_limits(&TestProtoMessage::capability(), limits, |conn| {
1428            DrainingProtocol { conn }
1429        })
1430        .unwrap();
1431        let mut stream = mux
1432            .into_satellite_stream(eth.capability().as_ref(), |proxy| BurstingPrimary {
1433                proxy,
1434                remaining: 3,
1435            })
1436            .unwrap();
1437        stream.inner.conn.start_send_unpin(Bytes::from_static(&[0, 0])).unwrap();
1438        stream.inner.conn.start_send_unpin(Bytes::from_static(&[0, 0])).unwrap();
1439        stream.inner.out_buffer.max_bytes = 1;
1440        stream.inner.out_buffer.push_back(Bytes::from_static(&[0, 0]));
1441
1442        let mut cx = Context::from_waker(noop_waker_ref());
1443        assert!(Pin::new(&mut stream).poll_next(&mut cx).is_pending());
1444        assert_eq!(stream.inner.conn.inner().frames.len(), 2);
1445
1446        assert!(Pin::new(&mut stream).poll_next(&mut cx).is_pending());
1447        assert_eq!(stream.inner.conn.inner().frames.len(), 2);
1448
1449        stream.inner.conn.inner().writable.store(true, Ordering::Relaxed);
1450        assert!(Pin::new(&mut stream).poll_next(&mut cx).is_pending());
1451        assert_eq!(stream.inner.conn.inner().frames.len(), 2);
1452
1453        assert!(matches!(Pin::new(&mut stream).poll_next(&mut cx), Poll::Ready(None)));
1454        assert_eq!(stream.inner.conn.inner().frames.len(), 0);
1455    }
1456
1457    #[tokio::test]
1458    async fn satellite_is_polled_between_frames_during_primary_handshake() {
1459        let (mut mux, eth, _) = test_multiplexer(2);
1460        let limits =
1461            ProtocolIngressLimits::new(2).with_max_buffered_bytes(2).with_max_buffered_messages(1);
1462        let (drained_tx, drained_rx) = oneshot::channel();
1463        mux.install_protocol_with_limits(&TestProtoMessage::capability(), limits, |conn| {
1464            SignalingDrainingProtocol { conn, drained: Some(drained_tx) }
1465        })
1466        .unwrap();
1467
1468        let stream = mux
1469            .into_satellite_stream_with_handshake(eth.capability().as_ref(), async move |proxy| {
1470                drained_rx.await.unwrap();
1471                Ok::<_, P2PStreamError>(PendingPrimary { _proxy: proxy })
1472            })
1473            .await;
1474
1475        assert!(stream.is_ok());
1476    }
1477
1478    #[tokio::test]
1479    async fn satellite_mux_stops_polling_protocols_when_out_buffer_is_full() {
1480        let (hello, _) = test_hello();
1481        let shared_capabilities =
1482            SharedCapabilities::try_new(hello.protocols.clone(), hello.message().capabilities)
1483                .unwrap();
1484        let conn = P2PStream::new(StalledTransport, shared_capabilities);
1485        let eth = conn.shared_capabilities().eth().unwrap().clone();
1486
1487        let mut st = RlpxProtocolMultiplexer::new(conn)
1488            .into_satellite_stream(eth.capability().as_ref(), |proxy| PendingPrimary {
1489                _proxy: proxy,
1490            })
1491            .unwrap();
1492        const MESSAGE_COUNT: usize = 4096;
1493        const MESSAGE_BYTES: usize = 1024;
1494        st.inner.out_buffer.max_bytes = 4 * MESSAGE_BYTES + 1;
1495        st.install_protocol(&TestProtoMessage::capability(), |_conn| {
1496            stream::iter((0..MESSAGE_COUNT).map(|_| {
1497                let mut msg = BytesMut::zeroed(MESSAGE_BYTES);
1498                msg[0] = TestProtoMessage::ping().message_type as u8;
1499                msg
1500            }))
1501        })
1502        .unwrap();
1503
1504        let mut cx = Context::from_waker(noop_waker_ref());
1505        assert!(Pin::new(&mut st).poll_next(&mut cx).is_pending());
1506
1507        assert!(st.inner.out_buffer.bytes > st.inner.out_buffer.max_bytes);
1508        assert!(st.inner.out_buffer.bytes <= st.inner.out_buffer.max_bytes + MESSAGE_BYTES);
1509        assert!(st.inner.out_buffer.messages.len() < MESSAGE_COUNT);
1510    }
1511
1512    #[tokio::test]
1513    async fn satellite_mux_round_robins_ready_protocols_when_out_buffer_fills() {
1514        let (mut hello, _) = eth_hello();
1515        let cap_a = Capability::new_static("aaa", 1);
1516        let cap_b = Capability::new_static("bbb", 1);
1517        hello.protocols.push(Protocol::new(cap_a.clone(), 1));
1518        hello.protocols.push(Protocol::new(cap_b.clone(), 1));
1519
1520        let shared_capabilities =
1521            SharedCapabilities::try_new(hello.protocols.clone(), hello.message().capabilities)
1522                .unwrap();
1523        let conn = P2PStream::new(StalledTransport, shared_capabilities);
1524        let eth = conn.shared_capabilities().eth().unwrap().clone();
1525        let cap_a_offset =
1526            conn.shared_capabilities().find(&cap_a).unwrap().relative_message_id_offset();
1527        let cap_b_offset =
1528            conn.shared_capabilities().find(&cap_b).unwrap().relative_message_id_offset();
1529
1530        let mut st = RlpxProtocolMultiplexer::new(conn)
1531            .into_satellite_stream(eth.capability().as_ref(), |proxy| PendingPrimary {
1532                _proxy: proxy,
1533            })
1534            .unwrap();
1535        st.inner.out_buffer.max_bytes = 5;
1536        st.install_protocol(&cap_a, |_conn| {
1537            stream::iter((0..16).map(|_| BytesMut::from(&[0, b'a'][..])))
1538        })
1539        .unwrap();
1540        st.install_protocol(&cap_b, |_conn| {
1541            stream::iter((0..16).map(|_| BytesMut::from(&[0, b'b'][..])))
1542        })
1543        .unwrap();
1544
1545        let mut cx = Context::from_waker(noop_waker_ref());
1546        assert!(Pin::new(&mut st).poll_next(&mut cx).is_pending());
1547
1548        let message_ids =
1549            st.inner.out_buffer.messages.iter().take(2).map(|msg| msg[0]).collect::<Vec<_>>();
1550        assert_eq!(message_ids.len(), 2);
1551        assert_ne!(message_ids[0], message_ids[1]);
1552        assert!(message_ids.contains(&cap_a_offset));
1553        assert!(message_ids.contains(&cap_b_offset));
1554    }
1555
1556    #[tokio::test]
1557    async fn eth_satellite() {
1558        reth_tracing::init_test_tracing();
1559        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1560        let local_addr = listener.local_addr().unwrap();
1561        let (status, fork_filter) = eth_handshake();
1562        let other_status = status;
1563        let other_fork_filter = fork_filter.clone();
1564        let _handle = tokio::spawn(async move {
1565            let (incoming, _) = listener.accept().await.unwrap();
1566            let stream = crate::PassthroughCodec::default().framed(incoming);
1567            let (server_hello, _) = eth_hello();
1568            let (p2p_stream, _) =
1569                UnauthedP2PStream::new(stream).handshake(server_hello).await.unwrap();
1570
1571            let (_eth_stream, _) = UnauthedEthStream::new(p2p_stream)
1572                .handshake::<EthNetworkPrimitives>(other_status, other_fork_filter)
1573                .await
1574                .unwrap();
1575
1576            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
1577        });
1578
1579        let conn = connect_passthrough(local_addr, eth_hello().0).await;
1580        let eth = conn.shared_capabilities().eth().unwrap().clone();
1581
1582        let multiplexer = RlpxProtocolMultiplexer::new(conn);
1583        let _satellite = multiplexer
1584            .into_satellite_stream_with_handshake(eth.capability().as_ref(), async move |proxy| {
1585                UnauthedEthStream::new(proxy)
1586                    .handshake::<EthNetworkPrimitives>(status, fork_filter)
1587                    .await
1588            })
1589            .await
1590            .unwrap();
1591    }
1592
1593    /// A test that install a satellite stream eth+test protocol and sends messages between them.
1594    #[tokio::test(flavor = "multi_thread")]
1595    async fn eth_test_protocol_satellite() {
1596        reth_tracing::init_test_tracing();
1597        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1598        let local_addr = listener.local_addr().unwrap();
1599        let (status, fork_filter) = eth_handshake();
1600        let other_status = status;
1601        let other_fork_filter = fork_filter.clone();
1602        let _handle = tokio::spawn(async move {
1603            let (incoming, _) = listener.accept().await.unwrap();
1604            let stream = crate::PassthroughCodec::default().framed(incoming);
1605            let (server_hello, _) = test_hello();
1606            let (conn, _) = UnauthedP2PStream::new(stream).handshake(server_hello).await.unwrap();
1607
1608            let (mut st, _their_status) = RlpxProtocolMultiplexer::new(conn)
1609                .into_eth_satellite_stream::<EthNetworkPrimitives>(
1610                    other_status,
1611                    other_fork_filter,
1612                    Arc::new(EthHandshake::default()),
1613                    MAX_MESSAGE_SIZE,
1614                )
1615                .await
1616                .unwrap();
1617
1618            st.install_protocol(&TestProtoMessage::capability(), |mut conn| {
1619                async_stream::stream! {
1620                    yield TestProtoMessage::ping().encoded();
1621                    let msg = conn.next().await.unwrap();
1622                    let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap();
1623                    assert_eq!(msg, TestProtoMessage::pong());
1624
1625                    yield TestProtoMessage::message("hello").encoded();
1626                    let msg = conn.next().await.unwrap();
1627                    let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap();
1628                    assert_eq!(msg, TestProtoMessage::message("good bye!"));
1629
1630                    yield TestProtoMessage::message("good bye!").encoded();
1631
1632                    futures::future::pending::<()>().await;
1633                    unreachable!()
1634                }
1635            })
1636            .unwrap();
1637
1638            loop {
1639                let _ = st.next().await;
1640            }
1641        });
1642
1643        let conn = connect_passthrough(local_addr, test_hello().0).await;
1644        let (mut st, _their_status) = RlpxProtocolMultiplexer::new(conn)
1645            .into_eth_satellite_stream::<EthNetworkPrimitives>(
1646                status,
1647                fork_filter,
1648                Arc::new(EthHandshake::default()),
1649                MAX_MESSAGE_SIZE,
1650            )
1651            .await
1652            .unwrap();
1653
1654        let (tx, mut rx) = oneshot::channel();
1655
1656        st.install_protocol(&TestProtoMessage::capability(), |mut conn| {
1657            async_stream::stream! {
1658                let msg = conn.next().await.unwrap();
1659                let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap();
1660                assert_eq!(msg, TestProtoMessage::ping());
1661
1662                yield TestProtoMessage::pong().encoded();
1663
1664                let msg = conn.next().await.unwrap();
1665                let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap();
1666                assert_eq!(msg, TestProtoMessage::message("hello"));
1667
1668                yield TestProtoMessage::message("good bye!").encoded();
1669
1670                let msg = conn.next().await.unwrap();
1671                let msg = TestProtoMessage::decode_message(&mut &msg[..]).unwrap();
1672                assert_eq!(msg, TestProtoMessage::message("good bye!"));
1673
1674                tx.send(()).unwrap();
1675
1676                futures::future::pending::<()>().await;
1677                unreachable!()
1678            }
1679        })
1680        .unwrap();
1681
1682        loop {
1683            tokio::select! {
1684                _ = &mut rx => {
1685                    break
1686                }
1687               _ = st.next() => {
1688                }
1689            }
1690        }
1691    }
1692}