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) is_inbound: bool,
264 pub(crate) storage: Store,
265 pub(crate) blockchain: Arc<Blockchain>,
266 pub(crate) capabilities: Vec<Capability>,
267 pub(crate) negotiated_eth_capability: Option<Capability>,
268 pub(crate) negotiated_snap_capability: Option<Capability>,
269 pub(crate) last_block_range_update_block: u64,
270 pub(crate) requested_pooled_txs: HashMap<u64, (NewPooledTransactionHashes, Vec<H256>, Instant)>,
273 pub(crate) pending_tx_requests: Vec<(NewPooledTransactionHashes, Vec<H256>)>,
276 pub(crate) client_version: String,
277 pub(crate) connection_broadcast_send: PeerConnBroadcastSender,
288 pub(crate) peer_table: PeerTable,
289 #[cfg(feature = "l2")]
290 pub(crate) l2_state: L2ConnState,
291 pub(crate) tx_broadcaster: ActorRef<TxBroadcaster>,
292 pub(crate) current_requests: HashMap<u64, (String, oneshot::Sender<Message>)>,
293 pub(crate) disconnect_reason: Option<DisconnectReason>,
295 pub(crate) is_validated: bool,
297 pub(crate) serve_request_window_start: Instant,
299 pub(crate) serve_requests_in_window: u64,
301 pub(crate) txs_sent_to_peer: u64,
303 pub(crate) received_txs_from_peer: bool,
305 pub(crate) missed_pongs: u8,
309}
310
311impl Established {
312 async fn teardown(&mut self) {
313 for (_, (_announced, requested_hashes, _)) in self.requested_pooled_txs.drain() {
319 if let Err(e) = self
320 .blockchain
321 .mempool
322 .clear_in_flight_txs(&requested_hashes)
323 {
324 warn!(error = %e, "Failed to clear in-flight transaction tracking during peer teardown");
325 }
326 retry_on_alternates(&self.blockchain, &self.peer_table, &requested_hashes).await;
327 }
328 for (_announced, pending_hashes) in self.pending_tx_requests.drain(..) {
330 if let Err(e) = self.blockchain.mempool.clear_in_flight_txs(&pending_hashes) {
331 warn!(error = %e, "Failed to clear in-flight transaction tracking during peer teardown");
332 }
333 retry_on_alternates(&self.blockchain, &self.peer_table, &pending_hashes).await;
334 }
335 }
338}
339
340#[derive(Debug)]
341pub enum ConnectionState {
342 HandshakeFailed,
343 Initiator(Initiator),
344 Receiver(Receiver),
345 Established(Box<Established>),
346}
347
348#[derive(Debug)]
349pub struct PeerConnectionServer {
350 state: ConnectionState,
351 _admission_permit: Option<tokio::sync::OwnedSemaphorePermit>,
355}
356
357#[actor(protocol = PeerConnectionServerProtocol)]
358impl PeerConnectionServer {
359 #[started]
360 async fn started(&mut self, ctx: &Context<Self>) {
361 let eth_version = Arc::new(RwLock::new(EthCapVersion::default()));
364 let state = std::mem::replace(&mut self.state, ConnectionState::HandshakeFailed);
366 let handshake_result = match tokio::time::timeout(
370 HANDSHAKE_TIMEOUT,
371 handshake::perform(state, eth_version.clone()),
372 )
373 .await
374 {
375 Ok(result) => result,
376 Err(_elapsed) => {
377 debug!("Handshake timed out on RLPx connection");
378 self.state = ConnectionState::HandshakeFailed;
379 ctx.stop();
380 return;
381 }
382 };
383 match handshake_result {
384 Ok((mut established_state, stream)) => {
385 trace!(peer=%established_state.node, "Starting RLPx connection");
386 if let Err(reason) =
387 initialize_connection(ctx, &mut established_state, stream, eth_version).await
388 {
389 match &reason {
390 PeerConnectionError::NoMatchingCapabilities
391 | PeerConnectionError::HandshakeError(_) => {
392 if let Err(e) = established_state
393 .peer_table
394 .set_unwanted(established_state.node.node_id())
395 {
396 debug!("Failed to set peer as unwanted: {e}");
397 }
398 }
399 _ => {}
400 }
401 connection_failed(
402 &mut established_state,
403 "Failed to initialize RLPx connection",
404 &reason,
405 )
406 .await;
407
408 METRICS.record_new_rlpx_conn_failure(reason).await;
409
410 self.state = ConnectionState::Established(Box::new(established_state));
411 ctx.stop();
412 } else {
413 METRICS
414 .record_new_rlpx_conn_established(
415 &established_state
416 .node
417 .version
418 .clone()
419 .unwrap_or("Unknown".to_string()),
420 )
421 .await;
422 established_state.is_validated = true;
423 self.state = ConnectionState::Established(Box::new(established_state));
425 }
426 }
427 Err(err) => {
428 debug!("Failed Handshake on RLPx connection {err}");
431 self.state = ConnectionState::HandshakeFailed;
432 ctx.stop();
433 }
434 }
435 }
436
437 #[stopped]
438 async fn stopped(&mut self, _ctx: &Context<Self>) {
439 match std::mem::replace(&mut self.state, ConnectionState::HandshakeFailed) {
440 ConnectionState::Established(mut established_state) => {
441 trace!(peer=%established_state.node, "Closing connection with established peer");
442 if established_state.is_validated {
443 let reason = established_state
445 .disconnect_reason
446 .unwrap_or(DisconnectReason::NetworkError);
447 METRICS
448 .record_new_rlpx_conn_disconnection(
449 &established_state
450 .node
451 .version
452 .clone()
453 .unwrap_or("Unknown".to_string()),
454 reason,
455 )
456 .await;
457 }
458 if let Err(e) = established_state
459 .peer_table
460 .remove_peer(established_state.node.node_id())
461 {
462 debug!("Failed to remove peer from table: {e}");
463 }
464 if let Err(e) = established_state
467 .tx_broadcaster
468 .remove_peer(established_state.node.node_id())
469 {
470 debug!("Failed to remove peer from tx broadcaster: {e}");
471 }
472 established_state.teardown().await;
473 }
474 _ => {
475 }
477 };
478 }
479
480 #[send_handler]
481 async fn handle_incoming_message(
482 &mut self,
483 msg: peer_connection_server_protocol::IncomingMessage,
484 ctx: &Context<Self>,
485 ) {
486 if let ConnectionState::Established(ref mut established_state) = self.state {
487 trace!(
488 peer=%established_state.node,
489 message=%msg.message,
490 "Received incoming message",
491 );
492 let result = handle_incoming_message(established_state, msg.message).await;
493 Self::process_cast_error(&self.state, result, ctx);
494 } else {
495 debug!("Connection not yet established");
496 }
497 }
498
499 #[send_handler]
500 async fn handle_outgoing_message(
501 &mut self,
502 msg: peer_connection_server_protocol::OutgoingMessage,
503 ctx: &Context<Self>,
504 ) {
505 if let ConnectionState::Established(ref mut established_state) = self.state {
506 trace!(
507 peer=%established_state.node,
508 message=%msg.message,
509 "Received outgoing request",
510 );
511 let result = handle_outgoing_message(established_state, msg.message).await;
512 Self::process_cast_error(&self.state, result, ctx);
513 } else {
514 debug!("Connection not yet established");
515 }
516 }
517
518 #[send_handler]
519 async fn handle_outgoing_request(
520 &mut self,
521 msg: peer_connection_server_protocol::OutgoingRequest,
522 ctx: &Context<Self>,
523 ) {
524 if let ConnectionState::Established(ref mut established_state) = self.state {
525 trace!(
526 peer=%established_state.node,
527 message=%msg.message,
528 "Received outgoing request",
529 );
530 let Some(sender) = Arc::<oneshot::Sender<Message>>::into_inner(msg.sender) else {
531 debug!("Could not obtain sender channel: Arc has multiple references");
532 return;
533 };
534 let result = handle_outgoing_request(established_state, msg.message, sender).await;
535 Self::process_cast_error(&self.state, result, ctx);
536 } else {
537 debug!("Connection not yet established");
538 }
539 }
540
541 #[send_handler]
542 async fn handle_request_timeout(
543 &mut self,
544 msg: peer_connection_server_protocol::RequestTimeout,
545 _ctx: &Context<Self>,
546 ) {
547 if let ConnectionState::Established(ref mut established_state) = self.state {
548 if let Some((msg_type, _)) = established_state.current_requests.remove(&msg.id) {
550 debug!(
551 peer=%established_state.node,
552 %msg_type,
553 id=%msg.id,
554 "Request timedout",
555 );
556 }
557 } else {
558 debug!("Connection not yet established");
559 }
560 }
561
562 #[send_handler]
563 async fn handle_send_ping(
564 &mut self,
565 _msg: peer_connection_server_protocol::SendPing,
566 ctx: &Context<Self>,
567 ) {
568 if let ConnectionState::Established(ref mut established_state) = self.state {
569 if established_state.missed_pongs >= MAX_MISSED_PONGS {
572 debug!(peer=%established_state.node, missed=MAX_MISSED_PONGS, "Peer missed max pongs, dropping");
573 established_state.disconnect_reason = Some(DisconnectReason::PingTimeout);
576 ctx.stop();
577 return;
578 }
579 established_state.missed_pongs = established_state.missed_pongs.saturating_add(1);
580 let result = send(established_state, Message::Ping(PingMessage {})).await;
581 Self::process_cast_error(&self.state, result, ctx);
582 } else {
583 debug!("Connection not yet established");
584 }
585 }
586
587 #[send_handler]
588 async fn handle_block_range_update(
589 &mut self,
590 _msg: peer_connection_server_protocol::BlockRangeUpdate,
591 ctx: &Context<Self>,
592 ) {
593 if let ConnectionState::Established(ref mut established_state) = self.state {
594 trace!(
595 peer=%established_state.node,
596 "Block Range Update"
597 );
598 let result = handle_block_range_update(established_state).await;
599 Self::process_cast_error(&self.state, result, ctx);
600 } else {
601 debug!("Connection not yet established");
602 }
603 }
604
605 #[send_handler]
606 async fn handle_sweep_inflight_txs(
607 &mut self,
608 _msg: peer_connection_server_protocol::SweepInflightTxs,
609 _ctx: &Context<Self>,
610 ) {
611 if let ConnectionState::Established(ref mut state) = self.state {
612 let now = Instant::now();
613 let stale_ids: Vec<u64> = state
614 .requested_pooled_txs
615 .iter()
616 .filter(|(_, (_, _, ts))| now.duration_since(*ts) > INFLIGHT_TX_TIMEOUT)
617 .map(|(id, _)| *id)
618 .collect();
619 for id in stale_ids {
620 if let Some((_announced, hashes, _)) = state.requested_pooled_txs.remove(&id) {
621 if let Err(e) = state.blockchain.mempool.clear_in_flight_txs(&hashes) {
624 warn!(error = %e, "Failed to clear in-flight transaction tracking while sweeping stale requests");
625 }
626 retry_on_alternates(&state.blockchain, &state.peer_table, &hashes).await;
627 }
628 }
629 }
630 }
631
632 #[send_handler]
633 async fn handle_flush_pending_tx_requests(
634 &mut self,
635 _msg: peer_connection_server_protocol::FlushPendingTxRequests,
636 ctx: &Context<Self>,
637 ) {
638 if let ConnectionState::Established(ref mut established_state) = self.state {
639 let result = flush_pending_tx_requests(established_state).await;
640 Self::process_cast_error(&self.state, result, ctx);
641 }
642 }
643
644 #[send_handler]
645 async fn handle_enqueue_tx_requests(
646 &mut self,
647 msg: peer_connection_server_protocol::EnqueueTxRequests,
648 _ctx: &Context<Self>,
649 ) {
650 if let ConnectionState::Established(ref mut state) = self.state {
651 let to_request: Vec<H256> = match state.blockchain.mempool.reserve_unknown_hashes(
654 &msg.announcement.transaction_hashes,
655 &msg.announcement.transaction_types,
656 &msg.announcement.transaction_sizes,
657 state.node.node_id(),
658 ) {
659 Ok(unknown) => unknown,
660 Err(_) => return,
661 };
662 if to_request.is_empty() {
663 return;
664 }
665 let trimmed = msg.announcement.filter_to(&to_request);
666 state.pending_tx_requests.push((trimmed, to_request));
667 }
668 }
669
670 #[send_handler]
671 async fn handle_broadcast_message(
672 &mut self,
673 msg: peer_connection_server_protocol::BroadcastMessage,
674 ctx: &Context<Self>,
675 ) {
676 if let ConnectionState::Established(ref mut established_state) = self.state {
677 trace!(
678 peer=%established_state.node,
679 message=%msg.msg,
680 "Received broadcasted message",
681 );
682 let result = handle_broadcast(established_state, (msg.task_id, msg.msg)).await;
683 Self::process_cast_error(&self.state, result, ctx);
684 } else {
685 debug!("Connection not yet established");
686 }
687 }
688
689 #[cfg(feature = "l2")]
690 #[send_handler]
691 async fn handle_l2_message(&mut self, msg: L2Message, ctx: &Context<Self>) {
692 if let ConnectionState::Established(ref mut established_state) = self.state {
693 let peer_supports_l2 = established_state.l2_state.connection_state().is_ok();
694 let result = if peer_supports_l2 {
695 trace!(
696 peer=%established_state.node,
697 message=?msg.msg,
698 "Handling cast for L2 msg"
699 );
700 match msg.msg {
701 L2Cast::BatchBroadcast => {
702 let res = l2_connection::send_sealed_batch(established_state).await;
703 res.and(l2_connection::process_batches_on_queue(established_state).await)
704 }
705 L2Cast::BlockBroadcast => {
706 let res = l2_connection::send_new_block(established_state).await;
707 res.and(l2_connection::process_blocks_on_queue(established_state).await)
708 }
709 }
710 } else {
711 Err(PeerConnectionError::MessageNotHandled(
712 "Unknown message or capability not handled".to_string(),
713 ))
714 };
715 Self::process_cast_error(&self.state, result, ctx);
716 } else {
717 debug!("Connection not yet established");
718 }
719 }
720
721 fn process_cast_error(
722 state: &ConnectionState,
723 result: Result<(), PeerConnectionError>,
724 ctx: &Context<Self>,
725 ) {
726 if let Err(e) = result
727 && let ConnectionState::Established(established_state) = state
728 {
729 match e {
730 PeerConnectionError::Disconnected
731 | PeerConnectionError::DisconnectReceived(_)
732 | PeerConnectionError::DisconnectSent(_)
733 | PeerConnectionError::HandshakeError(_)
734 | PeerConnectionError::NoMatchingCapabilities
735 | PeerConnectionError::InvalidPeerId
736 | PeerConnectionError::InvalidMessageLength
737 | PeerConnectionError::StateError(_)
738 | PeerConnectionError::InvalidRecoveryId
739 | PeerConnectionError::OutboundSendTimeout
740 | PeerConnectionError::OutboundQueueFull => {
741 trace!(peer=%established_state.node, error=e.to_string(), "Peer connection error");
742 ctx.stop();
743 }
744 PeerConnectionError::IoError(ref io_e)
745 if io_e.kind() == std::io::ErrorKind::BrokenPipe =>
746 {
747 debug!(peer=%established_state.node, "Broken pipe with peer, disconnected");
750 ctx.stop();
751 }
752 PeerConnectionError::StoreError(StoreError::Trie(TrieError::InconsistentTree(
753 _,
754 ))) => {
755 if established_state.blockchain.is_synced() {
756 error!(
759 peer=%established_state.node,
760 error=%e,
761 "Inconsistent trie while serving peer request; local state may be corrupted",
762 );
763 } else {
764 trace!(
766 peer=%established_state.node,
767 error=%e,
768 "Error handling cast message",
769 );
770 }
771 }
772 _ => {
773 debug!(
775 peer=%established_state.node,
776 capabilities=?established_state.capabilities,
777 error=%e,
778 "Error handling cast message",
779 );
780 }
781 }
782 }
783 }
784}
785
786async fn initialize_connection<S>(
787 ctx: &Context<PeerConnectionServer>,
788 state: &mut Established,
789 mut stream: S,
790 eth_version: Arc<RwLock<EthCapVersion>>,
791) -> Result<(), PeerConnectionError>
792where
793 S: Unpin + Send + Stream<Item = Result<Message, PeerConnectionError>> + 'static,
794{
795 if state.peer_table.target_peers_reached().await? {
796 debug!(peer=%state.node, "Reached target peer connections, discarding.");
797 return Err(PeerConnectionError::TooManyPeers);
798 }
799 exchange_hello_messages(state, &mut stream).await?;
800
801 let version = match &state.negotiated_eth_capability {
803 Some(cap) if cap == &Capability::eth(68) => EthCapVersion::V68,
804 Some(cap) if cap == &Capability::eth(69) => EthCapVersion::V69,
805 Some(cap) if cap == &Capability::eth(70) => EthCapVersion::V70,
806 Some(cap) if cap == &Capability::eth(71) => EthCapVersion::V71,
807 _ => EthCapVersion::default(),
808 };
809 *eth_version
810 .write()
811 .map_err(|err| PeerConnectionError::InternalError(err.to_string()))? = version;
812
813 init_capabilities(state, &mut stream).await?;
814
815 let mut connection = PeerConnection {
816 handle: ctx.actor_ref(),
817 };
818
819 state.peer_table.new_connected_peer(
820 state.node.clone(),
821 connection.clone(),
822 state.capabilities.clone(),
823 state.is_inbound,
824 )?;
825
826 trace!(peer=%state.node, "Peer connection initialized.");
827
828 send_all_pooled_tx_hashes(state, &mut connection).await?;
830
831 send_interval(
833 PING_INTERVAL,
834 ctx.clone(),
835 peer_connection_server_protocol::SendPing,
836 );
837
838 send_interval(
840 BLOCK_RANGE_UPDATE_INTERVAL,
841 ctx.clone(),
842 peer_connection_server_protocol::BlockRangeUpdate,
843 );
844
845 send_interval(
847 INFLIGHT_TX_SWEEP_INTERVAL,
848 ctx.clone(),
849 peer_connection_server_protocol::SweepInflightTxs,
850 );
851
852 send_interval(
854 TX_REQUEST_BATCH_INTERVAL,
855 ctx.clone(),
856 peer_connection_server_protocol::FlushPendingTxRequests,
857 );
858
859 #[cfg(feature = "l2")]
860 if state.l2_state.connection_state().is_ok() {
862 send_interval(
863 PERIODIC_BLOCK_BROADCAST_INTERVAL,
864 ctx.clone(),
865 L2Message {
866 msg: L2Cast::BlockBroadcast,
867 },
868 );
869 send_interval(
870 PERIODIC_BATCH_BROADCAST_INTERVAL,
871 ctx.clone(),
872 L2Message {
873 msg: L2Cast::BatchBroadcast,
874 },
875 );
876 }
877
878 spawn_listener(
879 ctx.clone(),
880 stream.filter_map(|result| match result {
881 Ok(msg) => Some(peer_connection_server_protocol::IncomingMessage { message: msg }),
882 Err(e) => {
883 debug!(error=?e, "Error receiving RLPx message");
884 None
886 }
887 }),
888 );
889
890 if state.negotiated_eth_capability.is_some() {
891 let stream: BroadcastStream<(Id, Arc<Message>)> =
892 BroadcastStream::new(state.connection_broadcast_send.subscribe());
893 let message_stream = stream.filter_map(|result| {
894 result.ok().map(
895 |(id, msg)| peer_connection_server_protocol::BroadcastMessage { task_id: id, msg },
896 )
897 });
898 spawn_listener(ctx.clone(), message_stream);
899 }
900
901 Ok(())
902}
903
904async fn send_all_pooled_tx_hashes(
905 state: &mut Established,
906 connection: &mut PeerConnection,
907) -> Result<(), PeerConnectionError> {
908 let txs: Vec<MempoolTransaction> = state
909 .blockchain
910 .mempool
911 .get_all_txs_by_sender()?
912 .into_values()
913 .flatten()
914 .filter(|tx| !tx.is_privileged())
915 .collect();
916 if !txs.is_empty() {
917 state
918 .tx_broadcaster
919 .add_txs(
920 txs.iter().map(|tx| tx.hash(&NativeCrypto)).collect(),
921 state.node.node_id(),
922 )
923 .map_err(|e| PeerConnectionError::BroadcastError(e.to_string()))?;
924 send_tx_hashes(
925 txs,
926 state.capabilities.clone(),
927 connection,
928 state.node.node_id(),
929 &state.blockchain,
930 )
931 .await
932 .map_err(|e| PeerConnectionError::SendMessage(e.to_string()))?;
933 }
934 Ok(())
935}
936
937async fn send_block_range_update(state: &mut Established) -> Result<(), PeerConnectionError> {
938 if state
940 .negotiated_eth_capability
941 .as_ref()
942 .is_some_and(|eth| eth.version >= 69)
943 {
944 trace!(peer=%state.node, "Sending BlockRangeUpdate");
945 let update = BlockRangeUpdate::new(&state.storage).await?;
946 let lastet_block = update.latest_block;
947 send(state, Message::BlockRangeUpdate(update)).await?;
948 state.last_block_range_update_block = lastet_block - (lastet_block % 32);
949 }
950 Ok(())
951}
952
953async fn should_send_block_range_update(state: &Established) -> Result<bool, PeerConnectionError> {
954 let latest_block = state.storage.get_latest_block_number().await?;
955 if latest_block < state.last_block_range_update_block
956 || latest_block - state.last_block_range_update_block >= 32
957 {
958 return Ok(true);
959 }
960 Ok(false)
961}
962
963async fn init_capabilities<S>(
964 state: &mut Established,
965 stream: &mut S,
966) -> Result<(), PeerConnectionError>
967where
968 S: Unpin + Stream<Item = Result<Message, PeerConnectionError>>,
969{
970 if let Some(eth) = state.negotiated_eth_capability.clone() {
972 let status = match eth.version {
973 68 => Message::Status68(StatusMessage68::new(&state.storage).await?),
974 69 => Message::Status69(StatusMessage69::new(&state.storage).await?),
975 70 => Message::Status70(StatusMessage70::new(&state.storage).await?),
976 71 => Message::Status71(StatusMessage71::new(&state.storage).await?),
977 ver => {
978 return Err(PeerConnectionError::HandshakeError(format!(
979 "Invalid eth version {ver}"
980 )));
981 }
982 };
983 trace!(peer=%state.node, "Sending status");
984 send(state, status).await?;
985 let msg = match receive(stream).await {
989 Some(msg) => msg?,
990 None => return Err(PeerConnectionError::Disconnected),
991 };
992 match msg {
993 Message::Status68(msg_data) => {
994 trace!(peer=%state.node, "Received Status(68)");
995 backend::validate_status(msg_data, &state.storage, ð).await?
996 }
997 Message::Status69(msg_data) => {
998 trace!(peer=%state.node, "Received Status(69)");
999 backend::validate_status(msg_data, &state.storage, ð).await?
1000 }
1001 Message::Status70(msg_data) => {
1002 trace!(peer=%state.node, "Received Status(70)");
1003 backend::validate_status(msg_data, &state.storage, ð).await?
1004 }
1005 Message::Status71(msg_data) => {
1006 trace!(peer=%state.node, "Received Status(71)");
1007 backend::validate_status(msg_data, &state.storage, ð).await?
1008 }
1009 Message::Disconnect(disconnect) => {
1010 return Err(PeerConnectionError::HandshakeError(format!(
1011 "Peer disconnected due to: {}",
1012 disconnect.reason()
1013 )));
1014 }
1015 _ => {
1016 return Err(PeerConnectionError::HandshakeError(
1017 "Expected a Status message".to_string(),
1018 ));
1019 }
1020 }
1021 }
1022 Ok(())
1023}
1024
1025async fn send_disconnect_message(state: &mut Established, reason: Option<DisconnectReason>) {
1026 send(state, Message::Disconnect(DisconnectMessage { reason }))
1027 .await
1028 .unwrap_or_else(|_| {
1029 debug!(
1030 peer=%state.node,
1031 ?reason,
1032 "Could not send Disconnect message",
1033 );
1034 });
1035}
1036
1037async fn connection_failed(state: &mut Established, error_text: &str, error: &PeerConnectionError) {
1038 debug!(
1039 peer=%state.node,
1040 %error_text,
1041 %error,
1042 "connection failure"
1043 );
1044
1045 if !matches!(error, PeerConnectionError::DisconnectReceived(_)) {
1048 send_disconnect_message(state, match_disconnect_reason(error)).await;
1049 }
1050
1051 match error {
1053 PeerConnectionError::DisconnectReceived(DisconnectReason::AlreadyConnected)
1055 | PeerConnectionError::DisconnectSent(DisconnectReason::AlreadyConnected) => {
1056 debug!(
1057 peer=%state.node,
1058 %error_text,
1059 %error,
1060 "Peer already connected, don't replace it"
1061 );
1062 }
1063 _ => {
1064 debug!(
1065 peer=%state.node,
1066 %error_text,
1067 %error,
1068 remote_public_key=%state.node.public_key,
1069 "discarding peer",
1070 );
1071 }
1072 }
1073}
1074
1075fn match_disconnect_reason(error: &PeerConnectionError) -> Option<DisconnectReason> {
1076 match error {
1077 PeerConnectionError::DisconnectSent(reason) => Some(*reason),
1078 PeerConnectionError::DisconnectReceived(reason) => Some(*reason),
1079 PeerConnectionError::RLPDecodeError(_) => Some(DisconnectReason::NetworkError),
1080 PeerConnectionError::TooManyPeers => Some(DisconnectReason::TooManyPeers),
1081 _ => None,
1083 }
1084}
1085
1086async fn exchange_hello_messages<S>(
1087 state: &mut Established,
1088 stream: &mut S,
1089) -> Result<(), PeerConnectionError>
1090where
1091 S: Unpin + Stream<Item = Result<Message, PeerConnectionError>>,
1092{
1093 #[allow(unused_mut)]
1096 let mut supported_capabilities: Vec<Capability> = [
1097 &SUPPORTED_ETH_CAPABILITIES[..],
1098 &SUPPORTED_SNAP_CAPABILITIES[..],
1099 ]
1100 .concat();
1101 #[cfg(feature = "l2")]
1102 if state.l2_state.is_supported() {
1103 supported_capabilities.push(crate::rlpx::l2::SUPPORTED_BASED_CAPABILITIES[0].clone());
1104 }
1105 let hello_msg = Message::Hello(p2p::HelloMessage::new(
1106 supported_capabilities,
1107 PublicKey::from_secret_key(secp256k1::SECP256K1, &state.signer),
1108 state.client_version.clone(),
1109 ));
1110
1111 send(state, hello_msg).await?;
1112
1113 let msg = match receive(stream).await {
1115 Some(msg) => msg?,
1116 None => return Err(PeerConnectionError::Disconnected),
1117 };
1118
1119 match msg {
1120 Message::Hello(hello_message) => {
1121 let mut negotiated_eth_version = 0;
1122 let mut negotiated_snap_version = 0;
1123
1124 trace!(
1125 peer=%state.node,
1126 capabilities=?hello_message.capabilities,
1127 "Hello message capabilities",
1128 );
1129
1130 for cap in &hello_message.capabilities {
1132 match cap.protocol() {
1133 "eth" => {
1134 if SUPPORTED_ETH_CAPABILITIES.contains(cap)
1135 && cap.version > negotiated_eth_version
1136 {
1137 negotiated_eth_version = cap.version;
1138 }
1139 }
1140 "snap" => {
1141 if SUPPORTED_SNAP_CAPABILITIES.contains(cap)
1142 && cap.version > negotiated_snap_version
1143 {
1144 negotiated_snap_version = cap.version;
1145 }
1146 }
1147 #[cfg(feature = "l2")]
1148 "based" if state.l2_state.is_supported() => {
1149 state.l2_state.set_established()?;
1150 }
1151 _ => {}
1152 }
1153 }
1154
1155 state.capabilities = hello_message.capabilities;
1156
1157 if negotiated_eth_version == 0 {
1158 return Err(PeerConnectionError::NoMatchingCapabilities);
1159 }
1160 debug!("Negotiated eth version: eth/{}", negotiated_eth_version);
1161 state.negotiated_eth_capability = Some(Capability::eth(negotiated_eth_version));
1162
1163 if negotiated_snap_version != 0 {
1164 debug!("Negotiated snap version: snap/{}", negotiated_snap_version);
1165 state.negotiated_snap_capability = Some(Capability::snap(negotiated_snap_version));
1166 }
1167
1168 state.node.version = Some(hello_message.client_id);
1169
1170 Ok(())
1171 }
1172 Message::Disconnect(disconnect) => {
1173 Err(PeerConnectionError::DisconnectReceived(disconnect.reason()))
1174 }
1175 _ => {
1176 Err(PeerConnectionError::BadRequest(
1178 "Expected Hello message".to_string(),
1179 ))
1180 }
1181 }
1182}
1183
1184pub(crate) async fn send(
1185 state: &mut Established,
1186 message: Message,
1187) -> Result<(), PeerConnectionError> {
1188 #[cfg(feature = "metrics")]
1189 {
1190 use ethrex_metrics::p2p::METRICS_P2P;
1191 METRICS_P2P.inc_outgoing_message(message.metric_label());
1192 }
1193 match state.outbound_tx.try_send(message) {
1198 Ok(()) => Ok(()),
1199 Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
1200 state
1204 .disconnect_reason
1205 .get_or_insert(DisconnectReason::UselessPeer);
1206 Err(PeerConnectionError::OutboundQueueFull)
1207 }
1208 Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
1209 if state
1213 .outbound_writer_timed_out
1214 .load(std::sync::atomic::Ordering::Acquire)
1215 {
1216 state
1217 .disconnect_reason
1218 .get_or_insert(DisconnectReason::UselessPeer);
1219 Err(PeerConnectionError::OutboundSendTimeout)
1220 } else {
1221 Err(PeerConnectionError::Disconnected)
1222 }
1223 }
1224 }
1225}
1226
1227pub(crate) fn spawn_outbound_writer(
1234 mut sink: SplitSink<Framed<TcpStream, RLPxCodec>, Message>,
1235) -> (
1236 tokio::sync::mpsc::Sender<Message>,
1237 std::sync::Arc<std::sync::atomic::AtomicBool>,
1238) {
1239 let (tx, mut rx) = tokio::sync::mpsc::channel::<Message>(OUTBOUND_QUEUE_CAP);
1240 let timed_out = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
1244 let writer_timed_out = timed_out.clone();
1245 tokio::spawn(async move {
1246 while let Some(message) = rx.recv().await {
1247 match tokio::time::timeout(OUTBOUND_SEND_TIMEOUT, sink.send(message)).await {
1248 Ok(Ok(())) => {}
1249 Ok(Err(_)) => break,
1251 Err(_) => {
1253 writer_timed_out.store(true, std::sync::atomic::Ordering::Release);
1254 break;
1255 }
1256 }
1257 }
1258 let _ = sink.close().await;
1259 });
1260 (tx, timed_out)
1261}
1262
1263async fn receive<S>(stream: &mut S) -> Option<Result<Message, PeerConnectionError>>
1275where
1276 S: Unpin + Stream<Item = Result<Message, PeerConnectionError>>,
1277{
1278 stream.next().await
1279}
1280
1281fn check_serve_request_rate(state: &mut Established) -> bool {
1284 let now = Instant::now();
1285 if now.duration_since(state.serve_request_window_start) >= SERVE_REQUEST_WINDOW {
1286 state.serve_request_window_start = now;
1287 state.serve_requests_in_window = 0;
1288 }
1289 state.serve_requests_in_window += 1;
1290 state.serve_requests_in_window <= MAX_SERVE_REQUESTS_PER_WINDOW
1291}
1292
1293async fn handle_incoming_message(
1294 state: &mut Established,
1295 message: Message,
1296) -> Result<(), PeerConnectionError> {
1297 #[cfg(feature = "metrics")]
1298 {
1299 use ethrex_metrics::p2p::METRICS_P2P;
1300 METRICS_P2P.inc_incoming_message(message.metric_label());
1301 }
1302
1303 let is_data_request = matches!(
1305 message,
1306 Message::GetBlockHeaders(_)
1307 | Message::GetBlockBodies(_)
1308 | Message::GetReceipts68(_)
1309 | Message::GetReceipts69(_)
1310 | Message::GetReceipts70(_)
1311 | Message::GetPooledTransactions(_)
1312 | Message::GetAccountRange(_)
1313 | Message::GetStorageRanges(_)
1314 | Message::GetByteCodes(_)
1315 | Message::GetTrieNodes(_)
1316 );
1317 if is_data_request && !check_serve_request_rate(state) {
1318 debug!(
1319 peer = %state.node,
1320 window_requests = state.serve_requests_in_window,
1321 "Disconnecting peer: exceeded incoming request rate limit",
1322 );
1323 send_disconnect_message(state, Some(DisconnectReason::UselessPeer)).await;
1324 return Err(PeerConnectionError::DisconnectSent(
1325 DisconnectReason::UselessPeer,
1326 ));
1327 }
1328
1329 let peer_supports_eth = state.negotiated_eth_capability.is_some();
1330 #[cfg(feature = "l2")]
1331 let peer_supports_l2 = state.l2_state.connection_state().is_ok();
1332 match message {
1333 Message::Disconnect(msg_data) => {
1334 let reason = msg_data.reason();
1335 trace!(
1336 peer=%state.node,
1337 ?reason,
1338 "Received Disconnect"
1339 );
1340 state.disconnect_reason = Some(reason);
1341
1342 return Err(PeerConnectionError::DisconnectReceived(reason));
1345 }
1346 Message::Ping(_) => {
1347 trace!(peer=%state.node, "Sending pong message");
1348 send(state, Message::Pong(PongMessage {})).await?;
1349 }
1350 Message::Pong(_) => {
1351 state.missed_pongs = 0;
1353 }
1354 Message::Status68(msg_data) => {
1355 if let Some(eth) = &state.negotiated_eth_capability {
1356 backend::validate_status(msg_data, &state.storage, eth).await?
1357 };
1358 }
1359 Message::Status69(msg_data) => {
1360 if let Some(eth) = &state.negotiated_eth_capability {
1361 backend::validate_status(msg_data, &state.storage, eth).await?
1362 };
1363 }
1364 Message::Status70(msg_data) => {
1365 if let Some(eth) = &state.negotiated_eth_capability {
1366 backend::validate_status(msg_data, &state.storage, eth).await?
1367 };
1368 }
1369 Message::Status71(msg_data) => {
1370 if let Some(eth) = &state.negotiated_eth_capability {
1371 backend::validate_status(msg_data, &state.storage, eth).await?
1372 };
1373 }
1374 Message::GetAccountRange(req) => {
1375 let response = process_account_range_request(req, state.storage.clone()).await?;
1376 send(state, Message::AccountRange(response)).await?
1377 }
1378 Message::Transactions(txs) if peer_supports_eth => {
1379 if !txs.transactions.is_empty() {
1381 state.received_txs_from_peer = true;
1382 }
1383 if state.blockchain.is_synced() {
1384 let tx_hashes: Vec<_> = txs
1385 .transactions
1386 .iter()
1387 .map(|tx| tx.hash(&NativeCrypto))
1388 .collect();
1389
1390 let blockchain = state.blockchain.clone();
1393 let peer = state.node.to_string();
1394 #[cfg(feature = "l2")]
1395 let is_l2_mode = state.l2_state.is_supported();
1396 tokio::spawn(async move {
1397 for tx in txs.transactions {
1398 #[cfg(feature = "l2")]
1399 if (is_l2_mode && matches!(tx, Transaction::EIP4844Transaction(_)))
1400 || tx.is_privileged()
1401 {
1402 let tx_type = tx.tx_type();
1403 debug!(peer=%peer, "Rejecting transaction in L2 mode - {tx_type} transactions are not broadcasted in L2");
1404 continue;
1405 }
1406
1407 if let Err(e) = blockchain.add_transaction_to_pool(tx).await {
1408 debug!(
1409 peer=%peer,
1410 error=%e,
1411 "Error adding transaction"
1412 );
1413 }
1414 }
1415 });
1416
1417 state
1421 .tx_broadcaster
1422 .add_txs(tx_hashes, state.node.node_id())
1423 .map_err(|e| PeerConnectionError::BroadcastError(e.to_string()))?;
1424 }
1425 }
1426 Message::GetBlockHeaders(msg_data) if peer_supports_eth => {
1427 let response = BlockHeaders {
1428 id: msg_data.id,
1429 block_headers: msg_data.fetch_headers(&state.storage).await,
1430 };
1431 send(state, Message::BlockHeaders(response)).await?;
1432 }
1433 Message::GetBlockBodies(msg_data) if peer_supports_eth => {
1434 let response = BlockBodies {
1435 id: msg_data.id,
1436 block_bodies: msg_data.fetch_blocks(&state.storage).await,
1437 };
1438 send(state, Message::BlockBodies(response)).await?;
1439 }
1440 Message::GetBlockAccessLists(GetBlockAccessLists { id, block_hashes })
1441 if peer_supports_eth =>
1442 {
1443 use crate::rlpx::eth::block_access_lists::BLOCK_ACCESS_LIST_LIMIT;
1444 let mut block_access_lists =
1445 Vec::with_capacity(block_hashes.len().min(BLOCK_ACCESS_LIST_LIMIT));
1446 for hash in &block_hashes {
1447 let bal = match state.storage.get_block_access_list(*hash) {
1453 Ok(Some(bal)) => {
1454 let commitment = match state.storage.get_block_header_by_hash(*hash) {
1455 Ok(Some(header)) => header.block_access_list_hash,
1456 Ok(None) => None,
1457 Err(err) => {
1458 warn!(
1462 "Failed to read header for BAL commitment check (hash {hash:#x}): {err}; reporting BAL unavailable"
1463 );
1464 None
1465 }
1466 };
1467 bal.matches_commitment(commitment, &NativeCrypto)
1468 .then_some(bal)
1469 }
1470 Ok(None) => None,
1471 Err(err) => {
1472 error!("Error accessing DB while building BAL response for peer: {err}");
1473 None
1474 }
1475 };
1476 block_access_lists.push(bal);
1477 if block_access_lists.len() >= BLOCK_ACCESS_LIST_LIMIT {
1478 break;
1479 }
1480 }
1481 let response = BlockAccessLists::new(id, block_access_lists);
1482 send(state, Message::BlockAccessLists(response)).await?;
1483 }
1484 Message::GetReceipts68(GetReceipts68 { id, block_hashes }) if peer_supports_eth => {
1485 let mut receipts = Vec::new();
1486 for hash in block_hashes.iter() {
1487 receipts.push(state.storage.get_receipts_for_block(hash).await?);
1488 }
1489 send(state, Message::Receipts68(Receipts68::new(id, receipts))).await?;
1490 }
1491 Message::GetReceipts69(GetReceipts68 { id, block_hashes }) if peer_supports_eth => {
1492 let mut receipts = Vec::new();
1493 for hash in block_hashes.iter() {
1494 receipts.push(state.storage.get_receipts_for_block(hash).await?);
1495 }
1496 send(state, Message::Receipts69(Receipts69::new(id, receipts))).await?;
1497 }
1498 Message::GetReceipts70(GetReceipts70 {
1500 id,
1501 first_block_receipt_index,
1502 block_hashes,
1503 }) if peer_supports_eth => {
1504 let block_hashes = &block_hashes[..block_hashes.len().min(256)];
1505 let mut all_receipts: Vec<Vec<Receipt>> = Vec::new();
1506 let mut total_size: usize = 0;
1507 let mut last_block_incomplete = false;
1508
1509 for (i, hash) in block_hashes.iter().enumerate() {
1510 let start_index = if i == 0 { first_block_receipt_index } else { 0 };
1511 let block_receipts = state
1512 .storage
1513 .get_receipts_for_block_from_index(hash, start_index, None)
1514 .await?;
1515
1516 let mut block_receipt_list = Vec::new();
1517 let mut hit_limit = false;
1518 for receipt in block_receipts {
1519 let receipt_size = receipt.length();
1520 if total_size + receipt_size > SOFT_RESPONSE_LIMIT
1521 && (!block_receipt_list.is_empty() || !all_receipts.is_empty())
1522 {
1523 hit_limit = true;
1524 if !block_receipt_list.is_empty() {
1530 last_block_incomplete = true;
1531 }
1532 break;
1533 }
1534 total_size += receipt_size;
1535 block_receipt_list.push(receipt);
1536 }
1537
1538 if !block_receipt_list.is_empty() || !hit_limit {
1543 all_receipts.push(block_receipt_list);
1544 }
1545
1546 if hit_limit {
1547 break;
1548 }
1549 }
1550
1551 let response =
1552 Message::Receipts70(Receipts70::new(id, last_block_incomplete, all_receipts));
1553 send(state, response).await?;
1554 }
1555 Message::BlockRangeUpdate(update) => {
1556 trace!(
1557 peer=%state.node,
1558 range_from=update.earliest_block,
1559 range_to=update.latest_block,
1560 "Block range update",
1561 );
1562 if let Err(err) = update.validate() {
1564 debug!(
1565 peer=%state.node,
1566 reason=%err,
1567 "Disconnecting peer: invalid block range update",
1568 );
1569 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError)).await;
1570 return Err(PeerConnectionError::DisconnectSent(
1571 DisconnectReason::SubprotocolError,
1572 ));
1573 }
1574 }
1575 Message::NewPooledTransactionHashes(new_pooled_transaction_hashes) if peer_supports_eth => {
1576 if state.blockchain.is_synced() {
1578 let hashes = new_pooled_transaction_hashes
1579 .get_transactions_to_request(&state.blockchain, state.node.node_id())?;
1580 if !hashes.is_empty() {
1581 state
1584 .pending_tx_requests
1585 .push((new_pooled_transaction_hashes, hashes));
1586 }
1587 }
1588 }
1589 Message::GetPooledTransactions(msg) => {
1590 let response = msg.handle(&state.blockchain)?;
1591 let batch_size = response.pooled_transactions.len() as u64;
1592 if state.txs_sent_to_peer + batch_size > LEECH_TX_SENT_THRESHOLD
1594 && !state.received_txs_from_peer
1595 {
1596 debug!(
1597 peer = %state.node,
1598 txs_sent = state.txs_sent_to_peer,
1599 "Disconnecting peer: leech detected (sent many txs but received none)",
1600 );
1601 send_disconnect_message(state, Some(DisconnectReason::UselessPeer)).await;
1602 return Err(PeerConnectionError::DisconnectSent(
1603 DisconnectReason::UselessPeer,
1604 ));
1605 }
1606 send(state, Message::PooledTransactions(response)).await?;
1607 state.txs_sent_to_peer += batch_size;
1608 }
1609 Message::PooledTransactions(msg) if peer_supports_eth => {
1610 if !msg.pooled_transactions.is_empty() {
1611 state.received_txs_from_peer = true;
1612 }
1613 let removed_request = state.requested_pooled_txs.remove(&msg.id);
1616 if let Some((_, ref requested_hashes, _)) = removed_request {
1617 state
1618 .blockchain
1619 .mempool
1620 .clear_in_flight_txs(requested_hashes)?;
1621 }
1622 for tx in &msg.pooled_transactions {
1624 if let P2PTransaction::EIP4844TransactionWithBlobs(itx) = tx
1625 && (itx.blobs_bundle.is_empty()
1626 || itx
1627 .blobs_bundle
1628 .validate_blob_commitment_hashes(&itx.tx.blob_versioned_hashes)
1629 .is_err())
1630 {
1631 debug!(
1632 peer=%state.node,
1633 "Disconnecting peer: invalid or missing blobs",
1634 );
1635 if let Some((_announced, requested_hashes, _)) = &removed_request {
1636 retry_on_alternates(&state.blockchain, &state.peer_table, requested_hashes)
1637 .await;
1638 }
1639 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError)).await;
1640 return Err(PeerConnectionError::DisconnectSent(
1641 DisconnectReason::SubprotocolError,
1642 ));
1643 }
1644 }
1645 if state.blockchain.is_synced() {
1646 if let Some((announced, requested_hashes, _)) = &removed_request {
1647 let fork = state.blockchain.current_fork().await?;
1648 if let Err(error) = msg.validate_requested(announced, fork) {
1649 debug!(
1650 peer=%state.node,
1651 reason=%error,
1652 "Disconnecting peer: invalid pooled transactions response",
1653 );
1654 retry_on_alternates(&state.blockchain, &state.peer_table, requested_hashes)
1655 .await;
1656 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError))
1657 .await;
1658 return Err(PeerConnectionError::DisconnectSent(
1659 DisconnectReason::SubprotocolError,
1660 ));
1661 }
1662 }
1663 #[cfg(feature = "l2")]
1664 let is_l2_mode = state.l2_state.is_supported();
1665
1666 #[cfg(not(feature = "l2"))]
1667 let is_l2_mode = false;
1668 if let Err(error) = msg.handle(&state.node, &state.blockchain, is_l2_mode).await {
1669 if matches!(
1670 error,
1671 ethrex_blockchain::error::MempoolError::BlobsBundleError(_)
1672 ) {
1673 debug!(
1674 peer=%state.node,
1675 reason=%error,
1676 "Disconnecting peer: invalid pooled transactions response",
1677 );
1678 if let Some((_announced, requested_hashes, _)) = &removed_request {
1679 retry_on_alternates(
1680 &state.blockchain,
1681 &state.peer_table,
1682 requested_hashes,
1683 )
1684 .await;
1685 }
1686 send_disconnect_message(state, Some(DisconnectReason::SubprotocolError))
1687 .await;
1688 return Err(PeerConnectionError::DisconnectSent(
1689 DisconnectReason::SubprotocolError,
1690 ));
1691 }
1692 return Err(error.into());
1693 }
1694 }
1695 }
1696 Message::GetStorageRanges(req) => {
1697 let response = process_storage_ranges_request(req, state.storage.clone()).await?;
1698 send(state, Message::StorageRanges(response)).await?
1699 }
1700 Message::GetByteCodes(req) => {
1701 let storage_clone = state.storage.clone();
1702 let response = process_byte_codes_request(req, storage_clone)
1703 .await
1704 .map_err(|_| {
1705 PeerConnectionError::InternalError(
1706 "Failed to execute bytecode retrieval task".to_string(),
1707 )
1708 })?;
1709 send(state, Message::ByteCodes(response)).await?
1710 }
1711 Message::GetTrieNodes(req) => {
1712 let id = req.id;
1713 match process_trie_nodes_request(req, state.storage.clone()).await {
1714 Ok(response) => send(state, Message::TrieNodes(response)).await?,
1715 Err(_) => send(state, Message::TrieNodes(TrieNodes { id, nodes: vec![] })).await?,
1716 }
1717 }
1718 #[cfg(feature = "l2")]
1719 Message::L2(req) if peer_supports_l2 => {
1720 handle_based_capability_message(state, req).await?;
1721 }
1722 message @ Message::AccountRange(_)
1724 | message @ Message::StorageRanges(_)
1725 | message @ Message::ByteCodes(_)
1726 | message @ Message::TrieNodes(_)
1727 | message @ Message::BlockBodies(_)
1728 | message @ Message::BlockHeaders(_)
1729 | message @ Message::Receipts68(_)
1730 | message @ Message::Receipts69(_)
1731 | message @ Message::Receipts70(_)
1732 | message @ Message::BlockAccessLists(_) => {
1733 if let Some((_, tx)) = message
1734 .request_id()
1735 .and_then(|id| state.current_requests.remove(&id))
1736 {
1737 tx.send(message)
1738 .map_err(|e| PeerConnectionError::SendMessage(e.to_string()))?
1739 } else {
1740 return Err(PeerConnectionError::ExpectedRequestId(format!("{message}")));
1741 }
1742 }
1743 message => return Err(PeerConnectionError::MessageNotHandled(format!("{message}"))),
1745 };
1746 Ok(())
1747}
1748
1749async fn handle_outgoing_message(
1750 state: &mut Established,
1751 message: Message,
1752) -> Result<(), PeerConnectionError> {
1753 trace!(
1754 peer=%state.node,
1755 %message,
1756 "Sending message"
1757 );
1758 send(state, message).await?;
1759 Ok(())
1760}
1761
1762async fn handle_outgoing_request(
1763 state: &mut Established,
1764 message: Message,
1765 sender: oneshot::Sender<Message>,
1766) -> Result<(), PeerConnectionError> {
1767 message.request_id().and_then(|id| {
1769 state
1770 .current_requests
1771 .insert(id, (format!("{message}"), sender))
1772 });
1773 trace!(
1774 peer=%state.node,
1775 %message,
1776 "Sending request"
1777 );
1778 send(state, message).await?;
1779 Ok(())
1780}
1781
1782async fn handle_broadcast(
1783 state: &mut Established,
1784 (id, broadcasted_msg): (task::Id, Arc<Message>),
1785) -> Result<(), PeerConnectionError> {
1786 if id != tokio::task::id() {
1787 match broadcasted_msg.as_ref() {
1788 #[cfg(feature = "l2")]
1789 l2_msg @ Message::L2(_) => {
1790 handle_l2_broadcast(state, l2_msg).await?;
1791 }
1792 msg => {
1793 error!(
1794 peer=%state.node,
1795 message=%msg,
1796 "Non-supported message broadcasted"
1797 );
1798 let error_message = format!("Non-supported message broadcasted: {msg}");
1799 return Err(PeerConnectionError::BroadcastError(error_message));
1800 }
1801 }
1802 }
1803 Ok(())
1804}
1805
1806async fn handle_block_range_update(state: &mut Established) -> Result<(), PeerConnectionError> {
1807 if should_send_block_range_update(state).await? {
1808 send_block_range_update(state).await
1809 } else {
1810 Ok(())
1811 }
1812}
1813
1814async fn flush_pending_tx_requests(state: &mut Established) -> Result<(), PeerConnectionError> {
1818 if state.pending_tx_requests.is_empty() {
1819 return Ok(());
1820 }
1821
1822 let pending = std::mem::take(&mut state.pending_tx_requests);
1823
1824 let mut all_hashes: Vec<H256> = Vec::new();
1827 let mut all_types: Vec<u8> = Vec::new();
1828 let mut all_sizes: Vec<usize> = Vec::new();
1829
1830 for (announcement, hashes) in &pending {
1831 let trimmed = announcement.filter_to(hashes);
1832 all_hashes.extend_from_slice(&trimmed.transaction_hashes);
1833 all_types.extend_from_slice(&trimmed.transaction_types);
1834 all_sizes.extend(trimmed.transaction_sizes);
1835 }
1836
1837 const MAX_HASHES_PER_REQUEST: usize = 256;
1839 for (i, chunk) in all_hashes.chunks(MAX_HASHES_PER_REQUEST).enumerate() {
1840 let offset = i * MAX_HASHES_PER_REQUEST;
1841 let chunk_types = &all_types[offset..offset + chunk.len()];
1842 let chunk_sizes = &all_sizes[offset..offset + chunk.len()];
1843
1844 let announcement = NewPooledTransactionHashes::from_raw(
1845 chunk_types.to_vec().into(),
1846 chunk_sizes.to_vec(),
1847 chunk.to_vec(),
1848 );
1849 let request = GetPooledTransactions::new(random(), chunk.to_vec());
1850 let request_id = request.id;
1851 if let Err(e) = send(state, Message::GetPooledTransactions(request)).await {
1854 let unsent = &all_hashes[offset..];
1860 if !unsent.is_empty() {
1861 if let Err(clear_err) = state.blockchain.mempool.clear_in_flight_txs(unsent) {
1862 warn!(error = %clear_err, "Failed to clear in-flight transaction tracking after send error");
1863 }
1864 retry_on_alternates(&state.blockchain, &state.peer_table, unsent).await;
1865 }
1866 return Err(e);
1867 }
1868 state
1869 .requested_pooled_txs
1870 .insert(request_id, (announcement, chunk.to_vec(), Instant::now()));
1871 }
1872
1873 Ok(())
1874}
1875
1876async fn retry_on_alternates(
1888 blockchain: &Arc<Blockchain>,
1889 peer_table: &PeerTable,
1890 hashes: &[H256],
1891) {
1892 if hashes.is_empty() {
1893 return;
1894 }
1895 type AltGroup = (PeerConnection, Vec<(H256, u8, usize)>);
1902 let mut by_peer: FxHashMap<H256, AltGroup> = FxHashMap::default();
1903 for hash in hashes {
1904 loop {
1905 let alt = match blockchain.mempool.pop_alternate(*hash) {
1906 Ok(Some(a)) => a,
1907 Ok(None) => break,
1908 Err(e) => {
1909 warn!(error = %e, "pop_alternate failed");
1910 break;
1911 }
1912 };
1913 if let Some((_, list)) = by_peer.get_mut(&alt.peer_id) {
1915 list.push((*hash, alt.tx_type, alt.tx_size));
1916 break;
1917 }
1918 match peer_table.get_peer_connection(alt.peer_id).await {
1919 Ok(Some(conn)) => {
1920 by_peer.insert(alt.peer_id, (conn, vec![(*hash, alt.tx_type, alt.tx_size)]));
1921 break;
1922 }
1923 Ok(None) => continue, Err(e) => {
1925 warn!(error = %e, "get_peer_connection failed");
1926 break;
1927 }
1928 }
1929 }
1930 }
1931
1932 for (_, (conn, entries)) in by_peer {
1933 let mut types = Vec::with_capacity(entries.len());
1934 let mut sizes = Vec::with_capacity(entries.len());
1935 let mut hash_list = Vec::with_capacity(entries.len());
1936 for (h, t, s) in &entries {
1937 hash_list.push(*h);
1938 types.push(*t);
1939 sizes.push(*s);
1940 }
1941 let announcement =
1942 NewPooledTransactionHashes::from_raw(types.into(), sizes, hash_list.clone());
1943 if let Err(e) = conn.enqueue_tx_requests(announcement, hash_list) {
1944 debug!(error = %e, "Failed to enqueue tx requests on alternate peer");
1945 }
1946 }
1947}