1use 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#[derive(Debug)]
43pub struct RlpxProtocolMultiplexer<St> {
44 inner: MultiplexInner<St>,
45}
46
47impl<St> RlpxProtocolMultiplexer<St> {
48 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 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 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 pub const fn shared_capabilities(&self) -> &SharedCapabilities {
92 self.inner.shared_capabilities()
93 }
94
95 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 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 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 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 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 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 to_primary.try_send(msg).map_err(Into::into)?;
234 } else {
235 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 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 conn: P2PStream<St>,
301 protocols: VecDeque<ProtocolStream>,
303 out_buffer: OutBuffer,
305 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 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 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#[derive(Debug)]
378struct PrimaryProtocol<Primary> {
379 to_primary: InboundSender,
381 from_primary: UnboundedReceiverStream<Bytes>,
383 shared_cap: SharedCapability,
385 st: Primary,
387}
388
389#[derive(Debug)]
393pub struct ProtocolProxy {
394 shared_cap: SharedCapability,
395 from_wire: InboundReceiver,
397 to_wire: UnboundedSender<Bytes>,
399}
400
401impl ProtocolProxy {
402 fn try_send(&self, msg: Bytes) -> Result<(), io::Error> {
404 if msg.is_empty() {
405 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 #[inline]
413 fn mask_msg_id(&self, msg: Bytes) -> Result<Bytes, io::Error> {
414 if msg.is_empty() {
415 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 #[inline]
431 fn unmask_id(&self, mut msg: BytesMut) -> Result<BytesMut, io::Error> {
432 if msg.is_empty() {
433 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#[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#[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#[derive(Debug)]
666pub struct RlpxSatelliteStream<St, Primary> {
667 inner: MultiplexInner<St>,
668 primary: PrimaryProtocol<Primary>,
669 next_outbound: usize,
671 producer_polls_since_inbound: usize,
673}
674
675impl<St, Primary> RlpxSatelliteStream<St, Primary> {
676 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 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 #[inline]
716 pub const fn primary(&self) -> &Primary {
717 &self.primary.st
718 }
719
720 #[inline]
722 pub const fn primary_mut(&mut self) -> &mut Primary {
723 &mut self.primary.st
724 }
725
726 #[inline]
728 pub const fn inner(&self) -> &P2PStream<St> {
729 &self.inner.conn
730 }
731
732 #[inline]
734 pub const fn inner_mut(&mut self) -> &mut P2PStream<St> {
735 &mut self.inner.conn
736 }
737
738 #[inline]
740 pub fn into_inner(self) -> P2PStream<St> {
741 self.inner.conn
742 }
743
744 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 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 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 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 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 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 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
957struct ProtocolStream {
959 shared_cap: SharedCapability,
960 to_satellite: InboundSender,
962 satellite_st: Pin<Box<dyn Stream<Item = BytesMut> + Send>>,
963}
964
965impl ProtocolStream {
966 #[inline]
968 fn mask_msg_id(&self, mut msg: BytesMut) -> Result<Bytes, io::Error> {
969 if msg.is_empty() {
970 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 #[inline]
981 fn unmask_id(&self, mut msg: BytesMut) -> Result<BytesMut, io::Error> {
982 if msg.is_empty() {
983 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 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
1015struct 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 self.protocols.push_back(proto);
1041 return Poll::Ready(Ok(msg));
1042 }
1043 _ => {
1044 self.protocols.push_back(proto);
1046 }
1047 }
1048 }
1049
1050 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
1068const MAX_MUX_IN_BUFFER_BYTES: usize = 32 * 1024 * 1024;
1070
1071const MAX_HANDSHAKE_SATELLITE_BYTES: usize = 256 * 1024;
1074const MAX_HANDSHAKE_SATELLITE_MESSAGES: usize = 64;
1075
1076const MAX_INBOUND_MESSAGES_PER_POLL: usize = 32;
1078
1079const 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1125enum ProducerPoll {
1126 Pending,
1128 Full,
1130 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 #[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 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 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}