Skip to main content

snarkos_node_network/
peer.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 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    /// The last time we attempted to connect to the peer.
52    /// `None` if there was no attempt to connect since the peer was last connected, or no attempt at all.
53    pub last_connection_attempt: Option<Instant>,
54    /// The total number of connection attempts, since the peer was last connected.
55    pub total_connection_attempts: u32,
56}
57
58/// A fully connected peer.
59#[derive(Clone, Debug)]
60pub struct ConnectedPeer<N: Network> {
61    /// The listener address of the peer.
62    pub listener_addr: SocketAddr,
63    /// The connected address of the peer.
64    pub connected_addr: SocketAddr,
65    /// Indicates whether this is a Router or a Gateway connection for the peer.
66    pub connection_mode: ConnectionMode,
67    /// Indicates whether the peer is considered trusted.
68    pub trusted: bool,
69    /// The Aleo address of the peer.
70    pub aleo_addr: Address<N>,
71    /// The node type of the peer.
72    pub node_type: NodeType,
73    /// The message version of the peer.
74    pub version: u32,
75    /// The snarkOS commit hash of the peer.
76    pub snarkos_sha: Option<[u8; 40]>,
77    /// The latest block height known to be associated with the peer.
78    pub last_height_seen: Option<u32>,
79    /// The timestamp of the first message received from the peer.
80    pub first_seen: Instant,
81    /// The timestamp of the last message received from this peer.
82    pub last_seen: Instant,
83}
84
85/// Indicates whether a peer is connected via the Gateway or the Router.
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
87pub enum ConnectionMode {
88    Gateway,
89    Router,
90}
91
92impl<N: Network> Peer<N> {
93    /// Create a candidate peer.
94    pub const fn new_candidate(listener_addr: SocketAddr, trusted: bool) -> Self {
95        Self::Candidate(CandidatePeer {
96            listener_addr,
97            trusted,
98            last_height_seen: None,
99            last_connection_attempt: None,
100            total_connection_attempts: 0,
101        })
102    }
103
104    /// Create a connecting peer.
105    pub const fn new_connecting(listener_addr: SocketAddr, trusted: bool) -> Self {
106        Self::Connecting(ConnectingPeer { listener_addr, trusted })
107    }
108
109    /// Promote a connecting peer to a fully connected one.
110    #[allow(clippy::too_many_arguments)]
111    pub fn upgrade_to_connected(
112        &mut self,
113        connected_addr: SocketAddr,
114        listener_port: u16,
115        aleo_address: Address<N>,
116        node_type: NodeType,
117        node_version: u32,
118        snarkos_sha: Option<[u8; 40]>,
119        connection_mode: ConnectionMode,
120    ) {
121        let timestamp = Instant::now();
122        let listener_addr = SocketAddr::from((connected_addr.ip(), listener_port));
123
124        // Logic check: this can only happen during the handshake. This isn't a fatal
125        // error, but should not be triggered.
126        if !matches!(self, Self::Connecting(_)) {
127            warn!("Peer '{listener_addr}' is being upgraded to Connected, but isn't Connecting");
128        }
129
130        *self = Self::Connected(ConnectedPeer {
131            listener_addr,
132            connected_addr,
133            connection_mode,
134            aleo_addr: aleo_address,
135            node_type,
136            trusted: self.is_trusted(),
137            version: node_version,
138            snarkos_sha,
139            last_height_seen: None,
140            first_seen: timestamp,
141            last_seen: timestamp,
142        });
143    }
144
145    /// Demote a peer to candidate status, marking it as disconnected.
146    pub fn downgrade_to_candidate(&mut self, listener_addr: SocketAddr) {
147        *self = Self::Candidate(CandidatePeer {
148            listener_addr,
149            trusted: self.is_trusted(),
150            last_height_seen: self.last_height_seen(),
151            last_connection_attempt: None,
152            total_connection_attempts: 0,
153        });
154    }
155
156    /// Returns the type of the node (only applicable to connected peers).
157    pub fn node_type(&self) -> Option<NodeType> {
158        match self {
159            Self::Candidate(_) => None,
160            Self::Connecting(_) => None,
161            Self::Connected(peer) => Some(peer.node_type),
162        }
163    }
164
165    /// The listener (public) address of this peer.
166    pub fn listener_addr(&self) -> SocketAddr {
167        match self {
168            Self::Candidate(p) => p.listener_addr,
169            Self::Connecting(p) => p.listener_addr,
170            Self::Connected(p) => p.listener_addr,
171        }
172    }
173
174    /// The listener (public) address of this peer.
175    pub fn last_height_seen(&self) -> Option<u32> {
176        match self {
177            Self::Candidate(_) => None,
178            Self::Connecting(_) => None,
179            Self::Connected(peer) => peer.last_height_seen,
180        }
181    }
182
183    /// Returns `true` if the peer is not connected or connecting.
184    pub fn is_candidate(&self) -> bool {
185        matches!(self, Peer::Candidate(_))
186    }
187
188    /// Returns `true` if the peer is currently undergoing the network handshake.
189    pub fn is_connecting(&self) -> bool {
190        matches!(self, Peer::Connecting(_))
191    }
192
193    /// Returns `true` if the peer has concluded the network handshake.
194    pub fn is_connected(&self) -> bool {
195        matches!(self, Peer::Connected(_))
196    }
197
198    /// Returns `true` if the peer is considered trusted.
199    pub fn is_trusted(&self) -> bool {
200        match self {
201            Self::Candidate(peer) => peer.trusted,
202            Self::Connecting(peer) => peer.trusted,
203            Self::Connected(peer) => peer.trusted,
204        }
205    }
206
207    /// Updates the peer's `last_seen` timestamp.
208    pub fn update_last_seen(&mut self) {
209        if let Self::Connected(ConnectedPeer { last_seen, .. }) = self {
210            *last_seen = Instant::now();
211        }
212    }
213}