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