mod.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphaos library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 mod codec; 20 mod handshake; 21 mod network; 22 23 use crate::tcp::{self, Tcp}; 24 use alphaos_account::Account; 25 use alphaos_node_network::{ConnectionMode, Peer, Resolver}; 26 use alphaos_node_tcp::{protocols::*, P2P}; 27 use alphaos_utilities::SignalHandler; 28 use alphavm::{ 29 ledger::committee::Committee, 30 prelude::{Address, Field, Header, Network, PrivateKey, ViewKey}, 31 synthesizer::Restrictions, 32 }; 33 34 #[cfg(feature = "locktick")] 35 use locktick::{ 36 parking_lot::{Mutex, RwLock}, 37 tokio::Mutex as TMutex, 38 }; 39 #[cfg(not(feature = "locktick"))] 40 use parking_lot::{Mutex, RwLock}; 41 use std::{ 42 collections::{HashMap, HashSet}, 43 net::SocketAddr, 44 ops::Deref, 45 str::FromStr, 46 sync::Arc, 47 time::{Duration, Instant}, 48 }; 49 use tokio::sync::oneshot; 50 #[cfg(not(feature = "locktick"))] 51 use tokio::sync::Mutex as TMutex; 52 53 #[derive(Clone)] 54 pub struct BootstrapClient<N: Network>(Arc<InnerBootstrapClient<N>>); 55 56 impl<N: Network> Deref for BootstrapClient<N> { 57 type Target = Arc<InnerBootstrapClient<N>>; 58 59 fn deref(&self) -> &Self::Target { 60 &self.0 61 } 62 } 63 64 // A tuple holding the validator's Alpha address, and its connection mode. 65 type KnownValidatorInfo<N> = (Address<N>, ConnectionMode); 66 67 pub struct InnerBootstrapClient<N: Network> { 68 tcp: Tcp, 69 peer_pool: RwLock<HashMap<SocketAddr, Peer<N>>>, 70 known_validators: RwLock<HashMap<SocketAddr, KnownValidatorInfo<N>>>, 71 resolver: RwLock<Resolver<N>>, 72 account: Account<N>, 73 genesis_header: Header<N>, 74 restrictions_id: Field<N>, 75 http_client: reqwest::Client, 76 latest_committee: TMutex<(HashSet<Address<N>>, Instant)>, 77 dev: Option<u16>, 78 shutdown_tx: Mutex<Option<oneshot::Sender<()>>>, 79 } 80 81 impl<N: Network> BootstrapClient<N> { 82 // The interval for validator committee refreshes. 83 const COMMITTEE_REFRESH_TIME: Duration = Duration::from_secs(20); 84 // The maximum amount of time per connection. 85 const CONNECTION_LIFETIME: Duration = Duration::from_secs(15); 86 // The maximum number of connected peers. 87 const MAX_PEERS: u16 = 1_000; 88 89 pub async fn new( 90 listener_addr: SocketAddr, 91 account: Account<N>, 92 genesis_header: Header<N>, 93 dev: Option<u16>, 94 ) -> anyhow::Result<Self> { 95 // Initialize the TCP stack. 96 let tcp = Tcp::new(tcp::Config::new(listener_addr, Self::MAX_PEERS)); 97 // Initialize the peer pool. 98 let peer_pool = Default::default(); 99 // Initialize a collection of validators. 100 let known_validators = Default::default(); 101 // Load the restrictions ID. 102 let restrictions_id = Restrictions::load()?.restrictions_id(); 103 // Create a resolver. 104 let resolver = Default::default(); 105 // Create an HTTP client to obtain the current committee. 106 let http_client = reqwest::Client::new(); 107 // Prepare a placeholder committee, ensuring that it's insta-outdated. 108 let latest_committee = TMutex::new((Default::default(), Instant::now() - Self::COMMITTEE_REFRESH_TIME)); 109 110 // Prepare the shutdown channel. 111 let (shutdown_tx, shutdown_rx) = oneshot::channel(); 112 let shutdown_tx = Mutex::new(Some(shutdown_tx)); 113 114 // Construct and return the bootstrap client. 115 let inner = InnerBootstrapClient { 116 tcp, 117 peer_pool, 118 known_validators, 119 resolver, 120 account, 121 genesis_header, 122 restrictions_id, 123 http_client, 124 latest_committee, 125 dev, 126 shutdown_tx, 127 }; 128 let node = BootstrapClient(Arc::new(inner)); 129 130 // Enable the TCP protocols. 131 node.enable_handshake().await; 132 node.enable_reading().await; 133 node.enable_writing().await; 134 node.enable_disconnect().await; 135 node.enable_on_connect().await; 136 // Enable the TCP listener. Note: This must be called after the above protocols. 137 node.tcp().enable_listener().await.expect("Failed to enable the TCP listener"); 138 139 // Await the shutdown signal. 140 let _ = shutdown_rx.await; 141 142 Ok(node) 143 } 144 145 /// Returns the account address of the node. 146 pub fn address(&self) -> Address<N> { 147 self.account.address() 148 } 149 150 /// Returns the account private key of the node. 151 pub fn private_key(&self) -> &PrivateKey<N> { 152 self.account.private_key() 153 } 154 155 /// Returns the account view key of the node. 156 pub fn view_key(&self) -> &ViewKey<N> { 157 self.account.view_key() 158 } 159 160 /// Returns the listener IP address from the connected peer address. 161 pub fn resolve_to_listener(&self, connected_addr: SocketAddr) -> Option<SocketAddr> { 162 self.resolver.read().get_listener(connected_addr) 163 } 164 165 /// Returns `true` if the node is in development mode. 166 pub fn is_dev(&self) -> bool { 167 self.dev.is_some() 168 } 169 170 /// Returns the current validator committee or updates it from the explorer, if 171 /// we are capable of obtaining it from the network. 172 pub async fn get_or_update_committee(&self) -> anyhow::Result<Option<HashSet<Address<N>>>> { 173 // Development testing may include a list of committee Alpha addresses loaded from the environment. 174 if cfg!(feature = "test") || self.is_dev() { 175 match std::env::var("TEST_COMMITTEE_ADDRS") { 176 Ok(alpha_addrs) => { 177 let dev_committee = 178 alpha_addrs.split(',').map(|addr| Address::<N>::from_str(addr).unwrap()).collect(); 179 return Ok(Some(dev_committee)); 180 } 181 Err(err) => { 182 warn!("Failed to load committee peers from environment: {err}"); 183 return Ok(None); 184 } 185 } 186 } 187 188 let now = Instant::now(); 189 let (committee, timestamp) = &mut *self.latest_committee.lock().await; 190 if now - *timestamp >= Self::COMMITTEE_REFRESH_TIME { 191 debug!("Updating the validator committee"); 192 *timestamp = now; 193 let committe_query_addr = format!("https://api.explorer.provable.com/v2/{}/committee/latest", N::NAME); 194 let response = self.http_client.get(committe_query_addr).send().await?; 195 let json = response.text().await?; 196 let full_committee = Committee::from_str(&json)?; 197 *committee = full_committee.members().keys().copied().collect(); 198 debug!("The validator committee has {} members now", committee.len()); 199 200 Ok(Some(committee.clone())) 201 } else { 202 Ok(Some(committee.clone())) 203 } 204 } 205 206 // Return the known addresses of current committee members, or all known 207 // validators if the committee info is unavailable. 208 pub async fn get_validator_addrs(&self) -> HashMap<SocketAddr, KnownValidatorInfo<N>> { 209 // First, collect info on all the validators we had connected to before. 210 let mut known_validators = self.known_validators.read().clone(); 211 // If the committee info is available, prune non-committee members. 212 match self.get_or_update_committee().await { 213 Ok(Some(committee)) => { 214 known_validators.retain(|_, (alpha_addr, _)| committee.contains(alpha_addr)); 215 known_validators 216 } 217 Ok(None) => known_validators, 218 Err(error) => { 219 error!("Couldn't update the validator committee: {error}"); 220 known_validators 221 } 222 } 223 } 224 225 /// Shuts down the bootstrap client. 226 pub async fn shut_down(&self) { 227 info!("Shutting down the bootstrap client..."); 228 229 // Shut down the low-level network features. 230 self.tcp.shut_down().await; 231 232 // Shut down the node. 233 if let Some(shutdown_tx) = self.shutdown_tx.lock().take() { 234 let _ = shutdown_tx.send(()); 235 } 236 } 237 238 /// Blocks until a shutdown signal was received or manual shutdown was triggered. 239 pub async fn wait_for_signals(&self, handler: &SignalHandler) { 240 handler.wait_for_signals().await; 241 242 // If the node is already initialized, then shut it down. 243 self.shut_down().await; 244 } 245 }