1use crate::NodeType;
17use snarkvm::prelude::{Address, FromBytes, Network, ToBytes, error};
18use tracing::*;
19
20use std::{fmt, io, net::SocketAddr, time::Instant};
21
22#[derive(Clone, Debug)]
24pub enum Peer<N: Network> {
25 Candidate(CandidatePeer<N>),
27 Connecting(ConnectingPeer),
29 Connected(ConnectedPeer<N>),
31}
32
33#[derive(Clone, Debug)]
35pub struct ConnectingPeer {
36 pub listener_addr: SocketAddr,
38 pub trusted: bool,
40 pub last_connection_attempt: Option<Instant>,
42 pub total_connection_attempts: u32,
44}
45
46#[derive(Clone, Debug)]
48pub struct CandidatePeer<N: Network> {
49 pub listener_addr: SocketAddr,
51 pub trusted: bool,
53 pub last_height_seen: Option<u32>,
55 pub last_connection_attempt: Option<Instant>,
58 pub total_connection_attempts: u32,
60 pub last_known_aleo_addr: Option<Address<N>>,
63}
64
65#[derive(Clone, Debug)]
67pub struct ConnectedPeer<N: Network> {
68 pub listener_addr: SocketAddr,
70 pub connected_addr: SocketAddr,
72 pub connection_mode: ConnectionMode,
74 pub trusted: bool,
76 pub aleo_addr: Address<N>,
78 pub node_type: NodeType,
80 pub version: u32,
82 pub snarkos_sha: Option<[u8; 40]>,
84 pub last_height_seen: Option<u32>,
86 pub first_seen: Instant,
88 pub last_seen: Instant,
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104#[repr(u8)]
105pub enum ConnectionMode {
106 Gateway = 0,
107 Router = 1,
108}
109
110impl ToBytes for ConnectionMode {
111 fn write_le<W: io::Write>(&self, writer: W) -> io::Result<()> {
112 (*self as u8).write_le(writer)
113 }
114}
115
116impl FromBytes for ConnectionMode {
117 fn read_le<R: io::Read>(reader: R) -> io::Result<Self> {
118 match u8::read_le(reader)? {
119 0 => Ok(Self::Gateway),
120 1 => Ok(Self::Router),
121 x => Err(error(format!("Invalid connection mode: expected 0..=1, got {x}."))),
122 }
123 }
124}
125
126impl fmt::Display for ConnectionMode {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match self {
129 ConnectionMode::Gateway => write!(f, "Gateway"),
130 ConnectionMode::Router => write!(f, "Router"),
131 }
132 }
133}
134
135impl<N: Network> Peer<N> {
136 pub fn new_candidate(listener_addr: SocketAddr, trusted: bool) -> Self {
138 Self::Candidate(CandidatePeer {
139 listener_addr,
140 trusted,
141 last_height_seen: None,
142 last_connection_attempt: None,
143 total_connection_attempts: 0,
144 last_known_aleo_addr: None,
145 })
146 }
147
148 pub const fn new_connecting(listener_addr: SocketAddr, trusted: bool) -> Self {
150 Self::Connecting(ConnectingPeer {
151 listener_addr,
152 trusted,
153 last_connection_attempt: None,
154 total_connection_attempts: 0,
155 })
156 }
157
158 pub fn promote_to_connecting(&mut self) {
160 let listener_addr = self.listener_addr();
161 let trusted = self.is_trusted();
162
163 let (last_connection_attempt, total_connection_attempts) = match self {
164 Self::Candidate(peer) => (peer.last_connection_attempt, peer.total_connection_attempts),
165 Self::Connecting(_) | Self::Connected(_) => {
166 warn!(
167 "Peer '{listener_addr}' is being promoted to Connecting, but is {}",
168 if self.is_connected() { "already Connected" } else { "already Connecting" }
169 );
170 (None, 0)
171 }
172 };
173
174 *self = Self::Connecting(ConnectingPeer {
175 listener_addr,
176 trusted,
177 last_connection_attempt,
178 total_connection_attempts,
179 });
180 }
181
182 #[allow(clippy::too_many_arguments)]
184 pub fn upgrade_to_connected(
185 &mut self,
186 connected_addr: SocketAddr,
187 listener_port: u16,
188 aleo_address: Address<N>,
189 node_type: NodeType,
190 node_version: u32,
191 snarkos_sha: Option<[u8; 40]>,
192 connection_mode: ConnectionMode,
193 ) {
194 let timestamp = Instant::now();
195 let listener_addr = SocketAddr::from((connected_addr.ip(), listener_port));
196
197 if !matches!(self, Self::Connecting(_)) {
200 warn!(
201 "Peer '{listener_addr}' is being upgraded to Connected, but isn't Connecting \
202 - it is {}",
203 if self.is_connected() { "already Connected" } else { "only a Candidate" }
204 );
205 }
206
207 *self = Self::Connected(ConnectedPeer {
208 listener_addr,
209 connected_addr,
210 connection_mode,
211 aleo_addr: aleo_address,
212 node_type,
213 trusted: self.is_trusted(),
214 version: node_version,
215 snarkos_sha,
216 last_height_seen: None,
217 first_seen: timestamp,
218 last_seen: timestamp,
219 });
220 }
221
222 pub fn downgrade_to_candidate(&mut self, listener_addr: SocketAddr) {
224 let last_known_aleo_addr = match self {
225 Self::Connected(p) => Some(p.aleo_addr),
226 _ => None,
227 };
228
229 let (last_connection_attempt, total_connection_attempts) = match self {
231 Self::Candidate(p) => (p.last_connection_attempt, p.total_connection_attempts),
232 Self::Connecting(p) => (p.last_connection_attempt, p.total_connection_attempts),
233 Self::Connected(_) => (None, 0),
234 };
235
236 *self = Self::Candidate(CandidatePeer {
237 listener_addr,
238 trusted: self.is_trusted(),
239 last_height_seen: self.last_height_seen(),
240 last_connection_attempt,
241 total_connection_attempts,
242 last_known_aleo_addr,
243 });
244 }
245
246 pub fn node_type(&self) -> Option<NodeType> {
248 match self {
249 Self::Candidate(_) => None,
250 Self::Connecting(_) => None,
251 Self::Connected(peer) => Some(peer.node_type),
252 }
253 }
254
255 pub fn listener_addr(&self) -> SocketAddr {
257 match self {
258 Self::Candidate(p) => p.listener_addr,
259 Self::Connecting(p) => p.listener_addr,
260 Self::Connected(p) => p.listener_addr,
261 }
262 }
263
264 pub fn last_height_seen(&self) -> Option<u32> {
266 match self {
267 Self::Candidate(_) => None,
268 Self::Connecting(_) => None,
269 Self::Connected(peer) => peer.last_height_seen,
270 }
271 }
272
273 pub fn failed_connection_attempts(&self) -> u32 {
275 match self {
276 Self::Candidate(peer) => peer.total_connection_attempts,
277 Self::Connecting(peer) => peer.total_connection_attempts,
278 Self::Connected(_) => 0,
279 }
280 }
281
282 pub fn is_candidate(&self) -> bool {
284 matches!(self, Peer::Candidate(_))
285 }
286
287 pub fn is_connecting(&self) -> bool {
289 matches!(self, Peer::Connecting(_))
290 }
291
292 pub fn is_connected(&self) -> bool {
294 matches!(self, Peer::Connected(_))
295 }
296
297 pub fn is_trusted(&self) -> bool {
299 match self {
300 Self::Candidate(peer) => peer.trusted,
301 Self::Connecting(peer) => peer.trusted,
302 Self::Connected(peer) => peer.trusted,
303 }
304 }
305
306 pub fn update_last_seen(&mut self) {
308 if let Self::Connected(ConnectedPeer { last_seen, .. }) = self {
309 *last_seen = Instant::now();
310 }
311 }
312
313 pub fn as_connected(&self) -> Option<&ConnectedPeer<N>> {
316 match self {
317 Self::Connected(peer) => Some(peer),
318 _ => None,
319 }
320 }
321}
322
323impl<N: Network> ConnectedPeer<N> {
324 pub fn is_validator(&self) -> bool {
326 self.node_type == NodeType::Validator
327 }
328}