1use std::{
8 future::Future,
9 marker::PhantomData,
10 pin::Pin,
11 sync::{Arc, Mutex},
12 task::{Context, Poll},
13};
14
15use futures::{FutureExt, SinkExt, Stream, StreamExt};
16use tokio::{
17 sync::{mpsc, OwnedSemaphorePermit, Semaphore},
18 time::{error::Elapsed, timeout},
19};
20use tower::{Service, ServiceExt};
21use tracing::{info_span, Instrument, Span};
22
23use cuprate_pruning::{PruningError, PruningSeed};
24use cuprate_wire::{
25 admin::{
26 HandshakeRequest, HandshakeResponse, PingResponse, SupportFlagsResponse,
27 PING_OK_RESPONSE_STATUS_TEXT,
28 },
29 common::PeerSupportFlags,
30 AdminRequestMessage, AdminResponseMessage, BasicNodeData, BucketError, LevinCommand, Message,
31};
32
33use crate::{
34 client::{
35 connection::Connection, request_handler::PeerRequestHandler,
36 timeout_monitor::connection_timeout_monitor_task, Client, InternalPeerID, PeerInformation,
37 PeerSyncCallback,
38 },
39 constants::{
40 CLIENT_QUEUE_SIZE, HANDSHAKE_TIMEOUT, MAX_EAGER_PROTOCOL_MESSAGES,
41 MAX_PEERS_IN_PEER_LIST_MESSAGE, PING_TIMEOUT,
42 },
43 handles::HandleBuilder,
44 AddressBook, AddressBookRequest, AddressBookResponse, BroadcastMessage, ConnectionDirection,
45 CoreSyncDataRequest, CoreSyncDataResponse, CoreSyncSvc, NetZoneAddress, NetworkZone,
46 ProtocolRequestHandlerMaker, Transport,
47};
48
49pub mod builder;
50pub use builder::HandshakerBuilder;
51
52#[derive(Debug, thiserror::Error)]
53pub enum HandshakeError {
54 #[error("The handshake timed out")]
55 TimedOut(#[from] Elapsed),
56 #[error("Peer has the same node ID as us")]
57 PeerHasSameNodeID,
58 #[error("Peer is on a different network")]
59 IncorrectNetwork,
60 #[error("Peer sent a peer list with peers from different zones")]
61 PeerSentIncorrectPeerList(#[from] crate::services::PeerListConversionError),
62 #[error("Peer sent invalid message: {0}")]
63 PeerSentInvalidMessage(&'static str),
64 #[error("The peers pruning seed is invalid.")]
65 InvalidPruningSeed(#[from] PruningError),
66 #[error("Levin bucket error: {0}")]
67 LevinBucketError(#[from] BucketError),
68 #[error("Internal service error: {0}")]
69 InternalSvcErr(#[from] tower::BoxError),
70 #[error("I/O error: {0}")]
71 IO(#[from] std::io::Error),
72}
73
74pub struct DoHandshakeRequest<Z: NetworkZone, T: Transport<Z>> {
76 pub addr: InternalPeerID<Z::Addr>,
78 pub peer_stream: T::Stream,
80 pub peer_sink: T::Sink,
82 pub direction: ConnectionDirection,
84 pub permit: Option<OwnedSemaphorePermit>,
86}
87
88#[derive(Debug, Clone)]
90pub struct HandShaker<Z: NetworkZone, T: Transport<Z>, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
91{
92 address_book: AdrBook,
94 core_sync_svc: CSync,
96 protocol_request_svc_maker: ProtoHdlrMkr,
98
99 our_basic_node_data: BasicNodeData,
101
102 broadcast_stream_maker: BrdcstStrmMkr,
104
105 connection_parent_span: Span,
106
107 on_peer_sync: Option<PeerSyncCallback>,
109
110 transport_client_config: T::ClientConfig,
112
113 _zone: PhantomData<Z>,
115}
116
117impl<Z: NetworkZone, T: Transport<Z>, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
118 HandShaker<Z, T, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
119{
120 #[expect(clippy::too_many_arguments)]
122 const fn new(
123 address_book: AdrBook,
124 core_sync_svc: CSync,
125 protocol_request_svc_maker: ProtoHdlrMkr,
126 broadcast_stream_maker: BrdcstStrmMkr,
127 our_basic_node_data: BasicNodeData,
128 connection_parent_span: Span,
129 on_peer_sync: Option<PeerSyncCallback>,
130 transport_client_config: T::ClientConfig,
131 ) -> Self {
132 Self {
133 address_book,
134 core_sync_svc,
135 protocol_request_svc_maker,
136 broadcast_stream_maker,
137 our_basic_node_data,
138 connection_parent_span,
139 on_peer_sync,
140 transport_client_config,
141 _zone: PhantomData,
142 }
143 }
144
145 #[inline]
147 pub const fn transport_config(&self) -> &T::ClientConfig {
148 &self.transport_client_config
149 }
150}
151
152impl<Z: NetworkZone, T: Transport<Z>, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr, BrdcstStrm>
153 Service<DoHandshakeRequest<Z, T>>
154 for HandShaker<Z, T, AdrBook, CSync, ProtoHdlrMkr, BrdcstStrmMkr>
155where
156 AdrBook: AddressBook<Z> + Clone,
157 CSync: CoreSyncSvc + Clone,
158 ProtoHdlrMkr: ProtocolRequestHandlerMaker<Z> + Clone,
159 BrdcstStrm: Stream<Item = BroadcastMessage> + Send + 'static,
160 BrdcstStrmMkr: Fn(InternalPeerID<Z::Addr>) -> BrdcstStrm + Clone + Send + 'static,
161{
162 type Response = Client<Z>;
163 type Error = HandshakeError;
164 type Future =
165 Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
166
167 fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
168 Poll::Ready(Ok(()))
169 }
170
171 fn call(&mut self, req: DoHandshakeRequest<Z, T>) -> Self::Future {
172 let broadcast_stream_maker = self.broadcast_stream_maker.clone();
173
174 let address_book = self.address_book.clone();
175 let protocol_request_svc_maker = self.protocol_request_svc_maker.clone();
176 let core_sync_svc = self.core_sync_svc.clone();
177 let our_basic_node_data = self.our_basic_node_data.clone();
178
179 let connection_parent_span = self.connection_parent_span.clone();
180 let on_peer_sync = self.on_peer_sync.clone();
181
182 let transport_client_config = self.transport_client_config.clone();
183
184 let span = info_span!(parent: &Span::current(), "handshaker", addr=%req.addr);
185
186 async move {
187 timeout(
188 HANDSHAKE_TIMEOUT,
189 handshake(
190 req,
191 transport_client_config,
192 broadcast_stream_maker,
193 address_book,
194 core_sync_svc,
195 protocol_request_svc_maker,
196 our_basic_node_data,
197 connection_parent_span,
198 on_peer_sync,
199 ),
200 )
201 .await?
202 }
203 .instrument(span)
204 .boxed()
205 }
206}
207
208pub async fn ping<N, T>(addr: N::Addr, config: &T::ClientConfig) -> Result<u64, HandshakeError>
212where
213 N: NetworkZone,
214 T: Transport<N>,
215{
216 tracing::debug!("Sending Ping to peer");
217
218 let (mut peer_stream, mut peer_sink) = T::connect_to_peer(addr, config).await?;
219
220 tracing::debug!("Made outbound connection to peer, sending ping.");
221
222 peer_sink
223 .send(Message::Request(AdminRequestMessage::Ping).into())
224 .await?;
225
226 if let Some(res) = peer_stream.next().await {
227 if let Message::Response(AdminResponseMessage::Ping(ping)) = res? {
228 if ping.status == PING_OK_RESPONSE_STATUS_TEXT {
229 tracing::debug!("Ping successful.");
230 return Ok(ping.peer_id);
231 }
232
233 tracing::debug!("Peer's ping response was not `OK`.");
234 return Err(HandshakeError::PeerSentInvalidMessage(
235 "Ping response was not `OK`",
236 ));
237 }
238
239 tracing::debug!("Peer sent invalid response to ping.");
240 return Err(HandshakeError::PeerSentInvalidMessage(
241 "Peer did not send correct response for ping.",
242 ));
243 }
244
245 tracing::debug!("Connection closed before ping response.");
246 Err(BucketError::IO(std::io::Error::new(
247 std::io::ErrorKind::ConnectionAborted,
248 "The peer stream returned None",
249 ))
250 .into())
251}
252
253#[expect(clippy::too_many_arguments)]
255async fn handshake<
256 Z: NetworkZone,
257 T: Transport<Z>,
258 AdrBook,
259 CSync,
260 ProtoHdlrMkr,
261 BrdcstStrmMkr,
262 BrdcstStrm,
263>(
264 req: DoHandshakeRequest<Z, T>,
265 transport_client_config: T::ClientConfig,
266
267 broadcast_stream_maker: BrdcstStrmMkr,
268
269 mut address_book: AdrBook,
270 mut core_sync_svc: CSync,
271 mut protocol_request_svc_maker: ProtoHdlrMkr,
272 our_basic_node_data: BasicNodeData,
273 connection_parent_span: Span,
274 on_peer_sync: Option<PeerSyncCallback>,
275) -> Result<Client<Z>, HandshakeError>
276where
277 AdrBook: AddressBook<Z> + Clone,
278 CSync: CoreSyncSvc + Clone,
279 ProtoHdlrMkr: ProtocolRequestHandlerMaker<Z>,
280 BrdcstStrm: Stream<Item = BroadcastMessage> + Send + 'static,
281 BrdcstStrmMkr: Fn(InternalPeerID<Z::Addr>) -> BrdcstStrm + Send + 'static,
282{
283 let DoHandshakeRequest {
284 addr,
285 mut peer_stream,
286 mut peer_sink,
287 direction,
288 permit,
289 } = req;
290
291 let mut eager_protocol_messages = Vec::new();
294
295 let (peer_core_sync, peer_node_data) = match direction {
296 ConnectionDirection::Inbound => {
297 tracing::debug!("waiting for handshake request.");
299
300 let Message::Request(AdminRequestMessage::Handshake(handshake_req)) =
301 wait_for_message::<Z, T>(
302 LevinCommand::Handshake,
303 true,
304 &mut peer_sink,
305 &mut peer_stream,
306 &mut eager_protocol_messages,
307 &our_basic_node_data,
308 )
309 .await?
310 else {
311 panic!("wait_for_message returned ok with wrong message.");
312 };
313
314 tracing::debug!("Received handshake request.");
315 (handshake_req.payload_data, handshake_req.node_data)
317 }
318 ConnectionDirection::Outbound => {
319 send_hs_request::<Z, T, _>(
321 &mut peer_sink,
322 &mut core_sync_svc,
323 our_basic_node_data.clone(),
324 )
325 .await?;
326
327 let Message::Response(AdminResponseMessage::Handshake(handshake_res)) =
329 wait_for_message::<Z, T>(
330 LevinCommand::Handshake,
331 false,
332 &mut peer_sink,
333 &mut peer_stream,
334 &mut eager_protocol_messages,
335 &our_basic_node_data,
336 )
337 .await?
338 else {
339 panic!("wait_for_message returned ok with wrong message.");
340 };
341
342 if handshake_res.local_peerlist_new.len() > MAX_PEERS_IN_PEER_LIST_MESSAGE {
343 tracing::debug!("peer sent too many peers in response, cancelling handshake");
344
345 return Err(HandshakeError::PeerSentInvalidMessage(
346 "Too many peers in peer list message (>250)",
347 ));
348 }
349
350 tracing::debug!(
351 "Telling address book about new peers, len: {}",
352 handshake_res.local_peerlist_new.len()
353 );
354
355 address_book
357 .ready()
358 .await?
359 .call(AddressBookRequest::IncomingPeerList(
360 addr,
361 handshake_res
362 .local_peerlist_new
363 .into_iter()
364 .map(TryInto::try_into)
365 .collect::<Result<_, _>>()?,
366 ))
367 .await?;
368
369 (handshake_res.payload_data, handshake_res.node_data)
370 }
371 };
372
373 if peer_node_data.network_id != our_basic_node_data.network_id {
374 return Err(HandshakeError::IncorrectNetwork);
375 }
376
377 if Z::CHECK_NODE_ID && peer_node_data.peer_id == our_basic_node_data.peer_id {
378 return Err(HandshakeError::PeerHasSameNodeID);
379 }
380
381 let pruning_seed = PruningSeed::decompress_p2p_rules(peer_core_sync.pruning_seed)?;
415
416 let public_address = 'check_out_addr: {
418 match direction {
419 ConnectionDirection::Inbound => {
420 send_hs_response::<Z, T, _, _>(
422 &mut peer_sink,
423 &mut core_sync_svc,
424 &mut address_book,
425 our_basic_node_data.clone(),
426 )
427 .await?;
428
429 if peer_node_data.my_port != 0 {
431 let InternalPeerID::KnownAddr(mut outbound_address) = addr else {
432 break 'check_out_addr None;
434 };
435
436 #[expect(
437 clippy::cast_possible_truncation,
438 reason = "u32 does not make sense as a port so just truncate it."
439 )]
440 outbound_address.set_port(peer_node_data.my_port as u16);
441
442 let Ok(Ok(ping_peer_id)) = timeout(
443 PING_TIMEOUT,
444 ping::<Z, T>(outbound_address, &transport_client_config)
445 .instrument(info_span!("ping")),
446 )
447 .await
448 else {
449 break 'check_out_addr None;
451 };
452
453 if ping_peer_id == peer_node_data.peer_id {
455 break 'check_out_addr Some(outbound_address);
456 }
457 }
458 None
460 }
461 ConnectionDirection::Outbound => {
462 let InternalPeerID::KnownAddr(outbound_addr) = addr else {
463 unreachable!("How could we make an outbound connection to an unknown address");
464 };
465
466 Some(outbound_addr)
468 }
469 }
470 };
471
472 tracing::debug!("Handshake complete.");
473
474 let (connection_guard, handle) = HandleBuilder::new().with_permit(permit).build();
475
476 address_book
478 .ready()
479 .await?
480 .call(AddressBookRequest::NewConnection {
481 internal_peer_id: addr,
482 public_address,
483 handle: handle.clone(),
484 id: peer_node_data.peer_id,
485 pruning_seed,
486 rpc_port: peer_node_data.rpc_port,
487 rpc_credits_per_hash: peer_node_data.rpc_credits_per_hash,
488 })
489 .await?;
490
491 let (connection_tx, client_rx) = mpsc::channel(CLIENT_QUEUE_SIZE);
493
494 let info = PeerInformation {
495 id: addr,
496 handle,
497 direction,
498 pruning_seed,
499 basic_node_data: peer_node_data,
500 core_sync_data: Arc::new(Mutex::new(peer_core_sync)),
501 };
502
503 let protocol_request_handler = protocol_request_svc_maker
504 .ready()
505 .await?
506 .call(info.clone())
507 .await?;
508
509 let request_handler = PeerRequestHandler {
510 address_book_svc: address_book.clone(),
511 our_sync_svc: core_sync_svc.clone(),
512 protocol_request_handler,
513 our_basic_node_data,
514 peer_info: info.clone(),
515 on_peer_sync: on_peer_sync.clone(),
516 };
517
518 let connection_guard = match on_peer_sync.clone() {
519 Some(callback) => connection_guard.with_on_close(move || callback.disconnected()),
520 None => connection_guard,
521 };
522
523 let semaphore = Arc::new(Semaphore::new(1));
524
525 let timeout_handle = tokio::spawn(connection_timeout_monitor_task(
526 info.clone(),
527 connection_tx.clone(),
528 Arc::clone(&semaphore),
529 address_book,
530 core_sync_svc,
531 on_peer_sync,
532 ));
533
534 let connection = Connection::<Z, T, _, _, _, _>::new(
535 peer_sink,
536 client_rx,
537 broadcast_stream_maker(addr),
538 request_handler,
539 connection_guard,
540 timeout_handle,
541 );
542
543 let connection_span =
544 tracing::error_span!(parent: &connection_parent_span, "connection", %addr);
545
546 tokio::spawn(
548 connection
549 .run(peer_stream.fuse(), eager_protocol_messages)
550 .instrument(connection_span)
551 .boxed(),
552 );
553
554 let client = Client::<Z>::new(info, connection_tx, semaphore);
555
556 Ok(client)
557}
558
559async fn send_hs_request<Z, T, CSync>(
561 peer_sink: &mut T::Sink,
562 core_sync_svc: &mut CSync,
563 our_basic_node_data: BasicNodeData,
564) -> Result<(), HandshakeError>
565where
566 Z: NetworkZone,
567 T: Transport<Z>,
568 CSync: CoreSyncSvc,
569{
570 let CoreSyncDataResponse(our_core_sync_data) = core_sync_svc
571 .ready()
572 .await?
573 .call(CoreSyncDataRequest)
574 .await?;
575
576 let req = HandshakeRequest {
577 node_data: our_basic_node_data,
578 payload_data: our_core_sync_data,
579 };
580
581 tracing::debug!("Sending handshake request.");
582
583 peer_sink
584 .send(Message::Request(AdminRequestMessage::Handshake(req)).into())
585 .await?;
586
587 Ok(())
588}
589
590async fn send_hs_response<Z, T, CSync, AdrBook>(
592 peer_sink: &mut T::Sink,
593 core_sync_svc: &mut CSync,
594 address_book: &mut AdrBook,
595 our_basic_node_data: BasicNodeData,
596) -> Result<(), HandshakeError>
597where
598 Z: NetworkZone,
599 T: Transport<Z>,
600 AdrBook: AddressBook<Z>,
601 CSync: CoreSyncSvc,
602{
603 let CoreSyncDataResponse(our_core_sync_data) = core_sync_svc
604 .ready()
605 .await?
606 .call(CoreSyncDataRequest)
607 .await?;
608
609 let AddressBookResponse::Peers(our_peer_list) = address_book
610 .ready()
611 .await?
612 .call(AddressBookRequest::GetWhitePeers(
613 MAX_PEERS_IN_PEER_LIST_MESSAGE,
614 ))
615 .await?
616 else {
617 panic!("Address book sent incorrect response");
618 };
619
620 let res = HandshakeResponse {
621 node_data: our_basic_node_data,
622 payload_data: our_core_sync_data,
623 local_peerlist_new: our_peer_list.into_iter().map(Into::into).collect(),
624 };
625
626 tracing::debug!("Sending handshake response.");
627
628 peer_sink
629 .send(Message::Response(AdminResponseMessage::Handshake(res)).into())
630 .await?;
631
632 Ok(())
633}
634
635async fn wait_for_message<Z, T>(
641 levin_command: LevinCommand,
642 request: bool,
643
644 peer_sink: &mut T::Sink,
645 peer_stream: &mut T::Stream,
646
647 eager_protocol_messages: &mut Vec<cuprate_wire::ProtocolMessage>,
648
649 our_basic_node_data: &BasicNodeData,
650) -> Result<Message, HandshakeError>
651where
652 Z: NetworkZone,
653 T: Transport<Z>,
654{
655 let mut allow_support_flag_req = true;
656 let mut allow_ping = true;
657
658 while let Some(message) = peer_stream.next().await {
659 let message = message?;
660
661 match message {
662 Message::Protocol(protocol_message) => {
663 tracing::debug!(
664 "Received eager protocol message with ID: {}, adding to queue",
665 protocol_message.command()
666 );
667 eager_protocol_messages.push(protocol_message);
668 if eager_protocol_messages.len() > MAX_EAGER_PROTOCOL_MESSAGES {
669 tracing::debug!(
670 "Peer sent too many protocol messages before a handshake response."
671 );
672 return Err(HandshakeError::PeerSentInvalidMessage(
673 "Peer sent too many protocol messages",
674 ));
675 }
676 continue;
677 }
678 Message::Request(req_message) => {
679 if req_message.command() == levin_command && request {
680 return Ok(Message::Request(req_message));
681 }
682
683 match req_message {
684 AdminRequestMessage::SupportFlags => {
685 if !allow_support_flag_req {
686 return Err(HandshakeError::PeerSentInvalidMessage(
687 "Peer sent 2 support flag requests",
688 ));
689 }
690 send_support_flags::<Z, T>(peer_sink, our_basic_node_data.support_flags)
691 .await?;
692 allow_support_flag_req = false;
694 continue;
695 }
696 AdminRequestMessage::Ping => {
697 if !allow_ping {
698 return Err(HandshakeError::PeerSentInvalidMessage(
699 "Peer sent 2 ping requests",
700 ));
701 }
702
703 send_ping_response::<Z, T>(peer_sink, our_basic_node_data.peer_id).await?;
704
705 allow_ping = false;
707 continue;
708 }
709 AdminRequestMessage::Handshake(_) | AdminRequestMessage::TimedSync(_) => {
710 return Err(HandshakeError::PeerSentInvalidMessage(
711 "Peer sent an admin request before responding to the handshake",
712 ));
713 }
714 }
715 }
716 Message::Response(res_message) if !request => {
717 if res_message.command() == levin_command {
718 return Ok(Message::Response(res_message));
719 }
720
721 tracing::debug!("Received unexpected response: {}", res_message.command());
722 return Err(HandshakeError::PeerSentInvalidMessage(
723 "Peer sent an incorrect response",
724 ));
725 }
726
727 Message::Response(_) => Err(HandshakeError::PeerSentInvalidMessage(
728 "Peer sent an incorrect message",
729 )),
730 }?;
731 }
732
733 Err(BucketError::IO(std::io::Error::new(
734 std::io::ErrorKind::ConnectionAborted,
735 "The peer stream returned None",
736 ))
737 .into())
738}
739
740async fn send_support_flags<Z, T>(
742 peer_sink: &mut T::Sink,
743 support_flags: PeerSupportFlags,
744) -> Result<(), HandshakeError>
745where
746 Z: NetworkZone,
747 T: Transport<Z>,
748{
749 tracing::debug!("Sending support flag response.");
750 Ok(peer_sink
751 .send(
752 Message::Response(AdminResponseMessage::SupportFlags(SupportFlagsResponse {
753 support_flags,
754 }))
755 .into(),
756 )
757 .await?)
758}
759
760async fn send_ping_response<Z, T>(
762 peer_sink: &mut T::Sink,
763 peer_id: u64,
764) -> Result<(), HandshakeError>
765where
766 Z: NetworkZone,
767 T: Transport<Z>,
768{
769 tracing::debug!("Sending ping response.");
770 Ok(peer_sink
771 .send(
772 Message::Response(AdminResponseMessage::Ping(PingResponse {
773 status: PING_OK_RESPONSE_STATUS_TEXT,
774 peer_id,
775 }))
776 .into(),
777 )
778 .await?)
779}