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