Skip to main content

snarkos_node_network/
lib.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
16#![forbid(unsafe_code)]
17
18pub mod node_type;
19pub use node_type::*;
20
21pub mod noise;
22
23pub mod peer;
24pub use peer::*;
25
26pub mod peering;
27pub use peering::*;
28
29pub mod resolver;
30pub use resolver::*;
31
32use snarkvm::prelude::Network;
33
34use smol_str::SmolStr;
35use socket2::SockRef;
36use std::{env::VarError, io, net::SocketAddr, str::FromStr, time::Duration};
37use tokio::net::TcpStream;
38use tracing::*;
39
40// Include the generated build information.
41pub mod built_info {
42    include!(concat!(env!("OUT_DIR"), "/built.rs"));
43}
44
45/// Returns the list of bootstrap peers.
46#[allow(clippy::if_same_then_else)]
47pub fn bootstrap_peers<N: Network>(is_dev: bool) -> Vec<SocketAddr> {
48    if cfg!(feature = "test") || is_dev {
49        // Development testing contains optional bootstrap peers loaded from the environment.
50        match std::env::var("TEST_BOOTSTRAP_PEERS") {
51            Ok(peers) => peers.split(',').map(|peer| SocketAddr::from_str(peer).unwrap()).collect(),
52            Err(VarError::NotPresent) => {
53                // Return an empty list if the environment variable is not present.
54                vec![]
55            }
56            Err(err) => {
57                // Log other errors, e.g., invalid encoding.
58                warn!("Failed to load bootstrap peers from environment: {err}");
59                vec![]
60            }
61        }
62    } else if N::ID == snarkvm::console::network::MainnetV0::ID {
63        // Mainnet contains the following bootstrap peers.
64        vec![
65            SocketAddr::from_str("35.231.67.219:4130").unwrap(),
66            SocketAddr::from_str("34.73.195.196:4130").unwrap(),
67            SocketAddr::from_str("34.23.225.202:4130").unwrap(),
68            SocketAddr::from_str("34.148.16.111:4130").unwrap(),
69        ]
70    } else if N::ID == snarkvm::console::network::TestnetV0::ID {
71        // TestnetV0 contains the following bootstrap peers.
72        vec![
73            SocketAddr::from_str("34.138.104.159:4130").unwrap(),
74            SocketAddr::from_str("35.231.46.237:4130").unwrap(),
75            SocketAddr::from_str("34.148.251.155:4130").unwrap(),
76            SocketAddr::from_str("35.190.141.234:4130").unwrap(),
77        ]
78    } else if N::ID == snarkvm::console::network::CanaryV0::ID {
79        // CanaryV0 contains the following bootstrap peers.
80        vec![
81            SocketAddr::from_str("34.139.88.58:4130").unwrap(),
82            SocketAddr::from_str("34.139.252.207:4130").unwrap(),
83            SocketAddr::from_str("35.185.98.12:4130").unwrap(),
84            SocketAddr::from_str("35.231.106.26:4130").unwrap(),
85        ]
86    } else {
87        // Unrecognized networks contain no bootstrap peers.
88        vec![]
89    }
90}
91
92/// Get our SHA from the build information (or None if it is not set or does not 40 bytes long).
93pub fn get_repo_commit_hash() -> Option<[u8; 40]> {
94    built_info::GIT_COMMIT_HASH.and_then(|sha| sha.as_bytes().try_into().ok())
95}
96
97/// Logs the peer's snarkOS repo SHA and how it compares to ours.
98pub fn log_repo_sha_comparison(peer_addr: SocketAddr, peer_sha: &Option<[u8; 40]>, ctx: &str) {
99    let our_sha = get_repo_commit_hash();
100
101    // Generate a string representation for the peers hash.
102    let peer_sha_str: Option<&str> = peer_sha.as_ref().and_then(|h| str::from_utf8(h).ok());
103
104    let sha_cmp = match (&our_sha, peer_sha, peer_sha_str) {
105        // They sent no hash, or an invalid string.
106        (_, _, None) | (_, None, _) => " with an unknown repo SHA".to_owned(),
107        // Our hash cannot be retrieved.
108        (None, _, Some(theirs_str)) => format!("@{theirs_str} (potentially different than us)"),
109        // Both hashes are valid. Compare.
110        (Some(ours), Some(theirs), Some(theirs_str)) => {
111            if ours == theirs {
112                format!("@{theirs_str} (same as us)")
113            } else {
114                format!("@{theirs_str} (different than us)")
115            }
116        }
117    };
118
119    debug!("{ctx} Peer '{peer_addr}' uses snarkOS{sha_cmp}");
120}
121
122/// Shortens the commit SHA.
123pub fn shorten_snarkos_sha(sha: &Option<[u8; 40]>) -> SmolStr {
124    if let Some(full_sha) = sha.as_ref().and_then(|s| str::from_utf8(s).ok()) {
125        let end_idx = full_sha.char_indices()
126            .nth(7) // GitHub commit SHA shorthand.
127            .map(|(i, _)| i)
128            .unwrap_or(full_sha.len()); // Can't really fail.
129
130        SmolStr::from(&full_sha[..end_idx])
131    } else {
132        "unknown snarkOS SHA".into()
133    }
134}
135
136/// Adjusts the low-level socket settings for extra robustness.
137pub fn harden_socket(stream: &TcpStream) -> io::Result<()> {
138    let socket = SockRef::from(stream);
139
140    // Make OS-level disconnects immediate (no TIME_WAIT).
141    socket.set_linger(Some(Duration::from_secs(0)))?;
142
143    // Disable Nagle's algorithm for lower latency.
144    socket.set_tcp_nodelay(true)?;
145
146    // Disconnect if unacknowledged data stalls for 20s. This protects
147    // the kernel's retransmission queue.
148    #[cfg(target_os = "linux")]
149    socket.set_tcp_user_timeout(Some(Duration::from_secs(20)))?;
150
151    Ok(())
152}