1use core::sync::atomic::Ordering;
4use std::{
5 collections::VecDeque,
6 future::Future,
7 net::SocketAddr,
8 pin::Pin,
9 sync::{
10 atomic::{AtomicU64, AtomicUsize},
11 Arc,
12 },
13 task::{ready, Context, Poll},
14 time::{Duration, Instant},
15};
16
17use crate::{
18 message::{NewBlockMessage, PeerMessage, PeerResponse, PeerResponseResult},
19 session::{
20 conn::EthRlpxConnection,
21 handle::{ActiveSessionMessage, SessionCommand},
22 BlockRangeInfo, EthVersion, SessionId,
23 },
24};
25use alloy_eips::merge::EPOCH_SLOTS;
26use alloy_primitives::Sealable;
27use futures::{stream::Fuse, SinkExt, StreamExt};
28use metrics::{Counter, Gauge};
29use reth_eth_wire::{
30 errors::{EthHandshakeError, EthStreamError},
31 message::{EthBroadcastMessage, MessageError},
32 Capabilities, DisconnectP2P, DisconnectReason, EthMessage, EthSnapMessage, NetworkPrimitives,
33 NewBlockPayload,
34};
35use reth_eth_wire_types::{
36 message::RequestPair, snap::SnapProtocolMessage, NewPooledTransactionHashes,
37 RawCapabilityMessage,
38};
39use reth_metrics::common::mpsc::MeteredPollSender;
40use reth_network_api::{PeerRequest, RequestMessage};
41use reth_network_p2p::{error::RequestError, snap::client::SnapResponse};
42use reth_network_peers::PeerId;
43use reth_network_types::session::config::INITIAL_REQUEST_TIMEOUT;
44use reth_primitives_traits::Block;
45use rustc_hash::FxHashMap;
46use tokio::{
47 sync::{mpsc, mpsc::error::TrySendError, oneshot},
48 time::Interval,
49};
50use tokio_stream::wrappers::ReceiverStream;
51use tokio_util::sync::PollSender;
52use tracing::{debug, trace};
53
54pub(super) const RANGE_UPDATE_INTERVAL: Duration = Duration::from_secs(EPOCH_SLOTS * 12);
60
61const MINIMUM_TIMEOUT: Duration = Duration::from_secs(2);
65
66const MAXIMUM_TIMEOUT: Duration = INITIAL_REQUEST_TIMEOUT;
68const SAMPLE_IMPACT: f64 = 0.1;
70const TIMEOUT_SCALING: u32 = 3;
72
73const MAX_QUEUED_OUTGOING_RESPONSES: usize = 4;
85
86const SHRINK_CAPACITY_THRESHOLD: usize = 64;
89
90const RECEIVE_MESSAGE_BUDGET: usize = 16;
98
99const MAX_QUEUED_BROADCAST_ITEMS: usize = 4096;
105
106#[derive(Debug, Clone)]
112pub(crate) struct BroadcastItemCounter(Arc<AtomicUsize>);
113
114impl BroadcastItemCounter {
115 pub(crate) fn new() -> Self {
117 Self(Arc::new(AtomicUsize::new(0)))
118 }
119
120 pub(crate) fn get(&self) -> usize {
122 self.0.load(Ordering::Relaxed)
123 }
124
125 pub(crate) fn try_add(&self, n: usize) -> bool {
127 let prev = self.0.fetch_add(n, Ordering::Relaxed);
128 if prev >= MAX_QUEUED_BROADCAST_ITEMS {
129 self.0.fetch_sub(n, Ordering::Relaxed);
130 false
131 } else {
132 true
133 }
134 }
135
136 pub(crate) fn sub(&self, n: usize) {
138 self.0.fetch_sub(n, Ordering::Relaxed);
139 }
140}
141
142#[expect(dead_code)]
152pub(crate) struct ActiveSession<N: NetworkPrimitives> {
153 pub(crate) next_id: u64,
155 pub(crate) conn: EthRlpxConnection<N>,
157 pub(crate) remote_peer_id: PeerId,
159 pub(crate) remote_addr: SocketAddr,
161 pub(crate) remote_capabilities: Arc<Capabilities>,
163 pub(crate) session_id: SessionId,
165 pub(crate) commands_rx: ReceiverStream<SessionCommand<N>>,
167 pub(crate) unbounded_rx: mpsc::UnboundedReceiver<SessionCommand<N>>,
170 pub(crate) unbounded_broadcast_msgs: Counter,
172 pub(crate) to_session_manager: MeteredPollSender<ActiveSessionMessage<N>>,
174 pub(crate) pending_message_to_session: Option<ActiveSessionMessage<N>>,
176 pub(crate) internal_request_rx: Fuse<ReceiverStream<PeerRequest<N>>>,
178 pub(crate) inflight_requests: FxHashMap<u64, InflightRequest<PeerRequest<N>>>,
181 pub(crate) received_requests_from_remote: Vec<ReceivedRequest<N>>,
183 pub(crate) queued_outgoing: QueuedOutgoingMessages<N>,
185 pub(crate) internal_request_timeout: Arc<AtomicU64>,
187 pub(crate) internal_request_timeout_interval: Interval,
189 pub(crate) protocol_breach_request_timeout: Duration,
192 pub(crate) terminate_message:
194 Option<(PollSender<ActiveSessionMessage<N>>, ActiveSessionMessage<N>)>,
195 pub(crate) range_info: Option<BlockRangeInfo>,
198 pub(crate) local_range_info: BlockRangeInfo,
201 pub(crate) range_update_interval: Option<Interval>,
205 pub(crate) last_sent_latest_block: Option<u64>,
208}
209
210impl<N: NetworkPrimitives> ActiveSession<N> {
211 fn is_disconnecting(&self) -> bool {
213 self.conn.inner().is_disconnecting()
214 }
215
216 const fn next_id(&mut self) -> u64 {
218 let id = self.next_id;
219 self.next_id += 1;
220 id
221 }
222
223 pub fn shrink_to_fit(&mut self) {
229 self.queued_outgoing.shrink_to_fit();
230 }
231
232 fn poll_send_queued(&mut self, cx: &mut Context<'_>) -> Result<bool, EthStreamError> {
241 let mut progress = false;
242 while self.conn.poll_ready_unpin(cx).is_ready() {
243 let Some(msg) = self.queued_outgoing.pop_front() else { break };
244 progress = true;
245 let res = match msg {
246 OutgoingMessage::Snap(msg) => self.conn.start_send_snap(msg),
247 OutgoingMessage::Eth(msg) => self.conn.start_send_unpin(msg),
248 OutgoingMessage::Broadcast(msg) => self.conn.start_send_broadcast(msg),
249 OutgoingMessage::Raw(msg) => self.conn.start_send_raw(msg),
250 };
251 res?;
252 }
253 Ok(progress)
254 }
255
256 fn on_incoming_message(&mut self, msg: EthMessage<N>) -> OnIncomingMessageOutcome<N> {
260 macro_rules! on_request {
264 ($req:ident, $resp_item:ident, $req_item:ident) => {{
265 let RequestPair { request_id, message: request } = $req;
266 let (tx, response) = oneshot::channel();
267 let received = ReceivedRequest {
268 request_id,
269 rx: PeerResponse::$resp_item { response },
270 received: Instant::now(),
271 };
272 self.received_requests_from_remote.push(received);
273 self.try_emit_request(PeerMessage::EthRequest(PeerRequest::$req_item {
274 request,
275 response: tx,
276 }))
277 .into()
278 }};
279 }
280
281 macro_rules! on_response {
283 ($resp:ident, $item:ident) => {{
284 let RequestPair { request_id, message } = $resp;
285 if let Some(req) = self.inflight_requests.remove(&request_id) {
286 match req.request {
287 RequestState::Waiting(PeerRequest::$item { response, .. }) => {
288 trace!(peer_id=?self.remote_peer_id, ?request_id, "received response from peer");
289 let _ = response.send(Ok(message));
290 self.update_request_timeout(req.timestamp, Instant::now());
291 }
292 RequestState::Waiting(request) => {
293 request.send_bad_response();
294 }
295 RequestState::TimedOut => {
296 self.update_request_timeout(req.timestamp, Instant::now());
298 }
299 }
300 } else {
301 trace!(peer_id=?self.remote_peer_id, ?request_id, "received response to unknown request");
302 self.on_bad_message();
304 }
305
306 OnIncomingMessageOutcome::Ok
307 }};
308 }
309
310 match msg {
311 message @ EthMessage::Status(_) => OnIncomingMessageOutcome::BadMessage {
312 error: EthStreamError::EthHandshakeError(EthHandshakeError::StatusNotInHandshake),
313 message,
314 },
315 EthMessage::NewBlockHashes(msg) => {
316 self.try_emit_broadcast(PeerMessage::NewBlockHashes(msg)).into()
317 }
318 EthMessage::NewBlock(msg) => {
319 let block = NewBlockMessage {
320 hash: msg.block().header().hash_slow(),
321 block: Arc::new(*msg),
322 };
323 self.try_emit_broadcast(PeerMessage::NewBlock(block)).into()
324 }
325 EthMessage::Transactions(msg) => {
326 self.try_emit_broadcast(PeerMessage::ReceivedTransaction(msg)).into()
327 }
328 EthMessage::NewPooledTransactionHashes66(msg) => {
329 self.try_emit_broadcast(PeerMessage::PooledTransactions(msg.into())).into()
330 }
331 EthMessage::NewPooledTransactionHashes68(msg) => {
332 self.try_emit_broadcast(PeerMessage::PooledTransactions(msg.into())).into()
333 }
334 EthMessage::NewPooledTransactionHashes72(msg) => {
335 self.try_emit_broadcast(PeerMessage::PooledTransactions(msg.into())).into()
336 }
337 EthMessage::GetBlockHeaders(req) => {
338 on_request!(req, BlockHeaders, GetBlockHeaders)
339 }
340 EthMessage::BlockHeaders(resp) => {
341 on_response!(resp, GetBlockHeaders)
342 }
343 EthMessage::GetBlockBodies(req) => {
344 on_request!(req, BlockBodies, GetBlockBodies)
345 }
346 EthMessage::BlockBodies(resp) => {
347 on_response!(resp, GetBlockBodies)
348 }
349 EthMessage::GetPooledTransactions(req) => {
350 on_request!(req, PooledTransactions, GetPooledTransactions)
351 }
352 EthMessage::PooledTransactions(resp) => {
353 on_response!(resp, GetPooledTransactions)
354 }
355 EthMessage::GetNodeData(req) => {
356 on_request!(req, NodeData, GetNodeData)
357 }
358 EthMessage::NodeData(resp) => {
359 on_response!(resp, GetNodeData)
360 }
361 EthMessage::GetReceipts(req) => {
362 if self.conn.version() >= EthVersion::Eth69 {
363 on_request!(req, Receipts69, GetReceipts69)
364 } else {
365 on_request!(req, Receipts, GetReceipts)
366 }
367 }
368 EthMessage::GetReceipts70(req) => {
369 on_request!(req, Receipts70, GetReceipts70)
370 }
371 EthMessage::Receipts(resp) => {
372 on_response!(resp, GetReceipts)
373 }
374 EthMessage::Receipts69(resp) => {
375 on_response!(resp, GetReceipts69)
376 }
377 EthMessage::Receipts70(resp) => {
378 on_response!(resp, GetReceipts70)
379 }
380 EthMessage::GetBlockAccessLists(req) => {
381 on_request!(req, BlockAccessLists, GetBlockAccessLists)
382 }
383 EthMessage::BlockAccessLists(resp) => {
384 on_response!(resp, GetBlockAccessLists)
385 }
386 EthMessage::Cells(resp) => {
387 on_response!(resp, GetCells)
388 }
389 EthMessage::BlockRangeUpdate(msg) => {
390 if msg.earliest > msg.latest {
392 return OnIncomingMessageOutcome::BadMessage {
393 error: EthStreamError::InvalidMessage(MessageError::Other(format!(
394 "invalid block range: earliest ({}) > latest ({})",
395 msg.earliest, msg.latest
396 ))),
397 message: EthMessage::BlockRangeUpdate(msg),
398 };
399 }
400
401 if msg.latest_hash.is_zero() {
403 return OnIncomingMessageOutcome::BadMessage {
404 error: EthStreamError::InvalidMessage(MessageError::Other(
405 "invalid block range: latest_hash cannot be zero".to_string(),
406 )),
407 message: EthMessage::BlockRangeUpdate(msg),
408 };
409 }
410
411 if let Some(range_info) = self.range_info.as_ref() {
412 range_info.update(msg.earliest, msg.latest, msg.latest_hash);
413 }
414
415 OnIncomingMessageOutcome::Ok
416 }
417 EthMessage::GetCells(resp) => {
418 on_request!(resp, Cells, GetCells)
419 }
420 EthMessage::Other(bytes) => self.try_emit_broadcast(PeerMessage::Other(bytes)).into(),
421 }
422 }
423
424 fn on_incoming_snap_message(
431 &mut self,
432 mut msg: SnapProtocolMessage,
433 ) -> OnIncomingMessageOutcome<N> {
434 let request_id = msg.request_id();
435 if !msg.is_response() {
436 let (tx, response) = oneshot::channel();
437 self.received_requests_from_remote.push(ReceivedRequest {
438 request_id,
439 rx: PeerResponse::Snap { response },
440 received: Instant::now(),
441 });
442 return self
443 .try_emit_request(PeerMessage::EthRequest(PeerRequest::GetSnap {
444 request: msg,
445 response: tx,
446 }))
447 .into()
448 }
449
450 let Some(req) = self.inflight_requests.remove(&request_id) else {
451 trace!(target: "net::session", ?request_id, remote_peer_id=?self.remote_peer_id, "received snap response to unknown request");
452 self.on_bad_message();
453 return OnIncomingMessageOutcome::Ok
454 };
455
456 match req.request {
457 RequestState::Waiting(PeerRequest::GetSnap { request, response }) => {
458 if Some(msg.message_id()) != request.message_id().response() {
459 debug!(target: "net::session", ?request_id, msg_id=?msg.message_id(), remote_peer_id=?self.remote_peer_id, "received snap response of wrong type");
460 self.on_bad_message();
461 let _ = response.send(Err(RequestError::BadResponse));
462 return OnIncomingMessageOutcome::Ok
463 }
464 msg.set_request_id(request.request_id());
466 match SnapResponse::try_from(msg) {
467 Ok(snap_response) => {
468 trace!(target: "net::session", ?request_id, remote_peer_id=?self.remote_peer_id, "received snap response from peer");
469 let _ = response.send(Ok(snap_response));
470 self.update_request_timeout(req.timestamp, Instant::now());
471 }
472 Err(_) => {
473 let _ = response.send(Err(RequestError::BadResponse));
474 }
475 }
476 }
477 RequestState::Waiting(request) => {
478 request.send_bad_response();
480 }
481 RequestState::TimedOut => {
482 self.update_request_timeout(req.timestamp, Instant::now());
483 }
484 }
485 OnIncomingMessageOutcome::Ok
486 }
487
488 fn on_internal_peer_request(&mut self, request: PeerRequest<N>, deadline: Instant) {
490 let version = self.conn.version();
491 if !Self::is_request_supported_for_version(&request, version) {
492 debug!(
493 target: "net",
494 ?request,
495 peer_id=?self.remote_peer_id,
496 ?version,
497 "Request not supported for negotiated eth version",
498 );
499 request.send_err_response(RequestError::UnsupportedCapability);
500 return;
501 }
502
503 if matches!(request, PeerRequest::GetSnap { .. }) && !self.conn.supports_snap() {
506 request.send_err_response(RequestError::UnsupportedCapability);
507 return;
508 }
509
510 let request_id = self.next_id();
511 trace!(?request, peer_id=?self.remote_peer_id, ?request_id, "sending request to peer");
512 let msg = match request.create_request_message(request_id) {
513 RequestMessage::Eth(msg) => msg.map_versioned(version).into(),
514 RequestMessage::Snap(msg) => OutgoingMessage::Snap(msg),
515 };
516
517 self.queued_outgoing.push_back(msg);
518 let req = InflightRequest {
519 request: RequestState::Waiting(request),
520 timestamp: Instant::now(),
521 deadline,
522 };
523 self.inflight_requests.insert(request_id, req);
524 }
525
526 #[inline]
527 fn is_request_supported_for_version(request: &PeerRequest<N>, version: EthVersion) -> bool {
528 request.is_supported_by_eth_version(version)
529 }
530
531 fn on_internal_peer_message(&mut self, msg: PeerMessage<N>) {
533 match msg {
534 PeerMessage::NewBlockHashes(msg) => {
535 self.queued_outgoing.push_back(EthMessage::NewBlockHashes(msg).into());
536 }
537 PeerMessage::NewBlock(msg) => {
538 self.queued_outgoing.push_back(EthBroadcastMessage::NewBlock(msg.block).into());
539 }
540 PeerMessage::PooledTransactions(msg) => {
541 if msg.is_valid_for_version(self.conn.version()) {
542 self.queued_outgoing.push_pooled_hashes(msg);
543 } else {
544 self.queued_outgoing.broadcast_items.sub(msg.len());
545 debug!(target: "net", ?msg, version=?self.conn.version(), "Message is invalid for connection version, skipping");
546 }
547 }
548 PeerMessage::EthRequest(req) => {
549 let deadline = self.request_deadline();
550 self.on_internal_peer_request(req, deadline);
551 }
552 PeerMessage::SendTransactions(msg) => {
553 self.queued_outgoing.push_back(EthBroadcastMessage::Transactions(msg).into());
554 }
555 PeerMessage::SendBroadcastPoolTransactions(msg) => {
556 self.queued_outgoing
557 .push_back(EthBroadcastMessage::BroadcastPoolTransactions(msg).into());
558 }
559 PeerMessage::BlockRangeUpdated(_) => {}
560 PeerMessage::ReceivedTransaction(_) => {
561 unreachable!("Not emitted by network")
562 }
563 PeerMessage::Other(other) => {
564 self.queued_outgoing.push_back(OutgoingMessage::Raw(other));
565 }
566 }
567 }
568
569 fn request_deadline(&self) -> Instant {
571 Instant::now() +
572 Duration::from_millis(self.internal_request_timeout.load(Ordering::Relaxed))
573 }
574
575 fn handle_outgoing_response(&mut self, id: u64, resp: PeerResponseResult<N>) {
579 match resp.try_into_message(id) {
580 Ok(RequestMessage::Eth(msg)) => {
581 self.queued_outgoing.push_back(msg.into());
582 }
583 Ok(RequestMessage::Snap(msg)) => {
584 self.queued_outgoing.push_back(OutgoingMessage::Snap(msg));
585 }
586 Err(err) => {
587 debug!(target: "net", %err, "Failed to respond to received request");
588 }
589 }
590 }
591
592 #[expect(clippy::result_large_err)]
596 fn try_emit_broadcast(&self, message: PeerMessage<N>) -> Result<(), ActiveSessionMessage<N>> {
597 let Some(sender) = self.to_session_manager.inner().get_ref() else { return Ok(()) };
598
599 match sender
600 .try_send(ActiveSessionMessage::ValidMessage { peer_id: self.remote_peer_id, message })
601 {
602 Ok(_) => Ok(()),
603 Err(err) => {
604 trace!(
605 target: "net",
606 %err,
607 "no capacity for incoming broadcast",
608 );
609 match err {
610 TrySendError::Full(msg) => Err(msg),
611 TrySendError::Closed(_) => Ok(()),
612 }
613 }
614 }
615 }
616
617 #[expect(clippy::result_large_err)]
622 fn try_emit_request(&self, message: PeerMessage<N>) -> Result<(), ActiveSessionMessage<N>> {
623 let Some(sender) = self.to_session_manager.inner().get_ref() else { return Ok(()) };
624
625 match sender
626 .try_send(ActiveSessionMessage::ValidMessage { peer_id: self.remote_peer_id, message })
627 {
628 Ok(_) => Ok(()),
629 Err(err) => {
630 trace!(
631 target: "net",
632 %err,
633 "no capacity for incoming request",
634 );
635 match err {
636 TrySendError::Full(msg) => Err(msg),
637 TrySendError::Closed(_) => {
638 Ok(())
641 }
642 }
643 }
644 }
645 }
646
647 fn on_bad_message(&self) {
649 let Some(sender) = self.to_session_manager.inner().get_ref() else { return };
650 let _ = sender.try_send(ActiveSessionMessage::BadMessage { peer_id: self.remote_peer_id });
651 }
652
653 fn emit_disconnect(&mut self, cx: &mut Context<'_>) -> Poll<()> {
655 trace!(target: "net::session", remote_peer_id=?self.remote_peer_id, "emitting disconnect");
656 let msg = ActiveSessionMessage::Disconnected {
657 peer_id: self.remote_peer_id,
658 remote_addr: self.remote_addr,
659 };
660
661 self.terminate_message = Some((self.to_session_manager.inner().clone(), msg));
662 self.poll_terminate_message(cx).expect("message is set")
663 }
664
665 fn close_on_error(&mut self, error: EthStreamError, cx: &mut Context<'_>) -> Poll<()> {
667 let msg = ActiveSessionMessage::ClosedOnConnectionError {
668 peer_id: self.remote_peer_id,
669 remote_addr: self.remote_addr,
670 error,
671 };
672 self.terminate_message = Some((self.to_session_manager.inner().clone(), msg));
673 self.poll_terminate_message(cx).expect("message is set")
674 }
675
676 fn start_disconnect(&mut self, reason: DisconnectReason) -> Result<(), EthStreamError> {
678 Ok(self.conn.inner_mut().start_disconnect(reason)?)
679 }
680
681 fn poll_disconnect(&mut self, cx: &mut Context<'_>) -> Poll<()> {
683 debug_assert!(self.is_disconnecting(), "not disconnecting");
684
685 let _ = ready!(self.conn.poll_close_unpin(cx));
687 self.emit_disconnect(cx)
688 }
689
690 fn try_disconnect(&mut self, reason: DisconnectReason, cx: &mut Context<'_>) -> Poll<()> {
692 match self.start_disconnect(reason) {
693 Ok(()) => {
694 self.poll_disconnect(cx)
696 }
697 Err(err) => {
698 debug!(target: "net::session", %err, remote_peer_id=?self.remote_peer_id, "could not send disconnect");
699 self.close_on_error(err, cx)
700 }
701 }
702 }
703
704 #[must_use]
713 fn check_timed_out_requests(&mut self, now: Instant) -> bool {
714 for (id, req) in &mut self.inflight_requests {
715 if req.is_timed_out(now) {
716 if req.is_waiting() {
717 debug!(target: "net::session", ?id, remote_peer_id=?self.remote_peer_id, "timed out outgoing request");
718 req.timeout();
719 } else if now - req.timestamp > self.protocol_breach_request_timeout {
720 return true
721 }
722 }
723 }
724
725 false
726 }
727
728 fn update_request_timeout(&mut self, sent: Instant, received: Instant) {
730 let elapsed = received.saturating_duration_since(sent);
731
732 let current = Duration::from_millis(self.internal_request_timeout.load(Ordering::Relaxed));
733 let request_timeout = calculate_new_timeout(current, elapsed);
734 self.internal_request_timeout.store(request_timeout.as_millis() as u64, Ordering::Relaxed);
735 self.internal_request_timeout_interval = request_timeout_interval(request_timeout);
736 }
737
738 fn poll_terminate_message(&mut self, cx: &mut Context<'_>) -> Option<Poll<()>> {
740 let (mut tx, msg) = self.terminate_message.take()?;
741 match tx.poll_reserve(cx) {
742 Poll::Pending => {
743 self.terminate_message = Some((tx, msg));
744 return Some(Poll::Pending)
745 }
746 Poll::Ready(Ok(())) => {
747 let _ = tx.send_item(msg);
748 }
749 Poll::Ready(Err(_)) => {
750 }
752 }
753 Some(Poll::Ready(()))
755 }
756}
757
758impl<N: NetworkPrimitives> Future for ActiveSession<N> {
759 type Output = ();
760
761 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
762 let this = self.get_mut();
763
764 if let Some(terminate) = this.poll_terminate_message(cx) {
766 return terminate
767 }
768
769 if this.is_disconnecting() {
770 return this.poll_disconnect(cx)
771 }
772
773 let mut budget = RECEIVE_MESSAGE_BUDGET;
779
780 'main: loop {
782 let mut progress = false;
783 let mut receive_pending = false;
784
785 loop {
787 match this.commands_rx.poll_next_unpin(cx) {
788 Poll::Pending => break,
789 Poll::Ready(None) => {
790 return Poll::Ready(())
793 }
794 Poll::Ready(Some(cmd)) => {
795 progress = true;
796 match cmd {
797 SessionCommand::Disconnect { reason } => {
798 debug!(
799 target: "net::session",
800 ?reason,
801 remote_peer_id=?this.remote_peer_id,
802 "Received disconnect command for session"
803 );
804 let reason =
805 reason.unwrap_or(DisconnectReason::DisconnectRequested);
806
807 return this.try_disconnect(reason, cx)
808 }
809 SessionCommand::Message(msg) => {
810 this.on_internal_peer_message(msg);
811 }
812 }
813 }
814 }
815 }
816
817 while let Poll::Ready(Some(cmd)) = this.unbounded_rx.poll_recv(cx) {
819 progress = true;
820 match cmd {
821 SessionCommand::Message(msg) => {
822 this.unbounded_broadcast_msgs.increment(1);
823 this.on_internal_peer_message(msg);
824 }
825 SessionCommand::Disconnect { reason } => {
826 let reason = reason.unwrap_or(DisconnectReason::DisconnectRequested);
827 return this.try_disconnect(reason, cx);
828 }
829 }
830 }
831
832 let deadline = this.request_deadline();
833
834 while let Poll::Ready(Some(req)) = this.internal_request_rx.poll_next_unpin(cx) {
835 progress = true;
836 this.on_internal_peer_request(req, deadline);
837 }
838
839 for idx in (0..this.received_requests_from_remote.len()).rev() {
842 let mut req = this.received_requests_from_remote.swap_remove(idx);
843 match req.rx.poll(cx) {
844 Poll::Pending => {
845 this.received_requests_from_remote.push(req);
847 }
848 Poll::Ready(resp) => {
849 this.handle_outgoing_response(req.request_id, resp);
850 }
851 }
852 }
853
854 match this.poll_send_queued(cx) {
859 Ok(sent) => progress |= sent,
860 Err(err) => {
861 debug!(target: "net::session", %err, remote_peer_id=?this.remote_peer_id, "failed to send message");
862 return this.close_on_error(err, cx)
864 }
865 }
866
867 'receive: loop {
869 budget -= 1;
871 if budget == 0 {
872 cx.waker().wake_by_ref();
874 break 'main
875 }
876
877 if let Some(msg) = this.pending_message_to_session.take() {
881 match this.to_session_manager.poll_reserve(cx) {
882 Poll::Ready(Ok(_)) => {
883 let _ = this.to_session_manager.send_item(msg);
884 }
885 Poll::Ready(Err(_)) => return Poll::Ready(()),
886 Poll::Pending => {
887 this.pending_message_to_session = Some(msg);
888 break 'receive
889 }
890 };
891 }
892
893 if this.received_requests_from_remote.len() > MAX_QUEUED_OUTGOING_RESPONSES {
895 break 'receive
901 }
902
903 if this.queued_outgoing.response_count() > MAX_QUEUED_OUTGOING_RESPONSES {
905 break 'receive
912 }
913
914 match this.conn.poll_next_unpin(cx) {
915 Poll::Pending => {
916 receive_pending = true;
917 break
918 }
919 Poll::Ready(None) => {
920 if this.is_disconnecting() {
921 break
922 }
923 debug!(target: "net::session", remote_peer_id=?this.remote_peer_id, "eth stream completed");
924 return this.emit_disconnect(cx)
925 }
926 Poll::Ready(Some(res)) => {
927 match res {
928 Ok(msg) => {
929 let outcome = match msg {
930 EthSnapMessage::Eth(msg) => {
931 trace!(target: "net::session", msg_id=?msg.message_id(), remote_peer_id=?this.remote_peer_id, "received eth message");
932 this.on_incoming_message(msg)
934 }
935 EthSnapMessage::Snap(msg) => this.on_incoming_snap_message(msg),
936 };
937 match outcome {
938 OnIncomingMessageOutcome::Ok => {
939 progress = true;
941 }
942 OnIncomingMessageOutcome::BadMessage { error, message } => {
943 debug!(target: "net::session", %error, msg=?message, remote_peer_id=?this.remote_peer_id, "received invalid protocol message");
944 this.on_bad_message();
945 return this
946 .try_disconnect(DisconnectReason::ProtocolBreach, cx)
947 }
948 OnIncomingMessageOutcome::NoCapacity(msg) => {
949 this.pending_message_to_session = Some(msg);
951 }
952 }
953 }
954 Err(err) => {
955 debug!(target: "net::session", %err, remote_peer_id=?this.remote_peer_id, "failed to receive message");
956 if err.is_protocol_breach() {
957 this.on_bad_message();
958 return this.try_disconnect(DisconnectReason::ProtocolBreach, cx)
959 }
960 return this.close_on_error(err, cx)
961 }
962 }
963 }
964 }
965 }
966
967 if receive_pending &&
970 this.queued_outgoing.is_empty() &&
971 this.pending_message_to_session.is_none() &&
972 this.received_requests_from_remote.is_empty()
973 {
974 break 'main
975 }
976
977 if !progress {
978 break 'main
979 }
980 }
981
982 if let Some(interval) = &mut this.range_update_interval {
983 while interval.poll_tick(cx).is_ready() {
985 let current_latest = this.local_range_info.latest();
986 let should_send = if let Some(last_sent) = this.last_sent_latest_block {
987 current_latest.saturating_sub(last_sent) >= EPOCH_SLOTS
989 } else {
990 true };
992
993 if should_send {
994 this.queued_outgoing.push_back(
995 EthMessage::BlockRangeUpdate(this.local_range_info.to_message()).into(),
996 );
997 this.last_sent_latest_block = Some(current_latest);
998 }
999 }
1000 }
1001
1002 if !this.inflight_requests.is_empty() {
1003 while this.internal_request_timeout_interval.poll_tick(cx).is_ready() {
1004 if this.check_timed_out_requests(Instant::now()) &&
1006 let Poll::Ready(Ok(_)) = this.to_session_manager.poll_reserve(cx)
1007 {
1008 let msg = ActiveSessionMessage::ProtocolBreach { peer_id: this.remote_peer_id };
1009 this.pending_message_to_session = Some(msg);
1010 }
1011 }
1012 }
1013
1014 if let Err(err) = this.poll_send_queued(cx) {
1018 debug!(target: "net::session", %err, remote_peer_id=?this.remote_peer_id, "failed to send message");
1019 return this.close_on_error(err, cx)
1020 }
1021 match this.conn.poll_flush_unpin(cx) {
1022 Poll::Pending | Poll::Ready(Ok(())) => {}
1023 Poll::Ready(Err(err)) => {
1024 debug!(target: "net::session", %err, remote_peer_id=?this.remote_peer_id, "failed to flush connection");
1025 return this.close_on_error(err, cx)
1026 }
1027 }
1028
1029 this.shrink_to_fit();
1030
1031 Poll::Pending
1032 }
1033}
1034
1035pub(crate) struct ReceivedRequest<N: NetworkPrimitives> {
1037 request_id: u64,
1039 rx: PeerResponse<N>,
1041 #[expect(dead_code)]
1043 received: Instant,
1044}
1045
1046pub(crate) struct InflightRequest<R> {
1048 request: RequestState<R>,
1050 timestamp: Instant,
1052 deadline: Instant,
1054}
1055
1056impl<N: NetworkPrimitives> InflightRequest<PeerRequest<N>> {
1057 #[inline]
1059 fn is_timed_out(&self, now: Instant) -> bool {
1060 now > self.deadline
1061 }
1062
1063 #[inline]
1065 const fn is_waiting(&self) -> bool {
1066 matches!(self.request, RequestState::Waiting(_))
1067 }
1068
1069 fn timeout(&mut self) {
1071 let mut req = RequestState::TimedOut;
1072 std::mem::swap(&mut self.request, &mut req);
1073
1074 if let RequestState::Waiting(req) = req {
1075 req.send_err_response(RequestError::Timeout);
1076 }
1077 }
1078}
1079
1080enum OnIncomingMessageOutcome<N: NetworkPrimitives> {
1082 Ok,
1084 BadMessage { error: EthStreamError, message: EthMessage<N> },
1086 NoCapacity(ActiveSessionMessage<N>),
1088}
1089
1090impl<N: NetworkPrimitives> From<Result<(), ActiveSessionMessage<N>>>
1091 for OnIncomingMessageOutcome<N>
1092{
1093 fn from(res: Result<(), ActiveSessionMessage<N>>) -> Self {
1094 match res {
1095 Ok(_) => Self::Ok,
1096 Err(msg) => Self::NoCapacity(msg),
1097 }
1098 }
1099}
1100
1101enum RequestState<R> {
1102 Waiting(R),
1104 TimedOut,
1106}
1107
1108#[derive(Debug)]
1110pub(crate) enum OutgoingMessage<N: NetworkPrimitives> {
1111 Eth(EthMessage<N>),
1113 Broadcast(EthBroadcastMessage<N>),
1115 Raw(RawCapabilityMessage),
1117 Snap(SnapProtocolMessage),
1119}
1120
1121impl<N: NetworkPrimitives> OutgoingMessage<N> {
1122 const fn is_response(&self) -> bool {
1124 match self {
1125 Self::Eth(msg) => msg.is_response(),
1126 Self::Snap(msg) => msg.is_response(),
1129 Self::Broadcast(_) | Self::Raw(_) => false,
1130 }
1131 }
1132
1133 fn broadcast_item_count(&self) -> usize {
1139 match self {
1140 Self::Eth(msg) => match msg {
1141 EthMessage::NewBlockHashes(h) => h.len(),
1142 EthMessage::NewPooledTransactionHashes66(h) => h.len(),
1143 EthMessage::NewPooledTransactionHashes68(h) => h.hashes.len(),
1144 EthMessage::NewPooledTransactionHashes72(h) => h.hashes.len(),
1145 _ => 0,
1146 },
1147 Self::Broadcast(msg) => match msg {
1148 EthBroadcastMessage::NewBlock(_) => 1,
1149 EthBroadcastMessage::Transactions(txs) => txs.len(),
1150 EthBroadcastMessage::BroadcastPoolTransactions(txs) => txs.len(),
1151 },
1152 Self::Raw(_) | Self::Snap(_) => 0,
1153 }
1154 }
1155
1156 fn try_merge_hashes(
1159 &mut self,
1160 incoming: NewPooledTransactionHashes,
1161 ) -> Option<NewPooledTransactionHashes> {
1162 let Self::Eth(eth) = self else { return Some(incoming) };
1163 match (eth, incoming) {
1164 (
1165 EthMessage::NewPooledTransactionHashes66(existing),
1166 NewPooledTransactionHashes::Eth66(inc),
1167 ) => {
1168 existing.extend(inc);
1169 None
1170 }
1171 (
1172 EthMessage::NewPooledTransactionHashes68(existing),
1173 NewPooledTransactionHashes::Eth68(inc),
1174 ) => {
1175 existing.hashes.extend(inc.hashes);
1176 existing.sizes.extend(inc.sizes);
1177 existing.types.extend(inc.types);
1178 None
1179 }
1180 (
1181 EthMessage::NewPooledTransactionHashes72(existing),
1182 NewPooledTransactionHashes::Eth72(inc),
1183 ) => {
1184 existing.hashes.extend(inc.hashes);
1185 existing.sizes.extend(inc.sizes);
1186 existing.types.extend(inc.types);
1187 None
1188 }
1189 (_, incoming) => Some(incoming),
1190 }
1191 }
1192}
1193
1194impl<N: NetworkPrimitives> From<EthMessage<N>> for OutgoingMessage<N> {
1195 fn from(value: EthMessage<N>) -> Self {
1196 Self::Eth(value)
1197 }
1198}
1199
1200impl<N: NetworkPrimitives> From<EthBroadcastMessage<N>> for OutgoingMessage<N> {
1201 fn from(value: EthBroadcastMessage<N>) -> Self {
1202 Self::Broadcast(value)
1203 }
1204}
1205
1206pub(super) fn request_timeout_interval(timeout: Duration) -> Interval {
1211 let mut interval = tokio::time::interval(timeout);
1212 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1213 interval
1214}
1215
1216#[inline]
1218fn calculate_new_timeout(current_timeout: Duration, estimated_rtt: Duration) -> Duration {
1219 let new_timeout = estimated_rtt.mul_f64(SAMPLE_IMPACT) * TIMEOUT_SCALING;
1220
1221 let smoothened_timeout = current_timeout.mul_f64(1.0 - SAMPLE_IMPACT) + new_timeout;
1223
1224 smoothened_timeout.clamp(MINIMUM_TIMEOUT, MAXIMUM_TIMEOUT)
1225}
1226
1227pub(crate) struct QueuedOutgoingMessages<N: NetworkPrimitives> {
1234 messages: VecDeque<OutgoingMessage<N>>,
1235 queued_responses: usize,
1238 count: Gauge,
1239 broadcast_items: BroadcastItemCounter,
1241}
1242
1243impl<N: NetworkPrimitives> QueuedOutgoingMessages<N> {
1244 pub(crate) const fn new(metric: Gauge, broadcast_items: BroadcastItemCounter) -> Self {
1245 Self { messages: VecDeque::new(), queued_responses: 0, count: metric, broadcast_items }
1246 }
1247
1248 pub(crate) const fn response_count(&self) -> usize {
1250 self.queued_responses
1251 }
1252
1253 pub(crate) fn is_empty(&self) -> bool {
1254 self.messages.is_empty()
1255 }
1256
1257 pub(crate) fn push_back(&mut self, message: OutgoingMessage<N>) {
1258 self.queued_responses += message.is_response() as usize;
1259 self.messages.push_back(message);
1260 self.count.increment(1);
1261 }
1262
1263 pub(crate) fn pop_front(&mut self) -> Option<OutgoingMessage<N>> {
1264 self.messages.pop_front().inspect(|msg| {
1265 self.count.decrement(1);
1266 self.queued_responses -= msg.is_response() as usize;
1267 let items = msg.broadcast_item_count();
1268 if items > 0 {
1269 self.broadcast_items.sub(items);
1270 }
1271 })
1272 }
1273
1274 pub(crate) fn push_pooled_hashes(&mut self, msg: NewPooledTransactionHashes) {
1277 let msg = if let Some(last) = self.messages.back_mut() {
1278 match last.try_merge_hashes(msg) {
1279 None => return,
1280 Some(msg) => msg,
1281 }
1282 } else {
1283 msg
1284 };
1285 self.messages.push_back(EthMessage::from(msg).into());
1286 self.count.increment(1);
1287 }
1288
1289 pub(crate) fn shrink_to_fit(&mut self) {
1293 if self.messages.is_empty() && self.messages.capacity() > SHRINK_CAPACITY_THRESHOLD {
1294 self.messages.shrink_to(MAX_QUEUED_OUTGOING_RESPONSES);
1295 }
1296 }
1297}
1298
1299impl<N: NetworkPrimitives> Drop for QueuedOutgoingMessages<N> {
1300 fn drop(&mut self) {
1301 let remaining = self.messages.len();
1303 if remaining > 0 {
1304 self.count.decrement(remaining as f64);
1305 }
1306 }
1307}
1308
1309#[cfg(test)]
1310mod tests {
1311 use super::*;
1312 use crate::session::{handle::PendingSessionEvent, start_pending_incoming_session};
1313 use alloy_eips::eip2124::ForkFilter;
1314 use alloy_primitives::B256;
1315 use futures::task::noop_waker;
1316 use reth_chainspec::MAINNET;
1317 use reth_ecies::stream::ECIESStream;
1318 use reth_eth_wire::{
1319 handshake::EthHandshake, protocol::Protocol, EthNetworkPrimitives, EthStream,
1320 GetBlockAccessLists, GetBlockBodies, HelloMessageWithProtocols, P2PStream, StatusBuilder,
1321 UnauthedEthStream, UnauthedP2PStream, UnifiedStatus,
1322 };
1323 use reth_eth_wire_types::{
1324 message::MAX_MESSAGE_SIZE,
1325 snap::{
1326 AccountRangeMessage, BlockAccessListsMessage, GetAccountRangeMessage,
1327 GetBlockAccessListsMessage,
1328 },
1329 BlockAccessLists, EthMessageID, NewPooledTransactionHashes72,
1330 };
1331 use reth_ethereum_forks::EthereumHardfork;
1332 use reth_network_p2p::error::RequestResult;
1333 use reth_network_peers::pk2id;
1334 use reth_network_types::session::config::PROTOCOL_BREACH_REQUEST_TIMEOUT;
1335 use secp256k1::{SecretKey, SECP256K1};
1336 use tokio::{
1337 net::{TcpListener, TcpStream},
1338 sync::mpsc,
1339 };
1340
1341 fn eth_hello(server_key: &SecretKey) -> HelloMessageWithProtocols {
1343 HelloMessageWithProtocols::builder(pk2id(&server_key.public_key(SECP256K1))).build()
1344 }
1345
1346 struct SessionBuilder<N: NetworkPrimitives = EthNetworkPrimitives> {
1347 _remote_capabilities: Arc<Capabilities>,
1348 active_session_tx: mpsc::Sender<ActiveSessionMessage<N>>,
1349 active_session_rx: ReceiverStream<ActiveSessionMessage<N>>,
1350 to_sessions: Vec<mpsc::Sender<SessionCommand<N>>>,
1351 secret_key: SecretKey,
1352 local_peer_id: PeerId,
1353 hello: HelloMessageWithProtocols,
1354 status: UnifiedStatus,
1355 fork_filter: ForkFilter,
1356 next_id: usize,
1357 }
1358
1359 impl<N: NetworkPrimitives> SessionBuilder<N> {
1360 fn next_id(&mut self) -> SessionId {
1361 let id = self.next_id;
1362 self.next_id += 1;
1363 SessionId(id)
1364 }
1365
1366 fn with_client_stream<F, O>(
1368 &self,
1369 local_addr: SocketAddr,
1370 f: F,
1371 ) -> Pin<Box<dyn Future<Output = ()> + Send>>
1372 where
1373 F: FnOnce(EthStream<P2PStream<ECIESStream<TcpStream>>, N>) -> O + Send + 'static,
1374 O: Future<Output = ()> + Send + Sync,
1375 {
1376 let mut status = self.status;
1377 let fork_filter = self.fork_filter.clone();
1378 let local_peer_id = self.local_peer_id;
1379 let mut hello = self.hello.clone();
1380 let key = SecretKey::new(&mut rand_08::thread_rng());
1381 hello.id = pk2id(&key.public_key(SECP256K1));
1382 Box::pin(async move {
1383 let outgoing = TcpStream::connect(local_addr).await.unwrap();
1384 let sink = ECIESStream::connect(outgoing, key, local_peer_id).await.unwrap();
1385
1386 let (p2p_stream, _) = UnauthedP2PStream::new(sink).handshake(hello).await.unwrap();
1387
1388 let eth_version = p2p_stream.shared_capabilities().eth_version().unwrap();
1389 status.set_eth_version(eth_version);
1390
1391 let (client_stream, _) = UnauthedEthStream::new(p2p_stream)
1392 .handshake(status, fork_filter)
1393 .await
1394 .unwrap();
1395 f(client_stream).await
1396 })
1397 }
1398
1399 async fn connect_incoming(&mut self, stream: TcpStream) -> ActiveSession<N> {
1400 let remote_addr = stream.local_addr().unwrap();
1401 let session_id = self.next_id();
1402 let (_disconnect_tx, disconnect_rx) = oneshot::channel();
1403 let (pending_sessions_tx, pending_sessions_rx) = mpsc::channel(1);
1404
1405 tokio::task::spawn(start_pending_incoming_session(
1406 Arc::new(EthHandshake::default()),
1407 MAX_MESSAGE_SIZE,
1408 disconnect_rx,
1409 session_id,
1410 stream,
1411 pending_sessions_tx,
1412 remote_addr,
1413 self.secret_key,
1414 self.hello.clone(),
1415 self.status,
1416 self.fork_filter.clone(),
1417 Default::default(),
1418 ));
1419
1420 let mut stream = ReceiverStream::new(pending_sessions_rx);
1421
1422 match stream.next().await.unwrap() {
1423 PendingSessionEvent::Established {
1424 session_id,
1425 remote_addr,
1426 peer_id,
1427 capabilities,
1428 conn,
1429 ..
1430 } => {
1431 let (_to_session_tx, messages_rx) = mpsc::channel(10);
1432 let (commands_to_session, commands_rx) = mpsc::channel(10);
1433 let (_unbounded_tx, unbounded_rx) = mpsc::unbounded_channel();
1434 let poll_sender = PollSender::new(self.active_session_tx.clone());
1435
1436 self.to_sessions.push(commands_to_session);
1437
1438 ActiveSession {
1439 next_id: 0,
1440 remote_peer_id: peer_id,
1441 remote_addr,
1442 remote_capabilities: Arc::clone(&capabilities),
1443 session_id,
1444 commands_rx: ReceiverStream::new(commands_rx),
1445 unbounded_rx,
1446 unbounded_broadcast_msgs: Counter::noop(),
1447 to_session_manager: MeteredPollSender::new(
1448 poll_sender,
1449 "network_active_session",
1450 ),
1451 pending_message_to_session: None,
1452 internal_request_rx: ReceiverStream::new(messages_rx).fuse(),
1453 inflight_requests: Default::default(),
1454 conn,
1455 queued_outgoing: QueuedOutgoingMessages::new(
1456 Gauge::noop(),
1457 BroadcastItemCounter::new(),
1458 ),
1459 received_requests_from_remote: Default::default(),
1460 internal_request_timeout_interval: request_timeout_interval(
1461 INITIAL_REQUEST_TIMEOUT,
1462 ),
1463 internal_request_timeout: Arc::new(AtomicU64::new(
1464 INITIAL_REQUEST_TIMEOUT.as_millis() as u64,
1465 )),
1466 protocol_breach_request_timeout: PROTOCOL_BREACH_REQUEST_TIMEOUT,
1467 terminate_message: None,
1468 range_info: None,
1469 local_range_info: BlockRangeInfo::new(
1470 0,
1471 1000,
1472 alloy_primitives::B256::ZERO,
1473 ),
1474 range_update_interval: None,
1475 last_sent_latest_block: None,
1476 }
1477 }
1478 ev => {
1479 panic!("unexpected message {ev:?}")
1480 }
1481 }
1482 }
1483 }
1484
1485 impl Default for SessionBuilder {
1486 fn default() -> Self {
1487 let (active_session_tx, active_session_rx) = mpsc::channel(100);
1488
1489 let (secret_key, pk) = SECP256K1.generate_keypair(&mut rand_08::thread_rng());
1490 let local_peer_id = pk2id(&pk);
1491
1492 Self {
1493 next_id: 0,
1494 _remote_capabilities: Arc::new(Capabilities::from(vec![])),
1495 active_session_tx,
1496 active_session_rx: ReceiverStream::new(active_session_rx),
1497 to_sessions: vec![],
1498 hello: eth_hello(&secret_key),
1499 secret_key,
1500 local_peer_id,
1501 status: StatusBuilder::default().build(),
1502 fork_filter: MAINNET
1503 .hardfork_fork_filter(EthereumHardfork::Frontier)
1504 .expect("The Frontier fork filter should exist on mainnet"),
1505 }
1506 }
1507 }
1508
1509 fn snap_session_builder() -> SessionBuilder {
1513 let mut builder = SessionBuilder::default();
1514 builder.hello.try_add_protocol(Protocol::snap_2()).unwrap();
1515 builder
1516 }
1517
1518 fn dispatch_snap_request(
1521 session: &mut ActiveSession<EthNetworkPrimitives>,
1522 caller_request_id: u64,
1523 ) -> (u64, oneshot::Receiver<RequestResult<SnapResponse>>) {
1524 let (response, rx) = oneshot::channel();
1525 let request = SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
1526 request_id: caller_request_id,
1527 block_hashes: Vec::new(),
1528 response_bytes: 0,
1529 });
1530 let deadline = session.request_deadline();
1531 session.on_internal_peer_request(PeerRequest::GetSnap { request, response }, deadline);
1532 let id = *session.inflight_requests.keys().next().expect("snap request tracked");
1533 (id, rx)
1534 }
1535
1536 #[tokio::test(flavor = "multi_thread")]
1537 async fn test_disconnect() {
1538 let mut builder = SessionBuilder::default();
1539
1540 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1541 let local_addr = listener.local_addr().unwrap();
1542
1543 let expected_disconnect = DisconnectReason::UselessPeer;
1544
1545 let fut = builder.with_client_stream(local_addr, async move |mut client_stream| {
1546 let msg = client_stream.next().await.unwrap().unwrap_err();
1547 assert_eq!(msg.as_disconnected().unwrap(), expected_disconnect);
1548 });
1549
1550 tokio::task::spawn(async move {
1551 let (incoming, _) = listener.accept().await.unwrap();
1552 let mut session = builder.connect_incoming(incoming).await;
1553
1554 session.start_disconnect(expected_disconnect).unwrap();
1555 session.await
1556 });
1557
1558 fut.await;
1559 }
1560
1561 #[tokio::test(flavor = "multi_thread")]
1562 async fn test_invalid_message_disconnects_with_protocol_breach() {
1563 let mut builder = SessionBuilder::default();
1564
1565 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1566 let local_addr = listener.local_addr().unwrap();
1567
1568 let fut = builder.with_client_stream(local_addr, async move |mut client_stream| {
1569 client_stream
1570 .start_send_raw(RawCapabilityMessage::eth(
1571 EthMessageID::PooledTransactions,
1572 vec![0xc0].into(),
1573 ))
1574 .unwrap();
1575 client_stream.flush().await.unwrap();
1576
1577 let msg = client_stream.next().await.unwrap().unwrap_err();
1578 assert_eq!(msg.as_disconnected(), Some(DisconnectReason::ProtocolBreach));
1579 });
1580
1581 let (tx, rx) = oneshot::channel();
1582
1583 tokio::task::spawn(async move {
1584 let (incoming, _) = listener.accept().await.unwrap();
1585 let session = builder.connect_incoming(incoming).await;
1586 session.await;
1587
1588 tx.send(()).unwrap();
1589 });
1590
1591 fut.await;
1592 rx.await.unwrap();
1593 }
1594
1595 #[tokio::test(flavor = "multi_thread")]
1596 async fn handle_dropped_stream() {
1597 let mut builder = SessionBuilder::default();
1598
1599 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1600 let local_addr = listener.local_addr().unwrap();
1601
1602 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1603 drop(client_stream);
1604 tokio::time::sleep(Duration::from_secs(1)).await
1605 });
1606
1607 let (tx, rx) = oneshot::channel();
1608
1609 tokio::task::spawn(async move {
1610 let (incoming, _) = listener.accept().await.unwrap();
1611 let session = builder.connect_incoming(incoming).await;
1612 session.await;
1613
1614 tx.send(()).unwrap();
1615 });
1616
1617 tokio::task::spawn(fut);
1618
1619 rx.await.unwrap();
1620 }
1621
1622 #[tokio::test(flavor = "multi_thread")]
1623 async fn test_send_many_messages() {
1624 reth_tracing::init_test_tracing();
1625 let mut builder = SessionBuilder::default();
1626
1627 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1628 let local_addr = listener.local_addr().unwrap();
1629
1630 let num_messages = 100;
1631
1632 let fut = builder.with_client_stream(local_addr, async move |mut client_stream| {
1633 for _ in 0..num_messages {
1634 client_stream
1635 .send(EthMessage::NewPooledTransactionHashes66(Vec::new().into()))
1636 .await
1637 .unwrap();
1638 }
1639 });
1640
1641 let (tx, rx) = oneshot::channel();
1642
1643 tokio::task::spawn(async move {
1644 let (incoming, _) = listener.accept().await.unwrap();
1645 let session = builder.connect_incoming(incoming).await;
1646 session.await;
1647
1648 tx.send(()).unwrap();
1649 });
1650
1651 tokio::task::spawn(fut);
1652
1653 rx.await.unwrap();
1654 }
1655
1656 #[tokio::test(flavor = "multi_thread")]
1657 async fn test_request_timeout() {
1658 reth_tracing::init_test_tracing();
1659
1660 let mut builder = SessionBuilder::default();
1661
1662 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1663 let local_addr = listener.local_addr().unwrap();
1664
1665 let request_timeout = Duration::from_millis(100);
1666 let drop_timeout = Duration::from_millis(1500);
1667
1668 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1669 let _client_stream = client_stream;
1670 tokio::time::sleep(drop_timeout * 60).await;
1671 });
1672 tokio::task::spawn(fut);
1673
1674 let (incoming, _) = listener.accept().await.unwrap();
1675 let mut session = builder.connect_incoming(incoming).await;
1676 session
1677 .internal_request_timeout
1678 .store(request_timeout.as_millis() as u64, Ordering::Relaxed);
1679 session.protocol_breach_request_timeout = drop_timeout;
1680 session.internal_request_timeout_interval =
1681 tokio::time::interval_at(tokio::time::Instant::now(), request_timeout);
1682 session
1683 .internal_request_timeout_interval
1684 .set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1685 let (tx, rx) = oneshot::channel();
1686 let req = PeerRequest::GetBlockBodies { request: GetBlockBodies(vec![]), response: tx };
1687 session.on_internal_peer_request(req, Instant::now());
1688 tokio::spawn(session);
1689
1690 let err = rx.await.unwrap().unwrap_err();
1691 assert_eq!(err, RequestError::Timeout);
1692
1693 let msg = builder.active_session_rx.next().await.unwrap();
1695 match msg {
1696 ActiveSessionMessage::ProtocolBreach { .. } => {}
1697 ev => unreachable!("{ev:?}"),
1698 }
1699 }
1700
1701 #[tokio::test(flavor = "multi_thread")]
1702 async fn snap_request_is_assigned_unique_id_and_response_correlated() {
1703 let mut builder = snap_session_builder();
1704 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1705 let local_addr = listener.local_addr().unwrap();
1706 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1707 let _client_stream = client_stream;
1708 tokio::time::sleep(Duration::from_secs(60)).await;
1709 });
1710 tokio::task::spawn(fut);
1711 let (incoming, _) = listener.accept().await.unwrap();
1712 let mut session = builder.connect_incoming(incoming).await;
1713
1714 let (id, rx) = dispatch_snap_request(&mut session, u64::MAX);
1716 assert_ne!(id, u64::MAX, "session must assign its own request id");
1717
1718 let outcome = session.on_incoming_snap_message(SnapProtocolMessage::BlockAccessLists(
1720 BlockAccessListsMessage {
1721 request_id: id,
1722 block_access_lists: BlockAccessLists(Vec::new()),
1723 },
1724 ));
1725 assert!(matches!(outcome, OnIncomingMessageOutcome::Ok));
1726 assert!(!session.inflight_requests.contains_key(&id));
1727
1728 let response = rx.await.unwrap().unwrap();
1730 assert!(matches!(
1731 response,
1732 SnapResponse::BlockAccessLists(m) if m.request_id == u64::MAX
1733 ));
1734 }
1735
1736 #[tokio::test(flavor = "multi_thread")]
1737 async fn wrong_type_snap_response_is_rejected() {
1738 let mut builder = snap_session_builder();
1739 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1740 let local_addr = listener.local_addr().unwrap();
1741 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1742 let _client_stream = client_stream;
1743 tokio::time::sleep(Duration::from_secs(60)).await;
1744 });
1745 tokio::task::spawn(fut);
1746 let (incoming, _) = listener.accept().await.unwrap();
1747 let mut session = builder.connect_incoming(incoming).await;
1748
1749 let (id, rx) = dispatch_snap_request(&mut session, 0);
1750
1751 let outcome = session.on_incoming_snap_message(SnapProtocolMessage::AccountRange(
1753 AccountRangeMessage { request_id: id, accounts: Vec::new(), proof: Vec::new() },
1754 ));
1755 assert!(matches!(outcome, OnIncomingMessageOutcome::Ok));
1756 assert!(!session.inflight_requests.contains_key(&id));
1757 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::BadResponse);
1758 assert!(matches!(
1759 builder.active_session_rx.next().await,
1760 Some(ActiveSessionMessage::BadMessage { .. })
1761 ));
1762 }
1763
1764 #[tokio::test(flavor = "multi_thread")]
1765 async fn snap_request_times_out() {
1766 let mut builder = snap_session_builder();
1767 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1768 let local_addr = listener.local_addr().unwrap();
1769 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1770 let _client_stream = client_stream;
1771 tokio::time::sleep(Duration::from_secs(60)).await;
1772 });
1773 tokio::task::spawn(fut);
1774 let (incoming, _) = listener.accept().await.unwrap();
1775 let mut session = builder.connect_incoming(incoming).await;
1776
1777 session.internal_request_timeout.store(1, Ordering::Relaxed);
1779 let (id, rx) = dispatch_snap_request(&mut session, 0);
1780
1781 tokio::time::sleep(Duration::from_millis(20)).await;
1784 assert!(!session.check_timed_out_requests(Instant::now()));
1785 assert!(session.inflight_requests.contains_key(&id));
1786 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::Timeout);
1787
1788 session.protocol_breach_request_timeout = Duration::from_millis(1);
1790 assert!(session.check_timed_out_requests(Instant::now()));
1791 }
1792
1793 #[tokio::test(flavor = "multi_thread")]
1794 async fn late_snap_response_is_consumed_without_penalty() {
1795 let mut builder = snap_session_builder();
1796 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1797 let local_addr = listener.local_addr().unwrap();
1798 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1799 let _client_stream = client_stream;
1800 tokio::time::sleep(Duration::from_secs(60)).await;
1801 });
1802 tokio::task::spawn(fut);
1803 let (incoming, _) = listener.accept().await.unwrap();
1804 let mut session = builder.connect_incoming(incoming).await;
1805
1806 session.internal_request_timeout.store(1, Ordering::Relaxed);
1807 let (id, _rx) = dispatch_snap_request(&mut session, 0);
1808 tokio::time::sleep(Duration::from_millis(20)).await;
1809 assert!(!session.check_timed_out_requests(Instant::now()));
1810
1811 let outcome = session.on_incoming_snap_message(SnapProtocolMessage::BlockAccessLists(
1813 BlockAccessListsMessage {
1814 request_id: id,
1815 block_access_lists: BlockAccessLists(Vec::new()),
1816 },
1817 ));
1818 assert!(matches!(outcome, OnIncomingMessageOutcome::Ok));
1819 assert!(!session.inflight_requests.contains_key(&id));
1820 assert!(futures::FutureExt::now_or_never(builder.active_session_rx.next())
1821 .flatten()
1822 .is_none());
1823 }
1824
1825 #[tokio::test(flavor = "multi_thread")]
1826 async fn unknown_snap_response_is_penalized() {
1827 let mut builder = snap_session_builder();
1828 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1829 let local_addr = listener.local_addr().unwrap();
1830 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1831 let _client_stream = client_stream;
1832 tokio::time::sleep(Duration::from_secs(60)).await;
1833 });
1834 tokio::task::spawn(fut);
1835 let (incoming, _) = listener.accept().await.unwrap();
1836 let mut session = builder.connect_incoming(incoming).await;
1837
1838 let outcome = session.on_incoming_snap_message(SnapProtocolMessage::BlockAccessLists(
1840 BlockAccessListsMessage {
1841 request_id: 999,
1842 block_access_lists: BlockAccessLists(Vec::new()),
1843 },
1844 ));
1845 assert!(matches!(outcome, OnIncomingMessageOutcome::Ok));
1846 assert!(session.inflight_requests.is_empty());
1847 assert!(session.queued_outgoing.pop_front().is_none());
1848 assert!(matches!(
1849 builder.active_session_rx.next().await,
1850 Some(ActiveSessionMessage::BadMessage { .. })
1851 ));
1852 }
1853
1854 #[tokio::test(flavor = "multi_thread")]
1855 async fn get_snap_request_rejected_without_negotiated_snap() {
1856 let mut builder = SessionBuilder::default();
1858 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1859 let local_addr = listener.local_addr().unwrap();
1860 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1861 let _client_stream = client_stream;
1862 tokio::time::sleep(Duration::from_secs(60)).await;
1863 });
1864 tokio::task::spawn(fut);
1865 let (incoming, _) = listener.accept().await.unwrap();
1866 let mut session = builder.connect_incoming(incoming).await;
1867 assert!(!session.conn.supports_snap());
1868
1869 let (response, rx) = oneshot::channel();
1870 let request = SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
1871 request_id: 0,
1872 block_hashes: Vec::new(),
1873 response_bytes: 0,
1874 });
1875 let deadline = session.request_deadline();
1876 session.on_internal_peer_request(PeerRequest::GetSnap { request, response }, deadline);
1877
1878 assert!(session.inflight_requests.is_empty());
1880 assert!(session.queued_outgoing.pop_front().is_none());
1881 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
1882 }
1883
1884 #[tokio::test(flavor = "multi_thread")]
1885 async fn inbound_snap_request_round_trips_to_a_response() {
1886 let mut builder = snap_session_builder();
1887 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1888 let local_addr = listener.local_addr().unwrap();
1889 let fut = builder.with_client_stream(local_addr, async move |client_stream| {
1890 let _client_stream = client_stream;
1891 tokio::time::sleep(Duration::from_secs(60)).await;
1892 });
1893 tokio::task::spawn(fut);
1894 let (incoming, _) = listener.accept().await.unwrap();
1895 let mut session = builder.connect_incoming(incoming).await;
1896
1897 let outcome = session.on_incoming_snap_message(SnapProtocolMessage::GetAccountRange(
1899 GetAccountRangeMessage {
1900 request_id: 7,
1901 root_hash: B256::ZERO,
1902 starting_hash: B256::ZERO,
1903 limit_hash: B256::ZERO,
1904 response_bytes: 1024,
1905 },
1906 ));
1907 assert!(matches!(outcome, OnIncomingMessageOutcome::Ok));
1908 assert_eq!(session.received_requests_from_remote.len(), 1);
1909
1910 let Some(ActiveSessionMessage::ValidMessage {
1912 message: PeerMessage::EthRequest(PeerRequest::GetSnap { request, response }),
1913 ..
1914 }) = builder.active_session_rx.next().await
1915 else {
1916 panic!("expected an outbound GetSnap request")
1917 };
1918 assert!(matches!(request, SnapProtocolMessage::GetAccountRange(_)));
1919
1920 let _ = response.send(Ok(SnapResponse::AccountRange(AccountRangeMessage {
1922 request_id: 7,
1923 accounts: Vec::new(),
1924 proof: Vec::new(),
1925 })));
1926
1927 let mut req = session.received_requests_from_remote.pop().unwrap();
1929 let waker = noop_waker();
1930 let mut cx = Context::from_waker(&waker);
1931 let Poll::Ready(resp) = req.rx.poll(&mut cx) else { panic!("response should be ready") };
1932 session.handle_outgoing_response(req.request_id, resp);
1933
1934 let msg = session.queued_outgoing.pop_front().expect("response queued for send");
1937 assert!(matches!(
1938 msg,
1939 OutgoingMessage::Snap(SnapProtocolMessage::AccountRange(AccountRangeMessage {
1940 request_id: 7,
1941 ..
1942 }))
1943 ));
1944 }
1945
1946 #[test]
1947 fn eth72_pooled_hashes_count_broadcast_items() {
1948 let hashes =
1949 vec![alloy_primitives::B256::repeat_byte(1), alloy_primitives::B256::repeat_byte(2)];
1950 let msg: OutgoingMessage<EthNetworkPrimitives> =
1951 EthMessage::NewPooledTransactionHashes72(NewPooledTransactionHashes72 {
1952 types: vec![0; hashes.len()],
1953 sizes: vec![1; hashes.len()],
1954 hashes,
1955 cell_mask: None,
1956 })
1957 .into();
1958
1959 assert_eq!(2, msg.broadcast_item_count());
1960 }
1961
1962 #[test]
1963 fn test_reject_bal_request_for_eth70() {
1964 let (tx, _rx) = oneshot::channel();
1965 let request: PeerRequest<EthNetworkPrimitives> =
1966 PeerRequest::GetBlockAccessLists { request: GetBlockAccessLists(vec![]), response: tx };
1967
1968 assert!(!ActiveSession::<EthNetworkPrimitives>::is_request_supported_for_version(
1969 &request,
1970 EthVersion::Eth70
1971 ));
1972 assert!(ActiveSession::<EthNetworkPrimitives>::is_request_supported_for_version(
1973 &request,
1974 EthVersion::Eth71
1975 ));
1976 }
1977
1978 #[tokio::test(flavor = "multi_thread")]
1979 async fn test_keep_alive() {
1980 let mut builder = SessionBuilder::default();
1981
1982 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
1983 let local_addr = listener.local_addr().unwrap();
1984
1985 let fut = builder.with_client_stream(local_addr, async move |mut client_stream| {
1986 let _ = tokio::time::timeout(Duration::from_secs(5), client_stream.next()).await;
1987 client_stream.into_inner().disconnect(DisconnectReason::UselessPeer).await.unwrap();
1988 });
1989
1990 let (tx, rx) = oneshot::channel();
1991
1992 tokio::task::spawn(async move {
1993 let (incoming, _) = listener.accept().await.unwrap();
1994 let session = builder.connect_incoming(incoming).await;
1995 session.await;
1996
1997 tx.send(()).unwrap();
1998 });
1999
2000 tokio::task::spawn(fut);
2001
2002 rx.await.unwrap();
2003 }
2004
2005 #[tokio::test(flavor = "multi_thread")]
2007 async fn test_send_at_capacity() {
2008 let mut builder = SessionBuilder::default();
2009
2010 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
2011 let local_addr = listener.local_addr().unwrap();
2012
2013 let fut = builder.with_client_stream(local_addr, async move |mut client_stream| {
2014 client_stream
2015 .send(EthMessage::NewPooledTransactionHashes68(Default::default()))
2016 .await
2017 .unwrap();
2018 let _ = tokio::time::timeout(Duration::from_secs(100), client_stream.next()).await;
2019 });
2020 tokio::task::spawn(fut);
2021
2022 let (incoming, _) = listener.accept().await.unwrap();
2023 let session = builder.connect_incoming(incoming).await;
2024
2025 let mut num_fill_messages = 0;
2027 loop {
2028 if builder
2029 .active_session_tx
2030 .try_send(ActiveSessionMessage::ProtocolBreach { peer_id: PeerId::random() })
2031 .is_err()
2032 {
2033 break
2034 }
2035 num_fill_messages += 1;
2036 }
2037
2038 tokio::task::spawn(async move {
2039 session.await;
2040 });
2041
2042 tokio::time::sleep(Duration::from_millis(100)).await;
2043
2044 for _ in 0..num_fill_messages {
2045 let message = builder.active_session_rx.next().await.unwrap();
2046 match message {
2047 ActiveSessionMessage::ProtocolBreach { .. } => {}
2048 ev => unreachable!("{ev:?}"),
2049 }
2050 }
2051
2052 let message = builder.active_session_rx.next().await.unwrap();
2053 match message {
2054 ActiveSessionMessage::ValidMessage {
2055 message: PeerMessage::PooledTransactions(_),
2056 ..
2057 } => {}
2058 _ => unreachable!(),
2059 }
2060 }
2061
2062 #[test]
2063 fn timeout_calculation_sanity_tests() {
2064 let rtt = Duration::from_secs(5);
2065 let timeout = rtt * TIMEOUT_SCALING;
2067
2068 assert_eq!(calculate_new_timeout(timeout, rtt), timeout);
2070
2071 assert!(calculate_new_timeout(timeout, rtt / 2) < timeout);
2073 assert!(calculate_new_timeout(timeout, rtt / 2) > timeout / 2);
2074 assert!(calculate_new_timeout(timeout, rtt * 2) > timeout);
2075 assert!(calculate_new_timeout(timeout, rtt * 2) < timeout * 2);
2076 }
2077}