1#[cfg(feature = "l2")]
2use crate::rlpx::l2::{
3 PERIODIC_BATCH_BROADCAST_INTERVAL, PERIODIC_BLOCK_BROADCAST_INTERVAL,
4 l2_connection::{
5 self, L2Cast, L2ConnState, handle_based_capability_message, handle_l2_broadcast,
6 },
7};
8use crate::{
9 backend,
10 metrics::METRICS,
11 network::P2PContext,
12 peer_table::{PeerTable, PeerTableServerProtocol as _},
13 rlpx::{
14 Message,
15 connection::{codec::RLPxCodec, handshake},
16 error::PeerConnectionError,
17 eth::{
18 block_access_lists::{BlockAccessLists, GetBlockAccessLists},
19 blocks::{BlockBodies, BlockHeaders},
20 receipts::{
21 GetReceipts68, GetReceipts70, Receipts68, Receipts69, Receipts70,
22 SOFT_RESPONSE_LIMIT,
23 },
24 status::{StatusMessage68, StatusMessage69, StatusMessage70, StatusMessage71},
25 transactions::{GetPooledTransactions, NewPooledTransactionHashes},
26 update::BlockRangeUpdate,
27 },
28 message::EthCapVersion,
29 p2p::{
30 self, Capability, DisconnectMessage, DisconnectReason, PingMessage, PongMessage,
31 SUPPORTED_ETH_CAPABILITIES, SUPPORTED_SNAP_CAPABILITIES,
32 },
33 snap::TrieNodes,
34 },
35 snap::{
36 process_account_range_request, process_byte_codes_request, process_storage_ranges_request,
37 process_trie_nodes_request,
38 },
39 tx_broadcaster::{TxBroadcaster, TxBroadcasterProtocol as _, send_tx_hashes},
40 types::Node,
41};
42use ethrex_blockchain::Blockchain;
43use ethrex_common::H256;
44#[cfg(feature = "l2")]
45use ethrex_common::types::Transaction;
46use ethrex_common::types::{MempoolTransaction, P2PTransaction, Receipt};
47use ethrex_crypto::NativeCrypto;
48use ethrex_rlp::encode::RLPEncode;
49use ethrex_storage::{Store, error::StoreError};
50use ethrex_trie::TrieError;
51use futures::{SinkExt as _, Stream, stream::SplitSink};
52use rand::random;
53use rustc_hash::FxHashMap;
54use secp256k1::{PublicKey, SecretKey};
55use spawned_concurrency::{
56 actor,
57 error::ActorError,
58 protocol,
59 tasks::{Actor, ActorRef, ActorStart as _, Context, Handler, send_interval, spawn_listener},
60};
61use spawned_rt::tasks::BroadcastStream;
62use std::{
63 collections::HashMap,
64 net::SocketAddr,
65 sync::{Arc, RwLock},
66 time::{Duration, Instant},
67};
68use tokio::{
69 net::TcpStream,
70 sync::{broadcast, oneshot},
71 task::{self, Id},
72};
73use tokio_stream::StreamExt;
74use tokio_util::codec::Framed;
75use tracing::{debug, error, trace, warn};
76
77const PING_INTERVAL: Duration = Duration::from_secs(10);
78const BLOCK_RANGE_UPDATE_INTERVAL: Duration = Duration::from_secs(60);
79const INFLIGHT_TX_SWEEP_INTERVAL: Duration = Duration::from_secs(15);
80const INFLIGHT_TX_TIMEOUT: Duration = Duration::from_secs(30);
81const OUTBOUND_SEND_TIMEOUT: Duration = Duration::from_secs(30);
88const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(10);
94const MAX_MISSED_PONGS: u8 = 3;
97const OUTBOUND_QUEUE_CAP: usize = 1024;
101const TX_REQUEST_BATCH_INTERVAL: Duration = Duration::from_millis(50);
104const SERVE_REQUEST_WINDOW: Duration = Duration::from_secs(60);
106const MAX_SERVE_REQUESTS_PER_WINDOW: u64 = 500;
108const LEECH_TX_SENT_THRESHOLD: u64 = 10_000;
110
111pub(crate) type PeerConnBroadcastSender = broadcast::Sender<(tokio::task::Id, Arc<Message>)>;
112
113#[protocol]
114pub trait PeerConnectionServerProtocol: Send + Sync {
115 fn incoming_message(&self, message: Message) -> Result<(), ActorError>;
116 fn outgoing_message(&self, message: Message) -> Result<(), ActorError>;
117 fn outgoing_request(
118 &self,
119 message: Message,
120 sender: Arc<oneshot::Sender<Message>>,
121 ) -> Result<(), ActorError>;
122 fn request_timeout(&self, id: u64) -> Result<(), ActorError>;
123 fn send_ping(&self) -> Result<(), ActorError>;
124 fn block_range_update(&self) -> Result<(), ActorError>;
125 fn broadcast_message(&self, task_id: Id, msg: Arc<Message>) -> Result<(), ActorError>;
126 fn sweep_inflight_txs(&self) -> Result<(), ActorError>;
127 fn flush_pending_tx_requests(&self) -> Result<(), ActorError>;
128 fn enqueue_tx_requests(
129 &self,
130 announcement: NewPooledTransactionHashes,
131 hashes: Vec<H256>,
132 ) -> Result<(), ActorError>;
133}
134
135#[cfg(feature = "l2")]
136#[derive(Clone)]
137pub struct L2Message {
138 pub msg: L2Cast,
139}
140
141#[cfg(feature = "l2")]
142impl spawned_concurrency::message::Message for L2Message {
143 type Result = ();
144}
145
146#[derive(Clone, Debug)]
147pub struct PeerConnection {
148 handle: ActorRef<PeerConnectionServer>,
149}
150
151impl PeerConnection {
152 pub fn spawn_as_receiver(
153 context: P2PContext,
154 peer_addr: SocketAddr,
155 stream: TcpStream,
156 admission_permit: tokio::sync::OwnedSemaphorePermit,
157 ) -> PeerConnection {
158 let state = ConnectionState::Receiver(Receiver {
159 context,
160 peer_addr,
161 stream: Arc::new(stream),
162 });
163 let connection = PeerConnectionServer {
164 state,
165 _admission_permit: Some(admission_permit),
166 };
167 Self {
168 handle: connection.start(),
169 }
170 }
171
172 pub fn spawn_as_initiator(context: P2PContext, node: &Node) -> PeerConnection {
173 let state = ConnectionState::Initiator(Initiator {
174 context,
175 node: node.clone(),
176 });
177 let connection = PeerConnectionServer {
179 state,
180 _admission_permit: None,
181 };
182 Self {
183 handle: connection.start(),
184 }
185 }
186
187 pub async fn outgoing_message(&mut self, message: Message) -> Result<(), PeerConnectionError> {
188 self.handle
189 .outgoing_message(message)
190 .map_err(|err| PeerConnectionError::InternalError(err.to_string()))
191 }
192
193 pub fn enqueue_tx_requests(
197 &self,
198 announcement: NewPooledTransactionHashes,
199 hashes: Vec<H256>,
200 ) -> Result<(), PeerConnectionError> {
201 self.handle
202 .enqueue_tx_requests(announcement, hashes)
203 .map_err(|err| PeerConnectionError::InternalError(err.to_string()))
204 }
205
206 pub async fn outgoing_request(
207 &mut self,
208 message: Message,
209 timeout: Duration,
210 ) -> Result<Message, PeerConnectionError> {
211 let id = message
212 .request_id()
213 .expect("Cannot wait on request without id");
214 let (oneshot_tx, oneshot_rx) = oneshot::channel::<Message>();
215
216 self.handle
217 .outgoing_request(message, Arc::new(oneshot_tx))
218 .map_err(|err| PeerConnectionError::InternalError(err.to_string()))?;
219
220 match tokio::time::timeout(timeout, oneshot_rx).await {
222 Ok(Ok(response)) => Ok(response),
223 Ok(Err(error)) => Err(PeerConnectionError::RecvError(error.to_string())),
224 Err(_timeout) => {
225 self.handle
227 .request_timeout(id)
228 .map_err(|err| PeerConnectionError::InternalError(err.to_string()))?;
229 Err(PeerConnectionError::Timeout)
231 }
232 }
233 }
234}
235
236#[derive(Debug)]
237pub struct Initiator {
238 pub(crate) context: P2PContext,
239 pub(crate) node: Node,
240}
241
242#[derive(Debug)]
243pub struct Receiver {
244 pub(crate) context: P2PContext,
245 pub(crate) peer_addr: SocketAddr,
246 pub(crate) stream: Arc<TcpStream>,
247}
248
249#[derive(Debug)]
250pub struct Established {
251 pub(crate) signer: SecretKey,
252 pub(crate) outbound_tx: tokio::sync::mpsc::Sender<Message>,
257 pub(crate) outbound_writer_timed_out: std::sync::Arc<std::sync::atomic::AtomicBool>,
260 pub(crate) node: Node,
261 pub(crate) storage: Store,
262 pub(crate) blockchain: Arc<Blockchain>,
263 pub(crate) capabilities: Vec<Capability>,
264 pub(crate) negotiated_eth_capability: Option<Capability>,
265 pub(crate) negotiated_snap_capability: Option<Capability>,
266 pub(crate) last_block_range_update_block: u64,
267 pub(crate) requested_pooled_txs: HashMap<u64, (NewPooledTransactionHashes, Vec<H256>, Instant)>,
270 pub(crate) pending_tx_requests: Vec<(NewPooledTransactionHashes, Vec<H256>)>,
273 pub(crate) client_version: String,
274 pub(crate) connection_broadcast_send: PeerConnBroadcastSender,
285 pub(crate) peer_table: PeerTable,
286 #[cfg(feature = "l2")]
287 pub(crate) l2_state: L2ConnState,
288 pub(crate) tx_broadcaster: ActorRef<TxBroadcaster>,
289 pub(crate) current_requests: HashMap<u64, (String, oneshot::Sender<Message>)>,
290 pub(crate) disconnect_reason: Option<DisconnectReason>,
292 pub(crate) is_validated: bool,
294 pub(crate) serve_request_window_start: Instant,
296 pub(crate) serve_requests_in_window: u64,
298 pub(crate) txs_sent_to_peer: u64,
300 pub(crate) received_txs_from_peer: bool,
302 pub(crate) missed_pongs: u8,
306}
307
308impl Established {
309 async fn teardown(&mut self) {
310 for (_, (_announced, requested_hashes, _)) in self.requested_pooled_txs.drain() {
316 if let Err(e) = self
317 .blockchain
318 .mempool
319 .clear_in_flight_txs(&requested_hashes)
320 {
321 warn!(error = %e, "Failed to clear in-flight transaction tracking during peer teardown");
322 }
323 retry_on_alternates(&self.blockchain, &self.peer_table, &requested_hashes).await;
324 }
325 for (_announced, pending_hashes) in self.pending_tx_requests.drain(..) {
327 if let Err(e) = self.blockchain.mempool.clear_in_flight_txs(&pending_hashes) {
328 warn!(error = %e, "Failed to clear in-flight transaction tracking during peer teardown");
329 }
330 retry_on_alternates(&self.blockchain, &self.peer_table, &pending_hashes).await;
331 }
332 }
335}
336
337#[derive(Debug)]
338pub enum ConnectionState {
339 HandshakeFailed,
340 Initiator(Initiator),
341 Receiver(Receiver),
342 Established(Box<Established>),
343}
344
345#[derive(Debug)]
346pub struct PeerConnectionServer {
347 state: ConnectionState,
348 _admission_permit: Option<tokio::sync::OwnedSemaphorePermit>,
352}
353
354#[actor(protocol = PeerConnectionServerProtocol)]
355impl PeerConnectionServer {
356 #[started]
357 async fn started(&mut self, ctx: &Context<Self>) {
358 let eth_version = Arc::new(RwLock::new(EthCapVersion::default()));
361 let state = std::mem::replace(&mut self.state, ConnectionState::HandshakeFailed);
363 let handshake_result = match tokio::time::timeout(
367 HANDSHAKE_TIMEOUT,
368 handshake::perform(state, eth_version.clone()),
369 )
370 .await
371 {
372 Ok(result) => result,
373 Err(_elapsed) => {
374 debug!("Handshake timed out on RLPx connection");
375 self.state = ConnectionState::HandshakeFailed;
376 ctx.stop();
377 return;
378 }
379 };
380 match handshake_result {
381 Ok((mut established_state, stream)) => {
382 trace!(peer=%established_state.node, "Starting RLPx connection");
383 if let Err(reason) =
384 initialize_connection(ctx, &mut established_state, stream, eth_version).await
385 {
386 match &reason {
387 PeerConnectionError::NoMatchingCapabilities
388 | PeerConnectionError::HandshakeError(_) => {
389 if let Err(e) = established_state
390 .peer_table
391 .set_unwanted(established_state.node.node_id())
392 {
393 debug!("Failed to set peer as unwanted: {e}");
394 }
395 }
396 _ => {}
397 }
398 connection_failed(
399 &mut established_state,
400 "Failed to initialize RLPx connection",
401 &reason,
402 )
403 .await;
404
405 METRICS.record_new_rlpx_conn_failure(reason).await;
406
407 self.state = ConnectionState::Established(Box::new(established_state));
408 ctx.stop();
409 } else {
410 METRICS
411 .record_new_rlpx_conn_established(
412 &established_state
413 .node
414 .version
415 .clone()
416 .unwrap_or("Unknown".to_string()),
417 )
418 .await;
419 established_state.is_validated = true;
420 self.state = ConnectionState::Established(Box::new(established_state));
422 }
423 }
424 Err(err) => {
425 debug!("Failed Handshake on RLPx connection {err}");
428 self.state = ConnectionState::HandshakeFailed;
429 ctx.stop();
430 }
431 }
432 }
433
434 #[stopped]
435 async fn stopped(&mut self, _ctx: &Context<Self>) {
436 match std::mem::replace(&mut self.state, ConnectionState::HandshakeFailed) {
437 ConnectionState::Established(mut established_state) => {
438 trace!(peer=%established_state.node, "Closing connection with established peer");
439 if established_state.is_validated {
440 let reason = established_state
442 .disconnect_reason
443 .unwrap_or(DisconnectReason::NetworkError);
444 METRICS
445 .record_new_rlpx_conn_disconnection(
446 &established_state
447 .node
448 .version
449 .clone()
450 .unwrap_or("Unknown".to_string()),
451 reason,
452 )
453 .await;
454 }
455 if let Err(e) = established_state
456 .peer_table
457 .remove_peer(established_state.node.node_id())
458 {
459 debug!("Failed to remove peer from table: {e}");
460 }
461 if let Err(e) = established_state
464 .tx_broadcaster
465 .remove_peer(established_state.node.node_id())
466 {
467 debug!("Failed to remove peer from tx broadcaster: {e}");
468 }
469 established_state.teardown().await;
470 }
471 _ => {
472 }
474 };
475 }
476
477 #[send_handler]
478 async fn handle_incoming_message(
479 &mut self,
480 msg: peer_connection_server_protocol::IncomingMessage,
481 ctx: &Context<Self>,
482 ) {
483 if let ConnectionState::Established(ref mut established_state) = self.state {
484 trace!(
485 peer=%established_state.node,
486 message=%msg.message,
487 "Received incoming message",
488 );
489 let result = handle_incoming_message(established_state, msg.message).await;
490 Self::process_cast_error(&self.state, result, ctx);
491 } else {
492 debug!("Connection not yet established");
493 }
494 }
495
496 #[send_handler]
497 async fn handle_outgoing_message(
498 &mut self,
499 msg: peer_connection_server_protocol::OutgoingMessage,
500 ctx: &Context<Self>,
501 ) {
502 if let ConnectionState::Established(ref mut established_state) = self.state {
503 trace!(
504 peer=%established_state.node,
505 message=%msg.message,
506 "Received outgoing request",
507 );
508 let result = handle_outgoing_message(established_state, msg.message).await;
509 Self::process_cast_error(&self.state, result, ctx);
510 } else {
511 debug!("Connection not yet established");
512 }
513 }
514
515 #[send_handler]
516 async fn handle_outgoing_request(
517 &mut self,
518 msg: peer_connection_server_protocol::OutgoingRequest,
519 ctx: &Context<Self>,
520 ) {
521 if let ConnectionState::Established(ref mut established_state) = self.state {
522 trace!(
523 peer=%established_state.node,
524 message=%msg.message,
525 "Received outgoing request",
526 );
527 let Some(sender) = Arc::<oneshot::Sender<Message>>::into_inner(msg.sender) else {
528 debug!("Could not obtain sender channel: Arc has multiple references");
529 return;
530 };
531 let result = handle_outgoing_request(established_state, msg.message, sender).await;
532 Self::process_cast_error(&self.state, result, ctx);
533 } else {
534 debug!("Connection not yet established");
535 }
536 }
537
538 #[send_handler]
539 async fn handle_request_timeout(
540 &mut self,
541 msg: peer_connection_server_protocol::RequestTimeout,
542 _ctx: &Context<Self>,
543 ) {
544 if let ConnectionState::Established(ref mut established_state) = self.state {
545 if let Some((msg_type, _)) = established_state.current_requests.remove(&msg.id) {
547 debug!(
548 peer=%established_state.node,
549 %msg_type,
550 id=%msg.id,
551 "Request timedout",
552 );
553 }
554 } else {
555 debug!("Connection not yet established");
556 }
557 }
558
559 #[send_handler]
560 async fn handle_send_ping(
561 &mut self,
562 _msg: peer_connection_server_protocol::SendPing,
563 ctx: &Context<Self>,
564 ) {
565 if let ConnectionState::Established(ref mut established_state) = self.state {
566 if established_state.missed_pongs >= MAX_MISSED_PONGS {
569 debug!(peer=%established_state.node, missed=MAX_MISSED_PONGS, "Peer missed max pongs, dropping");
570 established_state.disconnect_reason = Some(DisconnectReason::PingTimeout);
573 ctx.stop();
574 return;
575 }
576 established_state.missed_pongs = established_state.missed_pongs.saturating_add(1);
577 let result = send(established_state, Message::Ping(PingMessage {})).await;
578 Self::process_cast_error(&self.state, result, ctx);
579 } else {
580 debug!("Connection not yet established");
581 }
582 }
583
584 #[send_handler]
585 async fn handle_block_range_update(
586 &mut self,
587 _msg: peer_connection_server_protocol::BlockRangeUpdate,
588 ctx: &Context<Self>,
589 ) {
590 if let ConnectionState::Established(ref mut established_state) = self.state {
591 trace!(
592 peer=%established_state.node,
593 "Block Range Update"
594 );
595 let result = handle_block_range_update(established_state).await;
596 Self::process_cast_error(&self.state, result, ctx);
597 } else {
598 debug!("Connection not yet established");
599 }
600 }
601
602 #[send_handler]
603 async fn handle_sweep_inflight_txs(
604 &mut self,
605 _msg: peer_connection_server_protocol::SweepInflightTxs,
606 _ctx: &Context<Self>,
607 ) {
608 if let ConnectionState::Established(ref mut state) = self.state {
609 let now = Instant::now();
610 let stale_ids: Vec<u64> = state
611 .requested_pooled_txs
612 .iter()
613 .filter(|(_, (_, _, ts))| now.duration_since(*ts) > INFLIGHT_TX_TIMEOUT)
614 .map(|(id, _)| *id)
615 .collect();
616 for id in stale_ids {
617 if let Some((_announced, hashes, _)) = state.requested_pooled_txs.remove(&id) {
618 if let Err(e) = state.blockchain.mempool.clear_in_flight_txs(&hashes) {
621 warn!(error = %e, "Failed to clear in-flight transaction tracking while sweeping stale requests");
622 }
623 retry_on_alternates(&state.blockchain, &state.peer_table, &hashes).await;
624 }
625 }
626 }
627 }
628
629 #[send_handler]
630 async fn handle_flush_pending_tx_requests(
631 &mut self,
632 _msg: peer_connection_server_protocol::FlushPendingTxRequests,
633 ctx: &Context<Self>,
634 ) {
635 if let ConnectionState::Established(ref mut established_state) = self.state {
636 let result = flush_pending_tx_requests(established_state).await;
637 Self::process_cast_error(&self.state, result, ctx);
638 }
639 }
640
641 #[send_handler]
642 async fn handle_enqueue_tx_requests(
643 &mut self,
644 msg: peer_connection_server_protocol::EnqueueTxRequests,
645 _ctx: &Context<Self>,
646 ) {
647 if let ConnectionState::Established(ref mut state) = self.state {
648 let to_request: Vec<H256> = match state.blockchain.mempool.reserve_unknown_hashes(
651 &msg.announcement.transaction_hashes,
652 &msg.announcement.transaction_types,
653 &msg.announcement.transaction_sizes,
654 state.node.node_id(),
655 ) {
656 Ok(unknown) => unknown,
657 Err(_) => return,
658 };
659 if to_request.is_empty() {
660 return;
661 }
662 let trimmed = msg.announcement.filter_to(&to_request);
663 state.pending_tx_requests.push((trimmed, to_request));
664 }
665 }
666
667 #[send_handler]
668 async fn handle_broadcast_message(
669 &mut self,
670 msg: peer_connection_server_protocol::BroadcastMessage,
671 ctx: &Context<Self>,
672 ) {
673 if let ConnectionState::Established(ref mut established_state) = self.state {
674 trace!(
675 peer=%established_state.node,
676 message=%msg.msg,
677 "Received broadcasted message",
678 );
679 let result = handle_broadcast(established_state, (msg.task_id, msg.msg)).await;
680 Self::process_cast_error(&self.state, result, ctx);
681 } else {
682 debug!("Connection not yet established");
683 }
684 }
685
686 #[cfg(feature = "l2")]
687 #[send_handler]
688 async fn handle_l2_message(&mut self, msg: L2Message, ctx: &Context<Self>) {
689 if let ConnectionState::Established(ref mut established_state) = self.state {
690 let peer_supports_l2 = established_state.l2_state.connection_state().is_ok();
691 let result = if peer_supports_l2 {
692 trace!(
693 peer=%established_state.node,
694 message=?msg.msg,
695 "Handling cast for L2 msg"
696 );
697 match msg.msg {
698 L2Cast::BatchBroadcast => {
699 let res = l2_connection::send_sealed_batch(established_state).await;
700 res.and(l2_connection::process_batches_on_queue(established_state).await)
701 }
702 L2Cast::BlockBroadcast => {
703 let res = l2_connection::send_new_block(established_state).await;
704 res.and(l2_connection::process_blocks_on_queue(established_state).await)
705 }
706 }
707 } else {
708 Err(PeerConnectionError::MessageNotHandled(
709 "Unknown message or capability not handled".to_string(),
710 ))
711 };
712 Self::process_cast_error(&self.state, result, ctx);
713 } else {
714 debug!("Connection not yet established");
715 }
716 }
717
718 fn process_cast_error(
719 state: &ConnectionState,
720 result: Result<(), PeerConnectionError>,
721 ctx: &Context<Self>,
722 ) {
723 if let Err(e) = result
724 && let ConnectionState::Established(established_state) = state
725 {
726 match e {
727 PeerConnectionError::Disconnected
728 | PeerConnectionError::DisconnectReceived(_)
729 | PeerConnectionError::DisconnectSent(_)
730 | PeerConnectionError::HandshakeError(_)
731 | PeerConnectionError::NoMatchingCapabilities
732 | PeerConnectionError::InvalidPeerId
733 | PeerConnectionError::InvalidMessageLength
734 | PeerConnectionError::StateError(_)
735 | PeerConnectionError::InvalidRecoveryId
736 | PeerConnectionError::OutboundSendTimeout
737 | PeerConnectionError::OutboundQueueFull => {
738 trace!(peer=%established_state.node, error=e.to_string(), "Peer connection error");
739 ctx.stop();
740 }
741 PeerConnectionError::IoError(ref io_e)
742 if io_e.kind() == std::io::ErrorKind::BrokenPipe =>
743 {
744 debug!(peer=%established_state.node, "Broken pipe with peer, disconnected");
747 ctx.stop();
748 }
749 PeerConnectionError::StoreError(StoreError::Trie(TrieError::InconsistentTree(
750 _,
751 ))) => {
752 if established_state.blockchain.is_synced() {
753 error!(
756 peer=%established_state.node,
757 error=%e,
758 "Inconsistent trie while serving peer request; local state may be corrupted",
759 );
760 } else {
761 trace!(
763 peer=%established_state.node,
764 error=%e,
765 "Error handling cast message",
766 );
767 }
768 }
769 _ => {
770 debug!(
772 peer=%established_state.node,
773 capabilities=?established_state.capabilities,
774 error=%e,
775 "Error handling cast message",
776 );
777 }
778 }
779 }
780 }
781}
782
783async fn initialize_connection<S>(
784 ctx: &Context<PeerConnectionServer>,
785 state: &mut Established,
786 mut stream: S,
787 eth_version: Arc<RwLock<EthCapVersion>>,
788) -> Result<(), PeerConnectionError>
789where
790 S: Unpin + Send + Stream<Item = Result<Message, PeerConnectionError>> + 'static,
791{
792 if state.peer_table.target_peers_reached().await? {
793 debug!(peer=%state.node, "Reached target peer connections, discarding.");
794 return Err(PeerConnectionError::TooManyPeers);
795 }
796 exchange_hello_messages(state, &mut stream).await?;
797
798 let version = match &state.negotiated_eth_capability {
800 Some(cap) if cap == &Capability::eth(68) => EthCapVersion::V68,
801 Some(cap) if cap == &Capability::eth(69) => EthCapVersion::V69,
802 Some(cap) if cap == &Capability::eth(70) => EthCapVersion::V70,
803 Some(cap) if cap == &Capability::eth(71) => EthCapVersion::V71,
804 _ => EthCapVersion::default(),
805 };
806 *eth_version
807 .write()
808 .map_err(|err| PeerConnectionError::InternalError(err.to_string()))? = version;
809
810 init_capabilities(state, &mut stream).await?;
811
812 let mut connection = PeerConnection {
813 handle: ctx.actor_ref(),
814 };
815
816 state.peer_table.new_connected_peer(
817 state.node.clone(),
818 connection.clone(),
819 state.capabilities.clone(),
820 )?;
821
822 trace!(peer=%state.node, "Peer connection initialized.");
823
824 send_all_pooled_tx_hashes(state, &mut connection).await?;
826
827 send_interval(
829 PING_INTERVAL,
830 ctx.clone(),
831 peer_connection_server_protocol::SendPing,
832 );
833
834 send_interval(
836 BLOCK_RANGE_UPDATE_INTERVAL,
837 ctx.clone(),
838 peer_connection_server_protocol::BlockRangeUpdate,
839 );
840
841 send_interval(
843 INFLIGHT_TX_SWEEP_INTERVAL,
844 ctx.clone(),
845 peer_connection_server_protocol::SweepInflightTxs,
846 );
847
848 send_interval(
850 TX_REQUEST_BATCH_INTERVAL,
851 ctx.clone(),
852 peer_connection_server_protocol::FlushPendingTxRequests,
853 );
854
855 #[cfg(feature = "l2")]
856 if state.l2_state.connection_state().is_ok() {
858 send_interval(
859 PERIODIC_BLOCK_BROADCAST_INTERVAL,
860 ctx.clone(),
861 L2Message {
862 msg: L2Cast::BlockBroadcast,
863 },
864 );
865 send_interval(
866 PERIODIC_BATCH_BROADCAST_INTERVAL,
867 ctx.clone(),
868 L2Message {
869 msg: L2Cast::BatchBroadcast,
870 },
871 );
872 }
873
874 spawn_listener(
875 ctx.clone(),
876 stream.filter_map(|result| match result {
877 Ok(msg) => Some(peer_connection_server_protocol::IncomingMessage { message: msg }),
878 Err(e) => {
879 debug!(error=?e, "Error receiving RLPx message");
880 None
882 }
883 }),
884 );
885
886 if state.negotiated_eth_capability.is_some() {
887 let stream: BroadcastStream<(Id, Arc<Message>)> =
888 BroadcastStream::new(state.connection_broadcast_send.subscribe());
889 let message_stream = stream.filter_map(|result| {
890 result.ok().map(
891 |(id, msg)| peer_connection_server_protocol::BroadcastMessage { task_id: id, msg },
892 )
893 });
894 spawn_listener(ctx.clone(), message_stream);
895 }
896
897 Ok(())
898}
899
900async fn send_all_pooled_tx_hashes(
901 state: &mut Established,
902 connection: &mut PeerConnection,
903) -> Result<(), PeerConnectionError> {
904 let txs: Vec<MempoolTransaction> = state
905 .blockchain
906 .mempool
907 .get_all_txs_by_sender()?
908 .into_values()
909 .flatten()
910 .filter(|tx| !tx.is_privileged())
911 .collect();
912 if !txs.is_empty() {
913 state
914 .tx_broadcaster
915 .add_txs(
916 txs.iter().map(|tx| tx.hash(&NativeCrypto)).collect(),
917 state.node.node_id(),
918 )
919 .map_err(|e| PeerConnectionError::BroadcastError(e.to_string()))?;
920 send_tx_hashes(
921 txs,
922 state.capabilities.clone(),
923 connection,
924 state.node.node_id(),
925 &state.blockchain,
926 )
927 .await
928 .map_err(|e| PeerConnectionError::SendMessage(e.to_string()))?;
929 }
930 Ok(())
931}
932
933async fn send_block_range_update(state: &mut Established) -> Result<(), PeerConnectionError> {
934 if state
936 .negotiated_eth_capability
937 .as_ref()
938 .is_some_and(|eth| eth.version >= 69)
939 {
940 trace!(peer=%state.node, "Sending BlockRangeUpdate");
941 let update = BlockRangeUpdate::new(&state.storage).await?;
942 let lastet_block = update.latest_block;
943 send(state, Message::BlockRangeUpdate(update)).await?;
944 state.last_block_range_update_block = lastet_block - (lastet_block % 32);
945 }
946 Ok(())
947}
948
949async fn should_send_block_range_update(state: &Established) -> Result<bool, PeerConnectionError> {
950 let latest_block = state.storage.get_latest_block_number().await?;
951 if latest_block < state.last_block_range_update_block
952 || latest_block - state.last_block_range_update_block >= 32
953 {
954 return Ok(true);
955 }
956 Ok(false)
957}
958
959async fn init_capabilities<S>(
960 state: &mut Established,
961 stream: &mut S,
962) -> Result<(), PeerConnectionError>
963where
964 S: Unpin + Stream<Item = Result<Message, PeerConnectionError>>,
965{
966 if let Some(eth) = state.negotiated_eth_capability.clone() {
968 let status = match eth.version {
969 68 => Message::Status68(StatusMessage68::new(&state.storage).await?),
970 69 => Message::Status69(StatusMessage69::new(&state.storage).await?),
971 70 => Message::Status70(StatusMessage70::new(&state.storage).await?),
972 71 => Message::Status71(StatusMessage71::new(&state.storage).await?),
973 ver => {
974 return Err(PeerConnectionError::HandshakeError(format!(
975 "Invalid eth version {ver}"
976 )));
977 }
978 };
979 trace!(peer=%state.node, "Sending status");
980 send(state, status).await?;
981 let msg = match receive(stream).await {
985 Some(msg) => msg?,
986 None => return Err(PeerConnectionError::Disconnected),
987 };
988 match msg {
989 Message::Status68(msg_data) => {
990 trace!(peer=%state.node, "Received Status(68)");
991 backend::validate_status(msg_data, &state.storage, ð).await?
992 }
993 Message::Status69(msg_data) => {
994 trace!(peer=%state.node, "Received Status(69)");
995 backend::validate_status(msg_data, &state.storage, ð).await?
996 }
997 Message::Status70(msg_data) => {
998 trace!(peer=%state.node, "Received Status(70)");
999 backend::validate_status(msg_data, &state.storage, ð).await?
1000 }
1001 Message::Status71(msg_data) => {
1002 trace!(peer=%state.node, "Received Status(71)");
1003 backend::validate_status(msg_data, &state.storage, ð).await?
1004 }
1005 Message::Disconnect(disconnect) => {
1006 return Err(PeerConnectionError::HandshakeError(format!(
1007 "Peer disconnected due to: {}",
1008 disconnect.reason()
1009 )));
1010 }
1011 _ => {
1012 return Err(PeerConnectionError::HandshakeError(
1013 "Expected a Status message".to_string(),
1014 ));
1015 }
1016 }
1017 }
1018 Ok(())
1019}
1020
1021async fn send_disconnect_message(state: &mut Established, reason: Option<DisconnectReason>) {
1022 send(state, Message::Disconnect(DisconnectMessage { reason }))
1023 .await
1024 .unwrap_or_else(|_| {
1025 debug!(
1026 peer=%state.node,
1027 ?reason,
1028 "Could not send Disconnect message",
1029 );
1030 });
1031}
1032
1033async fn connection_failed(state: &mut Established, error_text: &str, error: &PeerConnectionError) {
1034 debug!(
1035 peer=%state.node,
1036 %error_text,
1037 %error,
1038 "connection failure"
1039 );
1040
1041 if !matches!(error, PeerConnectionError::DisconnectReceived(_)) {
1044 send_disconnect_message(state, match_disconnect_reason(error)).await;
1045 }
1046
1047 match error {
1049 PeerConnectionError::DisconnectReceived(DisconnectReason::AlreadyConnected)
1051 | PeerConnectionError::DisconnectSent(DisconnectReason::AlreadyConnected) => {
1052 debug!(
1053 peer=%state.node,
1054 %error_text,
1055 %error,
1056 "Peer already connected, don't replace it"
1057 );
1058 }
1059 _ => {
1060 debug!(
1061 peer=%state.node,
1062 %error_text,
1063 %error,
1064 remote_public_key=%state.node.public_key,
1065 "discarding peer",
1066 );
1067 }
1068 }
1069}
1070
1071fn match_disconnect_reason(error: &PeerConnectionError) -> Option<DisconnectReason> {
1072 match error {
1073 PeerConnectionError::DisconnectSent(reason) => Some(*reason),
1074 PeerConnectionError::DisconnectReceived(reason) => Some(*reason),
1075 PeerConnectionError::RLPDecodeError(_) => Some(DisconnectReason::NetworkError),
1076 PeerConnectionError::TooManyPeers => Some(DisconnectReason::TooManyPeers),
1077 _ => None,
1079 }
1080}
1081
1082async fn exchange_hello_messages<S>(
1083 state: &mut Established,
1084 stream: &mut S,
1085) -> Result<(), PeerConnectionError>
1086where
1087 S: Unpin + Stream<Item = Result<Message, PeerConnectionError>>,
1088{
1089 #[allow(unused_mut)]
1092 let mut supported_capabilities: Vec<Capability> = [
1093 &SUPPORTED_ETH_CAPABILITIES[..],
1094 &SUPPORTED_SNAP_CAPABILITIES[..],
1095 ]
1096 .concat();
1097 #[cfg(feature = "l2")]
1098 if state.l2_state.is_supported() {
1099 supported_capabilities.push(crate::rlpx::l2::SUPPORTED_BASED_CAPABILITIES[0].clone());
1100 }
1101 let hello_msg = Message::Hello(p2p::HelloMessage::new(
1102 supported_capabilities,
1103 PublicKey::from_secret_key(secp256k1::SECP256K1, &state.signer),
1104 state.client_version.clone(),
1105 ));
1106
1107 send(state, hello_msg).await?;
1108
1109 let msg = match receive(stream).await {
1111 Some(msg) => msg?,
1112 None => return Err(PeerConnectionError::Disconnected),
1113 };
1114
1115 match msg {
1116 Message::Hello(hello_message) => {
1117 let mut negotiated_eth_version = 0;
1118 let mut negotiated_snap_version = 0;
1119
1120 trace!(
1121 peer=%state.node,
1122 capabilities=?hello_message.capabilities,
1123 "Hello message capabilities",
1124 );
1125
1126 for cap in &hello_message.capabilities {
1128 match cap.protocol() {
1129 "eth" => {
1130 if SUPPORTED_ETH_CAPABILITIES.contains(cap)
1131 && cap.version > negotiated_eth_version
1132 {
1133 negotiated_eth_version = cap.version;
1134 }
1135 }
1136 "snap" => {
1137 if SUPPORTED_SNAP_CAPABILITIES.contains(cap)
1138 && cap.version > negotiated_snap_version
1139 {
1140 negotiated_snap_version = cap.version;
1141 }
1142 }
1143 #[cfg(feature = "l2")]
1144 "based" if state.l2_state.is_supported() => {
1145 state.l2_state.set_established()?;
1146 }
1147 _ => {}
1148 }
1149 }
1150
1151 state.capabilities = hello_message.capabilities;
1152
1153 if negotiated_eth_version == 0 {
1154 return Err(PeerConnectionError::NoMatchingCapabilities);
1155 }
1156 debug!("Negotiated eth version: eth/{}", negotiated_eth_version);
1157 state.negotiated_eth_capability = Some(Capability::eth(negotiated_eth_version));
1158
1159 if negotiated_snap_version != 0 {
1160 debug!("Negotiated snap version: snap/{}", negotiated_snap_version);
1161 state.negotiated_snap_capability = Some(Capability::snap(negotiated_snap_version));
1162 }
1163
1164 state.node.version = Some(hello_message.client_id);
1165
1166 Ok(())
1167 }
1168 Message::Disconnect(disconnect) => {
1169 Err(PeerConnectionError::DisconnectReceived(disconnect.reason()))
1170 }
1171 _ => {
1172 Err(PeerConnectionError::BadRequest(
1174 "Expected Hello message".to_string(),
1175 ))
1176 }
1177 }
1178}
1179
1180pub(crate) async fn send(
1181 state: &mut Established,
1182 message: Message,
1183) -> Result<(), PeerConnectionError> {
1184 #[cfg(feature = "metrics")]
1185 {
1186 use ethrex_metrics::p2p::METRICS_P2P;
1187 METRICS_P2P.inc_outgoing_message(message.metric_label());
1188 }
1189 match state.outbound_tx.try_send(message) {
1194 Ok(()) => Ok(()),
1195 Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
1196 state
1200 .disconnect_reason
1201 .get_or_insert(DisconnectReason::UselessPeer);
1202 Err(PeerConnectionError::OutboundQueueFull)
1203 }
1204 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
1205 if state
1209 .outbound_writer_timed_out
1210 .load(std::sync::atomic::Ordering::Acquire)
1211 {
1212 state
1213 .disconnect_reason
1214 .get_or_insert(DisconnectReason::UselessPeer);
1215 Err(PeerConnectionError::OutboundSendTimeout)
1216 } else {
1217 Err(PeerConnectionError::Disconnected)
1218 }
1219 }
1220 }
1221}
1222
1223pub(crate) fn spawn_outbound_writer(
1230 mut sink: SplitSink<Framed<TcpStream, RLPxCodec>, Message>,
1231) -> (
1232 tokio::sync::mpsc::Sender<Message>,
1233 std::sync::Arc<std::sync::atomic::AtomicBool>,
1234) {
1235 let (tx, mut rx) = tokio::sync::mpsc::channel::<Message>(OUTBOUND_QUEUE_CAP);
1236 let timed_out = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
1240 let writer_timed_out = timed_out.clone();
1241 tokio::spawn(async move {
1242 while let Some(message) = rx.recv().await {
1243 match tokio::time::timeout(OUTBOUND_SEND_TIMEOUT, sink.send(message)).await {
1244 Ok(Ok(())) => {}
1245 Ok(Err(_)) => break,
1247 Err(_) => {
1249 writer_timed_out.store(true, std::sync::atomic::Ordering::Release);
1250 break;
1251 }
1252 }
1253 }
1254 let _ = sink.close().await;
1255 });
1256 (tx, timed_out)
1257}
1258
1259async fn receive<S>(stream: &mut S) -> Option<Result<Message, PeerConnectionError>>
1271where
1272 S: Unpin + Stream<Item = Result<Message, PeerConnectionError>>,
1273{
1274 stream.next().await
1275}
1276
1277fn check_serve_request_rate(state: &mut Established) -> bool {
1280 let now = Instant::now();
1281 if now.duration_since(state.serve_request_window_start) >= SERVE_REQUEST_WINDOW {
1282 state.serve_request_window_start = now;
1283 state.serve_requests_in_window = 0;
1284 }
1285 state.serve_requests_in_window += 1;
1286 state.serve_requests_in_window <= MAX_SERVE_REQUESTS_PER_WINDOW
1287}
1288
1289async fn handle_incoming_message(
1290 state: &mut Established,
1291 message: Message,
1292) -> Result<(), PeerConnectionError> {
1293 #[cfg(feature = "metrics")]
1294 {
1295 use ethrex_metrics::p2p::METRICS_P2P;
1296 METRICS_P2P.inc_incoming_message(message.metric_label());
1297 }
1298
1299 let is_data_request = matches!(
1301 message,
1302 Message::GetBlockHeaders(_)
1303 | Message::GetBlockBodies(_)
1304 | Message::GetReceipts68(_)
1305 | Message::GetReceipts69(_)
1306 | Message::GetReceipts70(_)
1307 | Message::GetPooledTransactions(_)
1308 | Message::GetAccountRange(_)
1309 | Message::GetStorageRanges(_)
1310 | Message::GetByteCodes(_)
1311 | Message::GetTrieNodes(_)
1312 );
1313 if is_data_request && !check_serve_request_rate(state) {
1314 debug!(
1315 peer = %state.node,
1316 window_requests = state.serve_requests_in_window,
1317 "Disconnecting peer: exceeded incoming request rate limit",
1318 );
1319 send_disconnect_message(state, Some(DisconnectReason::UselessPeer)).await;
1320 return Err(PeerConnectionError::DisconnectSent(
1321 DisconnectReason::UselessPeer,
1322 ));
1323 }
1324
1325 let peer_supports_eth = state.negotiated_eth_capability.is_some();
1326 #[cfg(feature = "l2")]
1327 let peer_supports_l2 = state.l2_state.connection_state().is_ok();
1328 match message {
1329 Message::Disconnect(msg_data) => {
1330 let reason = msg_data.reason();
1331 trace!(
1332 peer=%state.node,
1333 ?reason,
1334 "Received Disconnect"
1335 );
1336 state.disconnect_reason = Some(reason);
1337
1338 return Err(PeerConnectionError::DisconnectReceived(reason));
1341 }
1342 Message::Ping(_) => {
1343 trace!(peer=%state.node, "Sending pong message");
1344 send(state, Message::Pong(PongMessage {})).await?;
1345 }
1346 Message::Pong(_) => {
1347 state.missed_pongs = 0;
1349 }
1350 Message::Status68(msg_data) => {
1351 if let Some(eth) = &state.negotiated_eth_capability {
1352 backend::validate_status(msg_data, &state.storage, eth).await?
1353 };
1354 }
1355 Message::Status69(msg_data) => {
1356 if let Some(eth) = &state.negotiated_eth_capability {
1357 backend::validate_status(msg_data, &state.storage, eth).await?
1358 };
1359 }
1360 Message::Status70(msg_data) => {
1361 if let Some(eth) = &state.negotiated_eth_capability {
1362 backend::validate_status(msg_data, &state.storage, eth).await?
1363 };
1364 }
1365 Message::Status71(msg_data) => {
1366 if let Some(eth) = &state.negotiated_eth_capability {
1367 backend::validate_status(msg_data, &state.storage, eth).await?
1368 };
1369 }
1370 Message::GetAccountRange(req) => {
1371 let response = process_account_range_request(req, state.storage.clone()).await?;
1372 send(state, Message::AccountRange(response)).await?
1373 }
1374 Message::Transactions(txs) if peer_supports_eth => {
1375 if !txs.transactions.is_empty() {
1377 state.received_txs_from_peer = true;
1378 }
1379 if state.blockchain.is_synced() {
1380 let tx_hashes: Vec<_> = txs
1381 .transactions
1382 .iter()
1383 .map(|tx| tx.hash(&NativeCrypto))
1384 .collect();
1385
1386 let blockchain = state.blockchain.clone();
1389 let peer = state.node.to_string();
1390 #[cfg(feature = "l2")]
1391 let is_l2_mode = state.l2_state.is_supported();
1392 tokio::spawn(async move {
1393 for tx in txs.transactions {
1394 #[cfg(feature = "l2")]
1395 if (is_l2_mode && matches!(tx, Transaction::EIP4844Transaction(_)))
1396 || tx.is_privileged()
1397 {
1398 let tx_type = tx.tx_type();
1399 debug!(peer=%peer, "Rejecting transaction in L2 mode - {tx_type} transactions are not broadcasted in L2");
1400 continue;
1401 }
1402
1403 if let Err(e) = blockchain.add_transaction_to_pool(tx).await {
1404 debug!(
1405 peer=%peer,
1406 error=%e,
1407 "Error adding transaction"
1408 );
1409 }
1410 }
1411 });
1412
1413 state
1417 .tx_broadcaster
1418 .add_txs(tx_hashes, state.node.node_id())
1419 .map_err(|e| PeerConnectionError::BroadcastError(e.to_string()))?;
1420 }
1421 }
1422 Message::GetBlockHeaders(msg_data) if peer_supports_eth => {
1423 let response = BlockHeaders {
1424 id: msg_data.id,
1425 block_headers: msg_data.fetch_headers(&state.storage).await,
1426 };
1427 send(state, Message::BlockHeaders(response)).await?;
1428 }
1429 Message::GetBlockBodies(msg_data) if peer_supports_eth => {
1430 let response = BlockBodies {
1431 id: msg_data.id,
1432 block_bodies: msg_data.fetch_blocks(&state.storage).await,
1433 };
1434 send(state, Message::BlockBodies(response)).await?;
1435 }
1436 Message::GetBlockAccessLists(GetBlockAccessLists { id, block_hashes })
1437 if peer_supports_eth =>
1438 {
1439 use crate::rlpx::eth::block_access_lists::BLOCK_ACCESS_LIST_LIMIT;
1440 let mut block_access_lists =
1441 Vec::with_capacity(block_hashes.len().min(BLOCK_ACCESS_LIST_LIMIT));
1442 for hash in &block_hashes {
1443 let bal = match state.storage.get_block_access_list(*hash) {
1449 Ok(Some(bal)) => {
1450 let commitment = match state.storage.get_block_header_by_hash(*hash) {
1451 Ok(Some(header)) => header.block_access_list_hash,
1452 Ok(None) => None,
1453 Err(err) => {
1454 warn!(
1458 "Failed to read header for BAL commitment check (hash {hash:#x}): {err}; reporting BAL unavailable"
1459 );
1460 None
1461 }
1462 };
1463 bal.matches_commitment(commitment, &NativeCrypto)
1464 .then_some(bal)
1465 }
1466 Ok(None) => None,
1467 Err(err) => {
1468 error!("Error accessing DB while building BAL response for peer: {err}");
1469 None
1470 }
1471 };
1472 block_access_lists.push(bal);
1473 if block_access_lists.len() >= BLOCK_ACCESS_LIST_LIMIT {
1474 break;
1475 }
1476 }
1477 let response = BlockAccessLists::new(id, block_access_lists);
1478 send(state, Message::BlockAccessLists(response)).await?;
1479 }
1480 Message::GetReceipts68(GetReceipts68 { id, block_hashes }) if peer_supports_eth => {
1481 let mut receipts = Vec::new();
1482 for hash in block_hashes.iter() {
1483 receipts.push(state.storage.get_receipts_for_block(hash).await?);
1484 }
1485 send(state, Message::Receipts68(Receipts68::new(id, receipts))).await?;
1486 }
1487 Message::GetReceipts69(GetReceipts68 { id, block_hashes }) if peer_supports_eth => {
1488 let mut receipts = Vec::new();
1489 for hash in block_hashes.iter() {
1490 receipts.push(state.storage.get_receipts_for_block(hash).await?);
1491 }
1492 send(state, Message::Receipts69(Receipts69::new(id, receipts))).await?;
1493 }
1494 Message::GetReceipts70(GetReceipts70 {
1496 id,
1497 first_block_receipt_index,
1498 block_hashes,
1499 }) if peer_supports_eth => {
1500 let block_hashes = &block_hashes[..block_hashes.len().min(256)];
1501 let mut all_receipts: Vec<Vec<Receipt>> = Vec::new();
1502 let mut total_size: usize = 0;
1503 let mut last_block_incomplete = false;
1504
1505 for (i, hash) in block_hashes.iter().enumerate() {
1506 let start_index = if i == 0 { first_block_receipt_index } else { 0 };
1507 let block_receipts = state
1508 .storage
1509 .get_receipts_for_block_from_index(hash, start_index, None)
1510 .await?;
1511
1512 let mut block_receipt_list = Vec::new();
1513 let mut hit_limit = false;
1514 for receipt in block_receipts {
1515 let receipt_size = receipt.length();
1516 if total_size + receipt_size > SOFT_RESPONSE_LIMIT
1517 && (!block_receipt_list.is_empty() || !all_receipts.is_empty())
1518 {
1519 hit_limit = true;
1520 if !block_receipt_list.is_empty() {
1526 last_block_incomplete = true;
1527 }
1528 break;
1529 }
1530 total_size += receipt_size;
1531 block_receipt_list.push(receipt);
1532 }
1533
1534 if !block_receipt_list.is_empty() || !hit_limit {
1539 all_receipts.push(block_receipt_list);
1540 }
1541
1542 if hit_limit {
1543 break;
1544 }
1545 }
1546
1547 let response =
1548 Message::Receipts70(Receipts70::new(id, last_block_incomplete, all_receipts));
1549 send(state, response).await?;
1550 }
1551 Message::BlockRangeUpdate(update) => {
1552 trace!(
1553 peer=%state.node,
1554 range_from=update.earliest_block,
1555 range_to=update.latest_block,
1556 "Block range update",
1557 );
1558 if let Err(err) = update.validate() {
1560 debug!(
1561 peer=%state.node,
1562 reason=%err,
1563 "Disconnecting peer: invalid block range update",
1564 );
1565 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError)).await;
1566 return Err(PeerConnectionError::DisconnectSent(
1567 DisconnectReason::SubprotocolError,
1568 ));
1569 }
1570 }
1571 Message::NewPooledTransactionHashes(new_pooled_transaction_hashes) if peer_supports_eth => {
1572 if state.blockchain.is_synced() {
1574 let hashes = new_pooled_transaction_hashes
1575 .get_transactions_to_request(&state.blockchain, state.node.node_id())?;
1576 if !hashes.is_empty() {
1577 state
1580 .pending_tx_requests
1581 .push((new_pooled_transaction_hashes, hashes));
1582 }
1583 }
1584 }
1585 Message::GetPooledTransactions(msg) => {
1586 let response = msg.handle(&state.blockchain)?;
1587 let batch_size = response.pooled_transactions.len() as u64;
1588 if state.txs_sent_to_peer + batch_size > LEECH_TX_SENT_THRESHOLD
1590 && !state.received_txs_from_peer
1591 {
1592 debug!(
1593 peer = %state.node,
1594 txs_sent = state.txs_sent_to_peer,
1595 "Disconnecting peer: leech detected (sent many txs but received none)",
1596 );
1597 send_disconnect_message(state, Some(DisconnectReason::UselessPeer)).await;
1598 return Err(PeerConnectionError::DisconnectSent(
1599 DisconnectReason::UselessPeer,
1600 ));
1601 }
1602 send(state, Message::PooledTransactions(response)).await?;
1603 state.txs_sent_to_peer += batch_size;
1604 }
1605 Message::PooledTransactions(msg) if peer_supports_eth => {
1606 if !msg.pooled_transactions.is_empty() {
1607 state.received_txs_from_peer = true;
1608 }
1609 let removed_request = state.requested_pooled_txs.remove(&msg.id);
1612 if let Some((_, ref requested_hashes, _)) = removed_request {
1613 state
1614 .blockchain
1615 .mempool
1616 .clear_in_flight_txs(requested_hashes)?;
1617 }
1618 for tx in &msg.pooled_transactions {
1620 if let P2PTransaction::EIP4844TransactionWithBlobs(itx) = tx
1621 && (itx.blobs_bundle.is_empty()
1622 || itx
1623 .blobs_bundle
1624 .validate_blob_commitment_hashes(&itx.tx.blob_versioned_hashes)
1625 .is_err())
1626 {
1627 debug!(
1628 peer=%state.node,
1629 "Disconnecting peer: invalid or missing blobs",
1630 );
1631 if let Some((_announced, requested_hashes, _)) = &removed_request {
1632 retry_on_alternates(&state.blockchain, &state.peer_table, requested_hashes)
1633 .await;
1634 }
1635 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError)).await;
1636 return Err(PeerConnectionError::DisconnectSent(
1637 DisconnectReason::SubprotocolError,
1638 ));
1639 }
1640 }
1641 if state.blockchain.is_synced() {
1642 if let Some((announced, requested_hashes, _)) = &removed_request {
1643 let fork = state.blockchain.current_fork().await?;
1644 if let Err(error) = msg.validate_requested(announced, fork) {
1645 debug!(
1646 peer=%state.node,
1647 reason=%error,
1648 "Disconnecting peer: invalid pooled transactions response",
1649 );
1650 retry_on_alternates(&state.blockchain, &state.peer_table, requested_hashes)
1651 .await;
1652 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError))
1653 .await;
1654 return Err(PeerConnectionError::DisconnectSent(
1655 DisconnectReason::SubprotocolError,
1656 ));
1657 }
1658 }
1659 #[cfg(feature = "l2")]
1660 let is_l2_mode = state.l2_state.is_supported();
1661
1662 #[cfg(not(feature = "l2"))]
1663 let is_l2_mode = false;
1664 if let Err(error) = msg.handle(&state.node, &state.blockchain, is_l2_mode).await {
1665 if matches!(
1666 error,
1667 ethrex_blockchain::error::MempoolError::BlobsBundleError(_)
1668 ) {
1669 debug!(
1670 peer=%state.node,
1671 reason=%error,
1672 "Disconnecting peer: invalid pooled transactions response",
1673 );
1674 if let Some((_announced, requested_hashes, _)) = &removed_request {
1675 retry_on_alternates(
1676 &state.blockchain,
1677 &state.peer_table,
1678 requested_hashes,
1679 )
1680 .await;
1681 }
1682 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError))
1683 .await;
1684 return Err(PeerConnectionError::DisconnectSent(
1685 DisconnectReason::SubprotocolError,
1686 ));
1687 }
1688 return Err(error.into());
1689 }
1690 }
1691 }
1692 Message::GetStorageRanges(req) => {
1693 let response = process_storage_ranges_request(req, state.storage.clone()).await?;
1694 send(state, Message::StorageRanges(response)).await?
1695 }
1696 Message::GetByteCodes(req) => {
1697 let storage_clone = state.storage.clone();
1698 let response = process_byte_codes_request(req, storage_clone)
1699 .await
1700 .map_err(|_| {
1701 PeerConnectionError::InternalError(
1702 "Failed to execute bytecode retrieval task".to_string(),
1703 )
1704 })?;
1705 send(state, Message::ByteCodes(response)).await?
1706 }
1707 Message::GetTrieNodes(req) => {
1708 let id = req.id;
1709 match process_trie_nodes_request(req, state.storage.clone()).await {
1710 Ok(response) => send(state, Message::TrieNodes(response)).await?,
1711 Err(_) => send(state, Message::TrieNodes(TrieNodes { id, nodes: vec![] })).await?,
1712 }
1713 }
1714 #[cfg(feature = "l2")]
1715 Message::L2(req) if peer_supports_l2 => {
1716 handle_based_capability_message(state, req).await?;
1717 }
1718 message @ Message::AccountRange(_)
1720 | message @ Message::StorageRanges(_)
1721 | message @ Message::ByteCodes(_)
1722 | message @ Message::TrieNodes(_)
1723 | message @ Message::BlockBodies(_)
1724 | message @ Message::BlockHeaders(_)
1725 | message @ Message::Receipts68(_)
1726 | message @ Message::Receipts69(_)
1727 | message @ Message::Receipts70(_)
1728 | message @ Message::BlockAccessLists(_) => {
1729 if let Some((_, tx)) = message
1730 .request_id()
1731 .and_then(|id| state.current_requests.remove(&id))
1732 {
1733 tx.send(message)
1734 .map_err(|e| PeerConnectionError::SendMessage(e.to_string()))?
1735 } else {
1736 return Err(PeerConnectionError::ExpectedRequestId(format!("{message}")));
1737 }
1738 }
1739 message => return Err(PeerConnectionError::MessageNotHandled(format!("{message}"))),
1741 };
1742 Ok(())
1743}
1744
1745async fn handle_outgoing_message(
1746 state: &mut Established,
1747 message: Message,
1748) -> Result<(), PeerConnectionError> {
1749 trace!(
1750 peer=%state.node,
1751 %message,
1752 "Sending message"
1753 );
1754 send(state, message).await?;
1755 Ok(())
1756}
1757
1758async fn handle_outgoing_request(
1759 state: &mut Established,
1760 message: Message,
1761 sender: oneshot::Sender<Message>,
1762) -> Result<(), PeerConnectionError> {
1763 message.request_id().and_then(|id| {
1765 state
1766 .current_requests
1767 .insert(id, (format!("{message}"), sender))
1768 });
1769 trace!(
1770 peer=%state.node,
1771 %message,
1772 "Sending request"
1773 );
1774 send(state, message).await?;
1775 Ok(())
1776}
1777
1778async fn handle_broadcast(
1779 state: &mut Established,
1780 (id, broadcasted_msg): (task::Id, Arc<Message>),
1781) -> Result<(), PeerConnectionError> {
1782 if id != tokio::task::id() {
1783 match broadcasted_msg.as_ref() {
1784 #[cfg(feature = "l2")]
1785 l2_msg @ Message::L2(_) => {
1786 handle_l2_broadcast(state, l2_msg).await?;
1787 }
1788 msg => {
1789 error!(
1790 peer=%state.node,
1791 message=%msg,
1792 "Non-supported message broadcasted"
1793 );
1794 let error_message = format!("Non-supported message broadcasted: {msg}");
1795 return Err(PeerConnectionError::BroadcastError(error_message));
1796 }
1797 }
1798 }
1799 Ok(())
1800}
1801
1802async fn handle_block_range_update(state: &mut Established) -> Result<(), PeerConnectionError> {
1803 if should_send_block_range_update(state).await? {
1804 send_block_range_update(state).await
1805 } else {
1806 Ok(())
1807 }
1808}
1809
1810async fn flush_pending_tx_requests(state: &mut Established) -> Result<(), PeerConnectionError> {
1814 if state.pending_tx_requests.is_empty() {
1815 return Ok(());
1816 }
1817
1818 let pending = std::mem::take(&mut state.pending_tx_requests);
1819
1820 let mut all_hashes: Vec<H256> = Vec::new();
1823 let mut all_types: Vec<u8> = Vec::new();
1824 let mut all_sizes: Vec<usize> = Vec::new();
1825
1826 for (announcement, hashes) in &pending {
1827 let trimmed = announcement.filter_to(hashes);
1828 all_hashes.extend_from_slice(&trimmed.transaction_hashes);
1829 all_types.extend_from_slice(&trimmed.transaction_types);
1830 all_sizes.extend(trimmed.transaction_sizes);
1831 }
1832
1833 const MAX_HASHES_PER_REQUEST: usize = 256;
1835 for (i, chunk) in all_hashes.chunks(MAX_HASHES_PER_REQUEST).enumerate() {
1836 let offset = i * MAX_HASHES_PER_REQUEST;
1837 let chunk_types = &all_types[offset..offset + chunk.len()];
1838 let chunk_sizes = &all_sizes[offset..offset + chunk.len()];
1839
1840 let announcement = NewPooledTransactionHashes::from_raw(
1841 chunk_types.to_vec().into(),
1842 chunk_sizes.to_vec(),
1843 chunk.to_vec(),
1844 );
1845 let request = GetPooledTransactions::new(random(), chunk.to_vec());
1846 let request_id = request.id;
1847 if let Err(e) = send(state, Message::GetPooledTransactions(request)).await {
1850 let unsent = &all_hashes[offset..];
1856 if !unsent.is_empty() {
1857 if let Err(clear_err) = state.blockchain.mempool.clear_in_flight_txs(unsent) {
1858 warn!(error = %clear_err, "Failed to clear in-flight transaction tracking after send error");
1859 }
1860 retry_on_alternates(&state.blockchain, &state.peer_table, unsent).await;
1861 }
1862 return Err(e);
1863 }
1864 state
1865 .requested_pooled_txs
1866 .insert(request_id, (announcement, chunk.to_vec(), Instant::now()));
1867 }
1868
1869 Ok(())
1870}
1871
1872async fn retry_on_alternates(
1884 blockchain: &Arc<Blockchain>,
1885 peer_table: &PeerTable,
1886 hashes: &[H256],
1887) {
1888 if hashes.is_empty() {
1889 return;
1890 }
1891 type AltGroup = (PeerConnection, Vec<(H256, u8, usize)>);
1898 let mut by_peer: FxHashMap<H256, AltGroup> = FxHashMap::default();
1899 for hash in hashes {
1900 loop {
1901 let alt = match blockchain.mempool.pop_alternate(*hash) {
1902 Ok(Some(a)) => a,
1903 Ok(None) => break,
1904 Err(e) => {
1905 warn!(error = %e, "pop_alternate failed");
1906 break;
1907 }
1908 };
1909 if let Some((_, list)) = by_peer.get_mut(&alt.peer_id) {
1911 list.push((*hash, alt.tx_type, alt.tx_size));
1912 break;
1913 }
1914 match peer_table.get_peer_connection(alt.peer_id).await {
1915 Ok(Some(conn)) => {
1916 by_peer.insert(alt.peer_id, (conn, vec![(*hash, alt.tx_type, alt.tx_size)]));
1917 break;
1918 }
1919 Ok(None) => continue, Err(e) => {
1921 warn!(error = %e, "get_peer_connection failed");
1922 break;
1923 }
1924 }
1925 }
1926 }
1927
1928 for (_, (conn, entries)) in by_peer {
1929 let mut types = Vec::with_capacity(entries.len());
1930 let mut sizes = Vec::with_capacity(entries.len());
1931 let mut hash_list = Vec::with_capacity(entries.len());
1932 for (h, t, s) in &entries {
1933 hash_list.push(*h);
1934 types.push(*t);
1935 sizes.push(*s);
1936 }
1937 let announcement =
1938 NewPooledTransactionHashes::from_raw(types.into(), sizes, hash_list.clone());
1939 if let Err(e) = conn.enqueue_tx_requests(announcement, hash_list) {
1940 debug!(error = %e, "Failed to enqueue tx requests on alternate peer");
1941 }
1942 }
1943}