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 router; 20 21 use crate::{ 22 bft::ledger_service::ProverLedgerService, 23 sync::{BlockSync, Ping}, 24 traits::NodeInterface, 25 }; 26 27 use alphaos_account::Account; 28 use alphaos_node_network::{NodeType, PeerPoolHandling}; 29 30 use alphaos_node_router::{ 31 messages::{Message, UnconfirmedSolution}, 32 Heartbeat, 33 Inbound, 34 Outbound, 35 Router, 36 Routing, 37 }; 38 use alphaos_node_tcp::{ 39 protocols::{Disconnect, Handshake, OnConnect, Reading}, 40 P2P, 41 }; 42 use alphaos_utilities::{SignalHandler, Stoppable}; 43 44 use alphavm::{ 45 ledger::narwhal::Data, 46 prelude::{ 47 block::{Block, Header}, 48 puzzle::{Puzzle, Solution}, 49 store::ConsensusStorage, 50 Network, 51 }, 52 synthesizer::VM, 53 }; 54 55 use alphaos_node_bft::helpers::fmt_id; 56 use alphastd::StorageMode; 57 use anyhow::Result; 58 use colored::Colorize; 59 use core::{marker::PhantomData, time::Duration}; 60 #[cfg(feature = "locktick")] 61 use locktick::parking_lot::{Mutex, RwLock}; 62 #[cfg(not(feature = "locktick"))] 63 use parking_lot::{Mutex, RwLock}; 64 use rand::{rngs::OsRng, CryptoRng, Rng}; 65 use std::{ 66 net::SocketAddr, 67 sync::{ 68 atomic::{AtomicU8, Ordering}, 69 Arc, 70 }, 71 }; 72 use tokio::task::JoinHandle; 73 74 /// A prover is a light node, capable of producing proofs for consensus. 75 #[derive(Clone)] 76 pub struct Prover<N: Network, C: ConsensusStorage<N>> { 77 /// The router of the node. 78 router: Router<N>, 79 /// The sync module. 80 sync: Arc<BlockSync<N>>, 81 /// The genesis block. 82 genesis: Block<N>, 83 /// The puzzle. 84 puzzle: Puzzle<N>, 85 /// The latest epoch hash. 86 latest_epoch_hash: Arc<RwLock<Option<N::BlockHash>>>, 87 /// The latest block header. 88 latest_block_header: Arc<RwLock<Option<Header<N>>>>, 89 /// The number of puzzle instances. 90 puzzle_instances: Arc<AtomicU8>, 91 /// The maximum number of puzzle instances. 92 max_puzzle_instances: u8, 93 /// The spawned handles. 94 handles: Arc<Mutex<Vec<JoinHandle<()>>>>, 95 /// Keeps track of sending pings. 96 ping: Arc<Ping<N>>, 97 /// The signal handling logic. 98 signal_handler: Arc<SignalHandler>, 99 /// PhantomData. 100 _phantom: PhantomData<C>, 101 } 102 103 impl<N: Network, C: ConsensusStorage<N>> Prover<N, C> { 104 /// Initializes a new prover node. 105 pub async fn new( 106 node_ip: SocketAddr, 107 account: Account<N>, 108 trusted_peers: &[SocketAddr], 109 genesis: Block<N>, 110 storage_mode: StorageMode, 111 trusted_peers_only: bool, 112 dev: Option<u16>, 113 signal_handler: Arc<SignalHandler>, 114 ) -> Result<Self> { 115 // Initialize the ledger service. 116 let ledger_service = Arc::new(ProverLedgerService::new()); 117 118 // Initialize the node router. 119 let router = Router::new( 120 node_ip, 121 NodeType::Prover, 122 account, 123 ledger_service.clone(), 124 trusted_peers, 125 Self::MAXIMUM_NUMBER_OF_PEERS as u16, 126 trusted_peers_only, 127 storage_mode, 128 dev.is_some(), 129 ) 130 .await?; 131 132 // Initialize the sync module. 133 let sync = BlockSync::new(ledger_service.clone()); 134 135 // Set up the ping logic. 136 let ping = Arc::new(Ping::new_nosync(router.clone())); 137 138 // Compute the maximum number of puzzle instances. 139 let max_puzzle_instances = num_cpus::get().saturating_sub(2).clamp(1, 6); 140 // Initialize the node. 141 let node = Self { 142 router, 143 sync: Arc::new(sync), 144 genesis, 145 puzzle: VM::<N, C>::new_puzzle()?, 146 latest_epoch_hash: Default::default(), 147 latest_block_header: Default::default(), 148 puzzle_instances: Default::default(), 149 max_puzzle_instances: u8::try_from(max_puzzle_instances)?, 150 handles: Default::default(), 151 ping, 152 signal_handler, 153 _phantom: Default::default(), 154 }; 155 // Initialize the routing. 156 node.initialize_routing().await; 157 // Initialize the puzzle. 158 node.initialize_puzzle().await; 159 // Initialize the notification message loop. 160 node.handles.lock().push(crate::start_notification_message_loop()); 161 162 // Return the node. 163 Ok(node) 164 } 165 166 pub fn router(&self) -> &Router<N> { 167 &self.router 168 } 169 } 170 171 #[async_trait] 172 impl<N: Network, C: ConsensusStorage<N>> NodeInterface<N> for Prover<N, C> { 173 /// Shuts down the node. 174 async fn shut_down(&self) { 175 info!("Shutting down..."); 176 177 // Shut down the puzzle. 178 debug!("Shutting down the puzzle..."); 179 180 // Abort the tasks. 181 debug!("Shutting down the prover..."); 182 self.handles.lock().iter().for_each(|handle| handle.abort()); 183 184 // Shut down the router. 185 self.router.shut_down().await; 186 187 info!("Node has shut down."); 188 } 189 } 190 191 impl<N: Network, C: ConsensusStorage<N>> Prover<N, C> { 192 /// Initialize a new instance of the puzzle. 193 async fn initialize_puzzle(&self) { 194 for _ in 0..self.max_puzzle_instances { 195 let prover = self.clone(); 196 self.handles.lock().push(tokio::spawn(async move { 197 prover.puzzle_loop().await; 198 })); 199 } 200 } 201 202 /// Executes an instance of the puzzle. 203 async fn puzzle_loop(&self) { 204 loop { 205 // If the node is not connected to any peers, then skip this iteration. 206 if self.router.number_of_connected_peers() == 0 { 207 debug!("Skipping an iteration of the puzzle (no connected peers)"); 208 tokio::time::sleep(Duration::from_secs(N::ANCHOR_TIME as u64)).await; 209 continue; 210 } 211 212 // If the number of instances of the puzzle exceeds the maximum, then skip this iteration. 213 if self.num_puzzle_instances() > self.max_puzzle_instances { 214 // Sleep for a brief period of time. 215 tokio::time::sleep(Duration::from_millis(500)).await; 216 continue; 217 } 218 219 // Read the latest epoch hash. 220 let latest_epoch_hash = *self.latest_epoch_hash.read(); 221 // Read the latest state. 222 let latest_state = self 223 .latest_block_header 224 .read() 225 .as_ref() 226 .map(|header| (header.coinbase_target(), header.proof_target())); 227 228 // If the latest epoch hash and latest state exists, then proceed to generate a solution. 229 if let (Some(epoch_hash), Some((coinbase_target, proof_target))) = (latest_epoch_hash, latest_state) { 230 // Execute the puzzle. 231 let prover = self.clone(); 232 let result = tokio::task::spawn_blocking(move || { 233 prover.puzzle_iteration(epoch_hash, coinbase_target, proof_target, &mut OsRng) 234 }) 235 .await; 236 237 // If the prover found a solution, then broadcast it. 238 if let Ok(Some((solution_target, solution))) = result { 239 info!("Found a Solution '{}' (Proof Target {solution_target})", solution.id()); 240 // Broadcast the solution. 241 self.broadcast_solution(solution); 242 } 243 } else { 244 // Otherwise, sleep for a brief period of time, to await for puzzle state. 245 tokio::time::sleep(Duration::from_secs(1)).await; 246 } 247 248 // If the Ctrl-C handler registered the signal, stop the prover. 249 if self.signal_handler.is_stopped() { 250 debug!("Shutting down the puzzle..."); 251 break; 252 } 253 } 254 } 255 256 /// Performs one iteration of the puzzle. 257 fn puzzle_iteration<R: Rng + CryptoRng>( 258 &self, 259 epoch_hash: N::BlockHash, 260 coinbase_target: u64, 261 proof_target: u64, 262 rng: &mut R, 263 ) -> Option<(u64, Solution<N>)> { 264 // Increment the puzzle instances. 265 self.increment_puzzle_instances(); 266 267 debug!( 268 "Proving 'Puzzle' for Epoch '{}' {}", 269 fmt_id(epoch_hash), 270 format!("(Coinbase Target {coinbase_target}, Proof Target {proof_target})").dimmed() 271 ); 272 273 // Compute the solution. 274 let result = 275 self.puzzle.prove(epoch_hash, self.address(), rng.r#gen(), Some(proof_target)).ok().and_then(|solution| { 276 self.puzzle.get_proof_target(&solution).ok().map(|solution_target| (solution_target, solution)) 277 }); 278 279 // Decrement the puzzle instances. 280 self.decrement_puzzle_instances(); 281 // Return the result. 282 result 283 } 284 285 /// Broadcasts the solution to the network. 286 fn broadcast_solution(&self, solution: Solution<N>) { 287 // Prepare the unconfirmed solution message. 288 let message = Message::UnconfirmedSolution(UnconfirmedSolution { 289 solution_id: solution.id(), 290 solution: Data::Object(solution), 291 }); 292 // Propagate the "UnconfirmedSolution". 293 self.propagate(message, &[]); 294 } 295 296 /// Returns the current number of puzzle instances. 297 fn num_puzzle_instances(&self) -> u8 { 298 self.puzzle_instances.load(Ordering::Relaxed) 299 } 300 301 /// Increments the number of puzzle instances. 302 fn increment_puzzle_instances(&self) { 303 self.puzzle_instances.fetch_add(1, Ordering::Relaxed); 304 #[cfg(debug_assertions)] 305 trace!("Number of Instances - {}", self.num_puzzle_instances()); 306 } 307 308 /// Decrements the number of puzzle instances. 309 fn decrement_puzzle_instances(&self) { 310 self.puzzle_instances.fetch_sub(1, Ordering::Relaxed); 311 #[cfg(debug_assertions)] 312 trace!("Number of Instances - {}", self.num_puzzle_instances()); 313 } 314 }