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