Skip to main content

snarkos_node_tcp/
tcp.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::{
17    collections::HashSet,
18    fmt,
19    io,
20    net::{IpAddr, SocketAddr},
21    ops::Deref,
22    sync::{
23        Arc,
24        atomic::{AtomicUsize, Ordering::*},
25    },
26    time::{Duration, Instant},
27};
28
29use anyhow::anyhow;
30#[cfg(feature = "locktick")]
31use locktick::parking_lot::Mutex;
32use once_cell::sync::OnceCell;
33#[cfg(not(feature = "locktick"))]
34use parking_lot::Mutex;
35use tokio::{
36    io::split,
37    net::{TcpListener, TcpSocket, TcpStream},
38    sync::{OwnedSemaphorePermit, Semaphore, oneshot},
39    task::{JoinHandle, JoinSet},
40    time::timeout,
41};
42use tracing::*;
43
44use crate::{
45    BannedPeers,
46    Config,
47    KnownPeers,
48    Stats,
49    connections::{Connection, ConnectionSide, Connections, DisconnectOrigin, create_connection_span},
50    protocols::{Protocol, Protocols},
51};
52
53// A sequential numeric identifier assigned to `Tcp`s that were not provided with a name.
54static SEQUENTIAL_NODE_ID: AtomicUsize = AtomicUsize::new(0);
55
56/// The central object responsible for handling connections.
57#[derive(Clone)]
58pub struct Tcp(Arc<InnerTcp>);
59
60impl Deref for Tcp {
61    type Target = Arc<InnerTcp>;
62
63    fn deref(&self) -> &Self::Target {
64        &self.0
65    }
66}
67
68/// A custom application error that can be returned by the `Tcp` stack.
69pub trait ApplicationError: Send + Sync + std::fmt::Debug + std::fmt::Display + 'static {}
70
71/// Error types for the `Tcp::connect` function.
72#[allow(missing_docs)]
73#[derive(thiserror::Error, Debug)]
74pub enum ConnectError {
75    #[error("already reached the maximum number of {limit} connections")]
76    MaximumConnectionsReached { limit: u16 },
77    #[error("already connecting to node at {address:?}")]
78    AlreadyConnecting { address: SocketAddr },
79    #[error("already connected to node at {address:?}")]
80    AlreadyConnected { address: SocketAddr },
81    #[error("attempt to self-connect (at address {address:?}")]
82    SelfConnect { address: SocketAddr },
83    #[error("rejected a connection attempt from a banned IP '{ip}'")]
84    BannedIp { ip: IpAddr },
85    // Socket errors, such as "connection refused".
86    #[error(transparent)]
87    IoError(std::io::Error),
88    // An application-specific reason to reject the connection or abort the handshake.
89    // For snarkOS, this is either a `DisconnectReason` or a `PeeringError`, which do not fully implement `std::error::Error`.
90    #[error("{0}")]
91    ApplicationError(Box<dyn ApplicationError>),
92    /// An unexpected error at the application layer and certain deserialization errors.
93    /// TODO(kaimast): (some of) these should be treated with higher severity, as they indicate a bug or corrupted state,
94    ///                and deserialization errors should not be included in this "other" category.
95    #[error(transparent)]
96    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
97}
98
99impl ConnectError {
100    /// Pass an application-level error to the `Tcp` stack.
101    pub fn application<E: ApplicationError>(err: E) -> Self {
102        Self::ApplicationError(Box::new(err))
103    }
104
105    /// A generic error that can be returned by the `Tcp` stack.
106    pub fn other<E: Into<Box<dyn std::error::Error + Send + Sync>>>(err: E) -> Self {
107        Self::Other(err.into())
108    }
109}
110
111impl From<ConnectError> for std::io::Error {
112    fn from(err: ConnectError) -> Self {
113        match err {
114            ConnectError::IoError(err) => err,
115            ConnectError::Other(err) => std::io::Error::other(err),
116            err => std::io::Error::other(err.to_string()),
117        }
118    }
119}
120
121impl From<std::io::Error> for ConnectError {
122    fn from(err: std::io::Error) -> Self {
123        // Other error are usually checks that fail when snarkVM deserializes a message.
124        if err.kind() == std::io::ErrorKind::Other {
125            // This unwrap should always succeed.
126            let inner = err.into_inner().unwrap_or_else(|| anyhow!("Unknown error").into());
127            ConnectError::other(inner)
128        } else {
129            ConnectError::IoError(err)
130        }
131    }
132}
133
134#[doc(hidden)]
135pub struct InnerTcp {
136    /// The tracing span.
137    span: Span,
138    /// The node's configuration.
139    config: Config,
140    /// The node's listening address.
141    listening_addr: OnceCell<SocketAddr>,
142    /// Contains objects used by the protocols implemented by the node.
143    pub(crate) protocols: Protocols,
144    /// A set of connections that have not been finalized yet.
145    connecting: Mutex<HashSet<SocketAddr>>,
146    /// Contains objects related to the node's active connections.
147    pub(crate) connections: Connections,
148    /// Collects statistics related to the node's peers.
149    known_peers: KnownPeers,
150    /// Contains the set of currently banned peers.
151    banned_peers: BannedPeers,
152    /// Collects statistics related to the node itself.
153    stats: Stats,
154    /// The node's tasks.
155    pub(crate) tasks: Mutex<Vec<JoinHandle<()>>>,
156}
157
158impl Tcp {
159    /// Creates a new [`Tcp`] using the given [`Config`].
160    pub fn new(mut config: Config) -> Self {
161        // If there is no pre-configured name, assign a sequential numeric identifier.
162        if config.name.is_none() {
163            config.name = Some(SEQUENTIAL_NODE_ID.fetch_add(1, Relaxed).to_string());
164        }
165
166        // Create a tracing span containing the node's name.
167        let span = crate::helpers::create_span(config.name.as_deref().unwrap());
168
169        // Initialize the Tcp stack.
170        let tcp = Tcp(Arc::new(InnerTcp {
171            span,
172            config,
173            listening_addr: Default::default(),
174            protocols: Default::default(),
175            connecting: Default::default(),
176            connections: Default::default(),
177            known_peers: Default::default(),
178            banned_peers: Default::default(),
179            stats: Stats::new(Instant::now()),
180            tasks: Default::default(),
181        }));
182
183        debug!(parent: tcp.span(), "The node is ready");
184
185        tcp
186    }
187
188    /// How long has this node accepting connections?
189    pub fn uptime(&self) -> Duration {
190        self.stats.timestamp().elapsed()
191    }
192
193    /// Returns the name assigned.
194    #[inline]
195    pub fn name(&self) -> &str {
196        // safe; can be set as None in Config, but receives a default value on Tcp creation
197        self.config.name.as_deref().unwrap()
198    }
199
200    /// Returns a reference to the configuration.
201    #[inline]
202    pub fn config(&self) -> &Config {
203        &self.config
204    }
205
206    /// Returns the listening address; returns an error if Tcp was not configured
207    /// to listen for inbound connections.
208    pub fn listening_addr(&self) -> io::Result<SocketAddr> {
209        self.listening_addr.get().copied().ok_or_else(|| io::ErrorKind::AddrNotAvailable.into())
210    }
211
212    /// Checks whether the provided address is connected.
213    pub fn is_connected(&self, addr: SocketAddr) -> bool {
214        self.connections.is_connected(addr)
215    }
216
217    /// Checks if Tcp is currently setting up a connection with the provided address.
218    pub fn is_connecting(&self, addr: SocketAddr) -> bool {
219        self.connecting.lock().contains(&addr)
220    }
221
222    /// Returns the number of active connections.
223    pub fn num_connected(&self) -> usize {
224        self.connections.num_connected()
225    }
226
227    /// Returns the number of connections that are currently being set up.
228    pub fn num_connecting(&self) -> usize {
229        self.connecting.lock().len()
230    }
231
232    /// Returns a list containing addresses of active connections.
233    pub fn connected_addrs(&self) -> Vec<SocketAddr> {
234        self.connections.addrs()
235    }
236
237    /// Returns a list containing addresses of pending connections.
238    pub fn connecting_addrs(&self) -> Vec<SocketAddr> {
239        self.connecting.lock().iter().copied().collect()
240    }
241
242    /// Returns a reference to the collection of statistics of known peers.
243    #[inline]
244    pub fn known_peers(&self) -> &KnownPeers {
245        &self.known_peers
246    }
247
248    /// Returns a reference to the set of currently banned peers.
249    #[inline]
250    pub fn banned_peers(&self) -> &BannedPeers {
251        &self.banned_peers
252    }
253
254    /// Returns a reference to the statistics.
255    #[inline]
256    pub fn stats(&self) -> &Stats {
257        &self.stats
258    }
259
260    /// Returns the tracing [`Span`] associated with Tcp.
261    #[inline]
262    pub fn span(&self) -> &Span {
263        &self.span
264    }
265
266    /// Gracefully shuts down the stack.
267    pub async fn shut_down(&self) {
268        debug!(parent: self.span(), "Shutting down the TCP stack");
269
270        // Retrieve all tasks.
271        let mut tasks = std::mem::take(&mut *self.tasks.lock()).into_iter();
272
273        // Abort the listening task first.
274        if let Some(listening_task) = tasks.next() {
275            listening_task.abort(); // abort the listening task first
276        }
277
278        // Disconnect from all connected peers.
279        let mut disconnect_tasks = JoinSet::new();
280        for addr in self.connected_addrs() {
281            let node = self.clone();
282            disconnect_tasks.spawn(async move {
283                node.disconnect_w_origin(addr, DisconnectOrigin::Shutdown).await;
284            });
285        }
286        while disconnect_tasks.join_next().await.is_some() {}
287
288        // Abort all remaining tasks.
289        for handle in tasks {
290            handle.abort();
291        }
292    }
293}
294
295impl Tcp {
296    /// Connects to the provided `SocketAddr`.
297    pub async fn connect(&self, addr: SocketAddr) -> Result<(), ConnectError> {
298        if let Ok(listening_addr) = self.listening_addr() {
299            // TODO(nkls): maybe this first check can be dropped; though it might be best to keep just in case.
300            if addr == listening_addr || self.is_self_connect(addr) {
301                error!(parent: self.span(), "Attempted to self-connect ({addr})");
302                return Err(ConnectError::SelfConnect { address: addr });
303            }
304        }
305
306        if !self.can_add_connection() {
307            error!(parent: self.span(), "Too many connections; refusing to connect to {addr}");
308            return Err(ConnectError::MaximumConnectionsReached { limit: self.config.max_connections });
309        }
310
311        if self.is_connected(addr) {
312            trace!(parent: self.span(), "Already connected to {addr}");
313            return Err(ConnectError::AlreadyConnected { address: addr });
314        }
315
316        if !self.connecting.lock().insert(addr) {
317            debug!(parent: self.span(), "Already connecting to {addr}");
318            return Err(ConnectError::AlreadyConnecting { address: addr });
319        }
320
321        let timeout_duration = Duration::from_millis(self.config().connection_timeout_ms.into());
322
323        // Bind the tcp socket to the configured listener ip if it's set.
324        // Otherwise default to the system's default interface.
325        let res = if let Some(listen_ip) = self.config().listener_ip {
326            timeout(timeout_duration, self.connect_with_specific_interface(listen_ip, addr)).await
327        } else {
328            timeout(timeout_duration, TcpStream::connect(addr)).await
329        };
330
331        let stream = match res {
332            Ok(Ok(stream)) => Ok(stream),
333            Ok(err) => {
334                self.connecting.lock().remove(&addr);
335                err
336            }
337            Err(err) => {
338                self.connecting.lock().remove(&addr);
339                error!("connection timeout error: {}", err);
340                Err(io::ErrorKind::TimedOut.into())
341            }
342        }?;
343
344        let ret = self.adapt_stream(stream, addr, ConnectionSide::Initiator).await;
345
346        if let Err(ref e) = ret {
347            self.connecting.lock().remove(&addr);
348            self.known_peers().register_failure(addr.ip());
349            error!(parent: self.span(), "Unable to initiate a connection with {addr}: {e}");
350        }
351
352        ret.map_err(|err| err.into())
353    }
354
355    async fn connect_with_specific_interface(&self, listen_ip: IpAddr, addr: SocketAddr) -> io::Result<TcpStream> {
356        let sock = if listen_ip.is_ipv4() { TcpSocket::new_v4()? } else { TcpSocket::new_v6()? };
357        // Lock the socket to a specific interface.
358        sock.bind(SocketAddr::new(listen_ip, 0))?;
359        sock.connect(addr).await
360    }
361
362    /// Disconnects from the provided `SocketAddr`.
363    ///
364    /// Returns true if the we were connected to the given address.
365    pub async fn disconnect(&self, addr: SocketAddr) -> bool {
366        self.disconnect_w_origin(addr, DisconnectOrigin::User).await
367    }
368
369    pub(crate) async fn disconnect_w_origin(&self, addr: SocketAddr, origin: DisconnectOrigin) -> bool {
370        // claim the disconnect to avoid duplicate executions, or return early if already claimed
371        if let Some(conn) = self.connections.0.read().get(&addr) {
372            if conn.disconnecting.swap(true, AcqRel) {
373                // valid connection, but someone else is already disconnecting it
374                return false;
375            }
376        } else {
377            // not connected
378            return false;
379        };
380
381        if let Some(handler) = self.protocols.disconnect.get() {
382            let (sender, receiver) = oneshot::channel();
383            handler.trigger(((addr, origin), sender)).await;
384            if let Ok((handle, waiter)) = receiver.await {
385                // register the associated task with the connection, in case
386                // it gets terminated before its completion
387                if let Some(conn) = self.connections.0.write().get_mut(&addr) {
388                    conn.tasks.push(handle);
389                }
390                // wait for the OnDisconnect protocol to perform its specified actions
391                let _ = waiter.await;
392            }
393        }
394
395        let conn = self.connections.remove(addr);
396        let disconnected = conn.is_some();
397
398        if let Some(conn) = conn {
399            debug!(parent: self.span(), "Disconnecting from {addr}");
400
401            // Shut down the associated tasks of the peer.
402            drop(conn);
403
404            debug!(parent: self.span(), "Disconnected from {addr}");
405        } else {
406            warn!(parent: self.span(), "Failed to disconnect, was not connected to {addr}");
407        }
408
409        disconnected
410    }
411}
412
413impl Tcp {
414    /// Spawns a task that listens for incoming connections.
415    pub async fn enable_listener(&self) -> io::Result<SocketAddr> {
416        // Retrieve the listening IP address, which must be set.
417        let listener_ip =
418            self.config().listener_ip.expect("Tcp::enable_listener was called, but Config::listener_ip is not set");
419
420        // Initialize the TCP listener.
421        let listener = self.create_listener(listener_ip).await?;
422
423        // Discover the port, if it was unspecified.
424        let port = listener.local_addr()?.port();
425
426        // Set the listening IP address.
427        let listening_addr = (listener_ip, port).into();
428        self.listening_addr.set(listening_addr).expect("The node's listener was started more than once");
429
430        // Use a channel to know when the listening task is ready.
431        let (tx, rx) = oneshot::channel();
432
433        // Cap the number of in-flight inbound connection handlers; the hard
434        // connection limits are still enforced inside `can_add_connection`;
435        // this bound exists separately to prevent per-SYN task-creation overhead
436        // from being unbounded under flood.
437        let inbound_permits = Arc::new(Semaphore::new(self.config.max_connections as usize));
438
439        let tcp = self.clone();
440        let listening_task = tokio::spawn(async move {
441            trace!(parent: tcp.span(), "Spawned the listening task");
442            tx.send(()).unwrap(); // safe; the channel was just opened
443
444            loop {
445                // Wait for capacity before accepting.
446                let permit = match inbound_permits.clone().acquire_owned().await {
447                    Ok(p) => p,
448                    Err(_) => {
449                        // semaphore is never closed in practice; bail defensively
450                        error!(parent: tcp.span(), "Inbound permit semaphore closed unexpectedly");
451                        return;
452                    }
453                };
454
455                // Await connection requests from peers.
456                match listener.accept().await {
457                    Ok((stream, addr)) => tcp.handle_connection(stream, addr, permit),
458                    Err(e) => {
459                        // Free the permit immediately.
460                        drop(permit);
461
462                        match e.kind() {
463                            // A peer aborted/reset before accept completed; no backoff - the listener is healthy.
464                            io::ErrorKind::ConnectionAborted | io::ErrorKind::ConnectionReset => {
465                                debug!(parent: tcp.span(), "Transient accept error: {e}");
466                            }
467                            // Otherwise, assume fd / memory exhaustion (EMFILE, ENFILE, ENOBUFS, ...)
468                            // and back off so we don't spin at 100% CPU waiting for a slot to free.
469                            _ => {
470                                error!(parent: tcp.span(), "Couldn't accept a connection: {e}");
471                                tokio::time::sleep(Duration::from_millis(500)).await;
472                            }
473                        }
474                    }
475                }
476            }
477        });
478        self.tasks.lock().push(listening_task);
479        let _ = rx.await;
480        debug!(parent: self.span(), "Listening on {listening_addr}");
481
482        Ok(listening_addr)
483    }
484
485    /// Creates an instance of `TcpListener` based on the node's configuration.
486    async fn create_listener(&self, listener_ip: IpAddr) -> io::Result<TcpListener> {
487        debug!("Creating a TCP listener on {listener_ip}...");
488        let listener = if let Some(port) = self.config().desired_listening_port {
489            // Construct the desired listening IP address.
490            let desired_listening_addr = SocketAddr::new(listener_ip, port);
491            // If a desired listening port is set, try to bind to it.
492            match TcpListener::bind(desired_listening_addr).await {
493                Ok(listener) => listener,
494                Err(e) => {
495                    if self.config().allow_random_port {
496                        warn!(
497                            parent: self.span(),
498                            "Trying any listening port, as the desired port is unavailable: {e}"
499                        );
500                        let random_available_addr = SocketAddr::new(listener_ip, 0);
501                        TcpListener::bind(random_available_addr).await?
502                    } else {
503                        error!(parent: self.span(), "The desired listening port is unavailable: {e}");
504                        return Err(e);
505                    }
506                }
507            }
508        } else if self.config().allow_random_port {
509            let random_available_addr = SocketAddr::new(listener_ip, 0);
510            TcpListener::bind(random_available_addr).await?
511        } else {
512            panic!("As 'listener_ip' is set, either 'desired_listening_port' or 'allow_random_port' must be set");
513        };
514
515        Ok(listener)
516    }
517
518    /// Handles a new inbound connection.
519    fn handle_connection(&self, stream: TcpStream, addr: SocketAddr, permit: OwnedSemaphorePermit) {
520        debug!(parent: self.span(), "Received a connection from {addr}");
521
522        if !self.can_add_connection() || self.is_self_connect(addr) {
523            debug!(parent: self.span(), "Rejecting the connection from {addr}");
524            return;
525        }
526
527        self.connecting.lock().insert(addr);
528
529        let tcp = self.clone();
530        tokio::spawn(async move {
531            // The permit is released when the connection is accepted or fails.
532            let _permit = permit;
533
534            if let Err(e) = tcp.adapt_stream(stream, addr, ConnectionSide::Responder).await {
535                tcp.connecting.lock().remove(&addr);
536                tcp.known_peers().register_failure(addr.ip());
537                error!(parent: tcp.span(), "Failed to connect with {addr}: {e}");
538            }
539        });
540    }
541
542    /// Checks if the given IP address is the same as the listening address of this `Tcp`.
543    fn is_self_connect(&self, addr: SocketAddr) -> bool {
544        // SAFETY: if we're opening connections, this should never fail.
545        let listening_addr = self.listening_addr().unwrap();
546
547        match listening_addr.ip().is_loopback() {
548            // If localhost, check the ports, this only works on outbound connections, since we
549            // don't know the ephemeral port a peer might be using if they initiate the connection.
550            true => listening_addr.port() == addr.port(),
551            // If it's not localhost, matching IPs indicate a self-connect in both directions.
552            false => listening_addr.ip() == addr.ip(),
553        }
554    }
555
556    /// Checks whether the `Tcp` can handle an additional connection.
557    fn can_add_connection(&self) -> bool {
558        // Retrieve the number of connected peers.
559        let num_connected = self.num_connected();
560        // Retrieve the maximum number of connected peers.
561        let limit = self.config.max_connections as usize;
562
563        if num_connected >= limit {
564            warn!(parent: self.span(), "Maximum number of active connections ({limit}) reached");
565            false
566        } else if num_connected + self.num_connecting() >= limit {
567            warn!(parent: self.span(), "Maximum number of active & pending connections ({limit}) reached");
568            false
569        } else {
570            true
571        }
572    }
573
574    /// Prepares the freshly acquired connection to handle the protocols the Tcp implements.
575    async fn adapt_stream(&self, stream: TcpStream, peer_addr: SocketAddr, own_side: ConnectionSide) -> io::Result<()> {
576        self.known_peers.add(peer_addr.ip());
577
578        // Register the port seen by the peer.
579        if own_side == ConnectionSide::Initiator {
580            if let Ok(addr) = stream.local_addr() {
581                debug!(
582                    parent: self.span(), "establishing connection with {}; the peer is connected on port {}",
583                    peer_addr, addr.port()
584                );
585            } else {
586                warn!(parent: self.span(), "couldn't determine the peer's port");
587            }
588        }
589
590        let conn_span = create_connection_span(peer_addr, self.span());
591        let connection = Connection::new(peer_addr, stream, !own_side, conn_span);
592
593        // Enact the enabled protocols.
594        let mut connection = self.enable_protocols(connection).await?;
595
596        // if Reading is enabled, we'll notify the related task when the connection is fully ready.
597        let conn_ready_tx = connection.readiness_notifier.take();
598
599        self.connections.add(connection);
600        self.connecting.lock().remove(&peer_addr);
601
602        // Send the aforementioned notification so that reading from the socket can commence.
603        if let Some(tx) = conn_ready_tx {
604            let _ = tx.send(());
605        }
606
607        // If enabled, enact OnConnect.
608        if let Some(handler) = self.protocols.on_connect.get() {
609            let (sender, receiver) = oneshot::channel();
610            handler.trigger((peer_addr, sender)).await;
611            // Receive the handle for the running task.
612            if let Ok(handle) = receiver.await {
613                // Add the task to the connection so it gets aborted on disconnect.
614                if let Some(conn) = self.connections.0.write().get_mut(&peer_addr) {
615                    conn.tasks.push(handle);
616                } else {
617                    // The connection has just been terminated; abort the OnConnect work.
618                    handle.abort();
619                }
620            }
621        }
622
623        Ok(())
624    }
625
626    /// Enacts the enabled protocols on the provided connection.
627    async fn enable_protocols(&self, conn: Connection) -> io::Result<Connection> {
628        /// A helper macro to enable a protocol on a connection.
629        macro_rules! enable_protocol {
630            ($handler_type: ident, $node:expr, $conn: expr) => {
631                if let Some(handler) = $node.protocols.$handler_type.get() {
632                    let (conn_returner, conn_retriever) = oneshot::channel();
633
634                    handler.trigger(($conn, conn_returner)).await;
635
636                    match conn_retriever.await {
637                        Ok(Ok(conn)) => conn,
638                        Err(_) => return Err(io::ErrorKind::BrokenPipe.into()),
639                        Ok(e) => return e,
640                    }
641                } else {
642                    $conn
643                }
644            };
645        }
646
647        let mut conn = enable_protocol!(handshake, self, conn);
648
649        // Split the stream after the handshake (if not done before).
650        if let Some(stream) = conn.stream.take() {
651            let (reader, writer) = split(stream);
652            conn.reader = Some(Box::new(reader));
653            conn.writer = Some(Box::new(writer));
654        }
655
656        let conn = enable_protocol!(reading, self, conn);
657        let conn = enable_protocol!(writing, self, conn);
658
659        Ok(conn)
660    }
661}
662
663impl fmt::Debug for Tcp {
664    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
665        write!(f, "The TCP stack config: {:?}", self.config)
666    }
667}
668
669#[cfg(test)]
670mod tests {
671    use super::*;
672
673    use std::{
674        net::{IpAddr, Ipv4Addr},
675        str::FromStr,
676    };
677
678    #[tokio::test]
679    async fn test_new() {
680        let tcp = Tcp::new(Config {
681            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
682            max_connections: 200,
683            ..Default::default()
684        });
685
686        assert_eq!(tcp.config.max_connections, 200);
687        assert_eq!(tcp.config.listener_ip, Some(IpAddr::V4(Ipv4Addr::LOCALHOST)));
688        assert_eq!(tcp.enable_listener().await.unwrap().ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
689
690        assert_eq!(tcp.num_connected(), 0);
691        assert_eq!(tcp.num_connecting(), 0);
692    }
693
694    #[tokio::test]
695    async fn test_connect() {
696        let tcp = Tcp::new(Config::default());
697        let node_ip = tcp.enable_listener().await.unwrap();
698
699        // Ensure self-connecting is not possible.
700        let result = tcp.connect(node_ip).await;
701        assert!(matches!(result, Err(ConnectError::SelfConnect { .. })));
702
703        assert_eq!(tcp.num_connected(), 0);
704        assert_eq!(tcp.num_connecting(), 0);
705        assert!(!tcp.is_connected(node_ip));
706        assert!(!tcp.is_connecting(node_ip));
707
708        // Initialize the peer.
709        let peer = Tcp::new(Config {
710            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
711            desired_listening_port: Some(0),
712            max_connections: 1,
713            ..Default::default()
714        });
715        let peer_ip = peer.enable_listener().await.unwrap();
716
717        // Connect to the peer.
718        tcp.connect(peer_ip).await.unwrap();
719        assert_eq!(tcp.num_connected(), 1);
720        assert_eq!(tcp.num_connecting(), 0);
721        assert!(tcp.is_connected(peer_ip));
722        assert!(!tcp.is_connecting(peer_ip));
723    }
724
725    #[tokio::test]
726    async fn test_disconnect() {
727        let tcp = Tcp::new(Config::default());
728        let _node_ip = tcp.enable_listener().await.unwrap();
729
730        // Initialize the peer.
731        let peer = Tcp::new(Config {
732            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
733            desired_listening_port: Some(0),
734            max_connections: 1,
735            ..Default::default()
736        });
737        let peer_ip = peer.enable_listener().await.unwrap();
738
739        // Connect to the peer.
740        tcp.connect(peer_ip).await.unwrap();
741        assert_eq!(tcp.num_connected(), 1);
742        assert_eq!(tcp.num_connecting(), 0);
743        assert!(tcp.is_connected(peer_ip));
744        assert!(!tcp.is_connecting(peer_ip));
745
746        // Disconnect from the peer.
747        let has_disconnected = tcp.disconnect(peer_ip).await;
748        assert!(has_disconnected);
749        assert_eq!(tcp.num_connected(), 0);
750        assert_eq!(tcp.num_connecting(), 0);
751        assert!(!tcp.is_connected(peer_ip));
752        assert!(!tcp.is_connecting(peer_ip));
753
754        // Ensure disconnecting from the peer a second time is okay.
755        let has_disconnected = tcp.disconnect(peer_ip).await;
756        assert!(!has_disconnected);
757        assert_eq!(tcp.num_connected(), 0);
758        assert_eq!(tcp.num_connecting(), 0);
759        assert!(!tcp.is_connected(peer_ip));
760        assert!(!tcp.is_connecting(peer_ip));
761    }
762
763    #[tokio::test]
764    async fn test_can_add_connection() {
765        let tcp = Tcp::new(Config { max_connections: 1, ..Default::default() });
766
767        // Initialize the peer.
768        let peer = Tcp::new(Config {
769            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
770            desired_listening_port: Some(0),
771            max_connections: 1,
772            ..Default::default()
773        });
774        let peer_ip = peer.enable_listener().await.unwrap();
775
776        assert!(tcp.can_add_connection());
777
778        // Simulate an active connection.
779        let stream = TcpStream::connect(peer_ip).await.unwrap();
780        tcp.connections.add(Connection::new(peer_ip, stream, ConnectionSide::Initiator, Span::none()));
781        assert!(!tcp.can_add_connection());
782
783        // Ensure that we cannot invoke connect() successfully in this case.
784        // Use a non-local IP, to ensure it is never qual to peer IP.
785        let another_ip = SocketAddr::from_str("1.2.3.4:4242").unwrap();
786        let result = tcp.connect(another_ip).await;
787        assert!(matches!(result, Err(ConnectError::MaximumConnectionsReached { .. })));
788
789        // Remove the active connection.
790        tcp.connections.remove(peer_ip);
791        assert!(tcp.can_add_connection());
792
793        // Simulate a pending connection.
794        tcp.connecting.lock().insert(peer_ip);
795        assert!(!tcp.can_add_connection());
796
797        // Ensure that we cannot invoke connect() successfully in this case either.
798        let another_ip = SocketAddr::from_str("1.2.3.4:4242").unwrap();
799        let result = tcp.connect(another_ip).await;
800        assert!(matches!(result, Err(ConnectError::MaximumConnectionsReached { .. })));
801
802        // Remove the pending connection.
803        tcp.connecting.lock().remove(&peer_ip);
804        assert!(tcp.can_add_connection());
805
806        // Simulate an active and a pending connection (this case should never occur).
807        let stream = TcpStream::connect(peer_ip).await.unwrap();
808        tcp.connections.add(Connection::new(peer_ip, stream, ConnectionSide::Responder, Span::none()));
809        tcp.connecting.lock().insert(peer_ip);
810        assert!(!tcp.can_add_connection());
811
812        // Remove the active and pending connection.
813        tcp.connections.remove(peer_ip);
814        tcp.connecting.lock().remove(&peer_ip);
815        assert!(tcp.can_add_connection());
816    }
817
818    #[tokio::test]
819    async fn test_handle_connection() {
820        let tcp = Tcp::new(Config {
821            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
822            max_connections: 1,
823            ..Default::default()
824        });
825
826        // Initialize peer 1.
827        let peer1 = Tcp::new(Config {
828            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
829            desired_listening_port: Some(0),
830            max_connections: 1,
831            ..Default::default()
832        });
833        let peer1_ip = peer1.enable_listener().await.unwrap();
834
835        // Simulate an active connection.
836        let stream = TcpStream::connect(peer1_ip).await.unwrap();
837        tcp.connections.add(Connection::new(peer1_ip, stream, ConnectionSide::Responder, Span::none()));
838        assert!(!tcp.can_add_connection());
839        assert_eq!(tcp.num_connected(), 1);
840        assert_eq!(tcp.num_connecting(), 0);
841        assert!(tcp.is_connected(peer1_ip));
842        assert!(!tcp.is_connecting(peer1_ip));
843
844        // Initialize peer 2.
845        let peer2 = Tcp::new(Config {
846            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
847            desired_listening_port: Some(0),
848            max_connections: 1,
849            ..Default::default()
850        });
851        let peer2_ip = peer2.enable_listener().await.unwrap();
852
853        // Handle the connection.
854        let stream = TcpStream::connect(peer2_ip).await.unwrap();
855        let inbound_permits = Arc::new(Semaphore::new(1));
856        let permit = inbound_permits.clone().acquire_owned().await.unwrap();
857        tcp.handle_connection(stream, peer2_ip, permit);
858        assert!(!tcp.can_add_connection());
859        assert_eq!(tcp.num_connected(), 1);
860        assert_eq!(tcp.num_connecting(), 0);
861        assert!(tcp.is_connected(peer1_ip));
862        assert!(!tcp.is_connected(peer2_ip));
863        assert!(!tcp.is_connecting(peer1_ip));
864        assert!(!tcp.is_connecting(peer2_ip));
865    }
866
867    #[tokio::test]
868    async fn test_adapt_stream() {
869        let tcp = Tcp::new(Config { max_connections: 1, ..Default::default() });
870
871        // Initialize the peer.
872        let peer = Tcp::new(Config {
873            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
874            desired_listening_port: Some(0),
875            max_connections: 1,
876            ..Default::default()
877        });
878        let peer_ip = peer.enable_listener().await.unwrap();
879
880        // Simulate a pending connection.
881        tcp.connecting.lock().insert(peer_ip);
882        assert_eq!(tcp.num_connected(), 0);
883        assert_eq!(tcp.num_connecting(), 1);
884        assert!(!tcp.is_connected(peer_ip));
885        assert!(tcp.is_connecting(peer_ip));
886
887        // Simulate a new connection.
888        let stream = TcpStream::connect(peer_ip).await.unwrap();
889        tcp.adapt_stream(stream, peer_ip, ConnectionSide::Responder).await.unwrap();
890        assert_eq!(tcp.num_connected(), 1);
891        assert_eq!(tcp.num_connecting(), 0);
892        assert!(tcp.is_connected(peer_ip));
893        assert!(!tcp.is_connecting(peer_ip));
894    }
895}