Skip to main content

snarkos_node_network/
peer.rs

1// Copyright (c) 2019-2025 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 crate::NodeType;
17use snarkvm::prelude::{Address, Network};
18use tracing::*;
19
20use std::{net::SocketAddr, time::Instant};
21
22/// A peer of any connection status.
23#[derive(Clone, Debug)]
24pub enum Peer<N: Network> {
25    /// A candidate peer that's currently not connected to.
26    Candidate(CandidatePeer),
27    /// A peer that's currently being connected to (the handshake is in progress).
28    Connecting(ConnectingPeer),
29    /// A fully connected (post-handshake) peer.
30    Connected(ConnectedPeer<N>),
31}
32
33/// A connecting peer.
34#[derive(Clone, Debug)]
35pub struct ConnectingPeer {
36    /// The listening address of a connecting peer.
37    pub listener_addr: SocketAddr,
38    /// Indicates whether the peer is considered trusted.
39    pub trusted: bool,
40}
41
42/// A candidate peer.
43#[derive(Clone, Debug)]
44pub struct CandidatePeer {
45    /// The listening address of a candidate peer.
46    pub listener_addr: SocketAddr,
47    /// Indicates whether the peer is considered trusted.
48    pub trusted: bool,
49    /// The latest block height known to be associated with the peer.
50    pub last_height_seen: Option<u32>,
51}
52
53/// A fully connected peer.
54#[derive(Clone, Debug)]
55pub struct ConnectedPeer<N: Network> {
56    /// The listener address of the peer.
57    pub listener_addr: SocketAddr,
58    /// The connected address of the peer.
59    pub connected_addr: SocketAddr,
60    /// Indicates whether this is a Router or a Gateway connection for the peer.
61    pub connection_mode: ConnectionMode,
62    /// Indicates whether the peer is considered trusted.
63    pub trusted: bool,
64    /// The Aleo address of the peer.
65    pub aleo_addr: Address<N>,
66    /// The node type of the peer.
67    pub node_type: NodeType,
68    /// The message version of the peer.
69    pub version: u32,
70    /// The latest block height known to be associated with the peer.
71    pub last_height_seen: Option<u32>,
72    /// The timestamp of the first message received from the peer.
73    pub first_seen: Instant,
74    /// The timestamp of the last message received from this peer.
75    pub last_seen: Instant,
76}
77
78/// Indicates whether a peer is connected via the Gateway or the Router.
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
80pub enum ConnectionMode {
81    Gateway,
82    Router,
83}
84
85impl<N: Network> Peer<N> {
86    /// Create a candidate peer.
87    pub const fn new_candidate(listener_addr: SocketAddr, trusted: bool) -> Self {
88        Self::Candidate(CandidatePeer { listener_addr, trusted, last_height_seen: None })
89    }
90
91    /// Create a connecting peer.
92    pub const fn new_connecting(listener_addr: SocketAddr, trusted: bool) -> Self {
93        Self::Connecting(ConnectingPeer { listener_addr, trusted })
94    }
95
96    /// Promote a connecting peer to a fully connected one.
97    pub fn upgrade_to_connected(
98        &mut self,
99        connected_addr: SocketAddr,
100        listener_port: u16,
101        aleo_address: Address<N>,
102        node_type: NodeType,
103        node_version: u32,
104        connection_mode: ConnectionMode,
105    ) {
106        let timestamp = Instant::now();
107        let listener_addr = SocketAddr::from((connected_addr.ip(), listener_port));
108
109        // Logic check: this can only happen during the handshake. This isn't a fatal
110        // error, but should not be triggered.
111        if !matches!(self, Self::Connecting(_)) {
112            warn!("Peer '{listener_addr}' is being upgraded to Connected, but isn't Connecting");
113        }
114
115        *self = Self::Connected(ConnectedPeer {
116            listener_addr,
117            connected_addr,
118            connection_mode,
119            aleo_addr: aleo_address,
120            node_type,
121            trusted: self.is_trusted(),
122            version: node_version,
123            last_height_seen: None,
124            first_seen: timestamp,
125            last_seen: timestamp,
126        });
127    }
128
129    /// Demote a peer to candidate status, marking it as disconnected.
130    pub fn downgrade_to_candidate(&mut self, listener_addr: SocketAddr) {
131        *self = Self::Candidate(CandidatePeer {
132            listener_addr,
133            trusted: self.is_trusted(),
134            last_height_seen: self.last_height_seen(),
135        });
136    }
137
138    /// Returns the type of the node (only applicable to connected peers).
139    pub fn node_type(&self) -> Option<NodeType> {
140        match self {
141            Self::Candidate(_) => None,
142            Self::Connecting(_) => None,
143            Self::Connected(peer) => Some(peer.node_type),
144        }
145    }
146
147    /// The listener (public) address of this peer.
148    pub fn listener_addr(&self) -> SocketAddr {
149        match self {
150            Self::Candidate(p) => p.listener_addr,
151            Self::Connecting(p) => p.listener_addr,
152            Self::Connected(p) => p.listener_addr,
153        }
154    }
155
156    /// The listener (public) address of this peer.
157    pub fn last_height_seen(&self) -> Option<u32> {
158        match self {
159            Self::Candidate(_) => None,
160            Self::Connecting(_) => None,
161            Self::Connected(peer) => peer.last_height_seen,
162        }
163    }
164
165    /// Returns `true` if the peer is not connected or connecting.
166    pub fn is_candidate(&self) -> bool {
167        matches!(self, Peer::Candidate(_))
168    }
169
170    /// Returns `true` if the peer is currently undergoing the network handshake.
171    pub fn is_connecting(&self) -> bool {
172        matches!(self, Peer::Connecting(_))
173    }
174
175    /// Returns `true` if the peer has concluded the network handshake.
176    pub fn is_connected(&self) -> bool {
177        matches!(self, Peer::Connected(_))
178    }
179
180    /// Returns `true` if the peer is considered trusted.
181    pub fn is_trusted(&self) -> bool {
182        match self {
183            Self::Candidate(peer) => peer.trusted,
184            Self::Connecting(peer) => peer.trusted,
185            Self::Connected(peer) => peer.trusted,
186        }
187    }
188
189    /// Updates the peer's `last_seen` timestamp.
190    pub fn update_last_seen(&mut self) {
191        if let Self::Connected(ConnectedPeer { last_seen, .. }) = self {
192            *last_seen = Instant::now();
193        }
194    }
195}