genesis.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 use alphavm::prelude::{block::Block, Network}; 20 use anyhow::{anyhow, bail, ensure, Result}; 21 use std::{collections::HashMap, net::SocketAddr, path::PathBuf}; 22 23 /// Configuration for genesis bootstrap process. 24 #[derive(Clone, Debug)] 25 pub struct GenesisConfig<N: Network> { 26 /// Expected genesis block hash (optional safety check). 27 /// If provided, the fetched genesis must match this hash. 28 pub expected_genesis_hash: Option<N::BlockHash>, 29 /// Require governor consensus before accepting genesis. 30 pub require_governor_consensus: bool, 31 /// Minimum governor agreement rate (default: 0.67 = 67%). 32 pub min_governor_agreement: f64, 33 /// Path to genesis cache file (default: ~/.alphaos/genesis.cache). 34 pub cache_path: Option<PathBuf>, 35 /// Re-verify cached genesis on every load (default: true). 36 pub reverify_cache: bool, 37 } 38 39 impl<N: Network> Default for GenesisConfig<N> { 40 fn default() -> Self { 41 Self { 42 expected_genesis_hash: None, 43 require_governor_consensus: true, 44 min_governor_agreement: 0.67, 45 cache_path: None, // Will use default path if None 46 reverify_cache: true, 47 } 48 } 49 } 50 51 impl<N: Network> GenesisConfig<N> { 52 /// Returns the cache file path, using default if not specified. 53 fn get_cache_path(&self) -> PathBuf { 54 self.cache_path.clone().unwrap_or_else(|| { 55 let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); 56 PathBuf::from(home).join(".alphaos").join("genesis.cache") 57 }) 58 } 59 } 60 61 /// Governor information returned from REST API. 62 #[derive(Clone, Debug)] 63 pub struct GovernorInfo { 64 pub address: String, 65 pub stake: u64, 66 } 67 68 /// Loads genesis from cache or fetches from network with BFT verification. 69 /// 70 /// This is the main entry point for genesis bootstrapping. It: 71 /// 1. Checks for cached genesis 72 /// 2. If cache exists and reverify_cache=true, re-verifies with governors 73 /// 3. If cache valid, returns cached genesis (fast path) 74 /// 4. If cache invalid/missing, fetches fresh genesis 75 /// 5. Caches the verified genesis for future use 76 /// 77 /// Returns: Verified genesis block (from cache or network) 78 pub async fn load_or_fetch_genesis<N: Network>( 79 bootstrap_peer: SocketAddr, 80 config: &GenesisConfig<N>, 81 ) -> Result<Block<N>> { 82 let cache_path = config.get_cache_path(); 83 84 // Try to load from cache 85 if let Ok(cached_genesis) = load_genesis_from_cache::<N>(&cache_path) { 86 info!("📦 Found cached genesis at {}", cache_path.display()); 87 info!(" Hash: {}", cached_genesis.hash()); 88 89 // Re-verify with governors if enabled 90 if config.reverify_cache { 91 info!("🔍 Re-verifying cached genesis with governors..."); 92 match verify_genesis_with_governors::<N>(bootstrap_peer, &cached_genesis, config).await { 93 Ok(true) => { 94 info!("✅ Cached genesis verified by governors"); 95 return Ok(cached_genesis); 96 } 97 Ok(false) => { 98 warn!("⚠️ Cached genesis rejected by governors, fetching fresh genesis"); 99 } 100 Err(e) => { 101 warn!("⚠️ Failed to verify cached genesis: {}, fetching fresh", e); 102 } 103 } 104 } else { 105 info!("⏩ Skipping cache reverification (disabled in config)"); 106 return Ok(cached_genesis); 107 } 108 } else { 109 info!("📭 No cached genesis found, fetching from network"); 110 } 111 112 // Cache invalid or missing - fetch fresh genesis 113 let genesis = bootstrap_genesis(bootstrap_peer, config).await?; 114 115 // Save to cache for next time 116 if let Err(e) = save_genesis_to_cache(&genesis, &cache_path) { 117 warn!("⚠️ Failed to cache genesis: {}", e); 118 // Non-fatal - genesis is still valid 119 } else { 120 info!("💾 Genesis cached at {}", cache_path.display()); 121 } 122 123 Ok(genesis) 124 } 125 126 /// Verifies a genesis block by checking consensus across governors. 127 /// 128 /// Returns: 129 /// - Ok(true) if ≥67% of governors confirm this genesis 130 /// - Ok(false) if <67% consensus (stale/wrong genesis) 131 /// - Err if network/query failures prevent verification 132 async fn verify_genesis_with_governors<N: Network>( 133 bootstrap_peer: SocketAddr, 134 genesis: &Block<N>, 135 config: &GenesisConfig<N>, 136 ) -> Result<bool> { 137 let genesis_hash = genesis.hash(); 138 139 // Fetch governor list 140 let governors = match fetch_governors_from_peer(bootstrap_peer).await { 141 Ok(govs) => govs, 142 Err(e) => { 143 return Err(anyhow!("Failed to fetch governors: {}", e)); 144 } 145 }; 146 147 if governors.is_empty() { 148 return Err(anyhow!("No governors found")); 149 } 150 151 // Query governors for genesis hash 152 let mut matches = 0; 153 let mut total_queries = 0; 154 155 for _governor in &governors { 156 // Query bootstrap peer (representing governors) 157 if let Ok(hash) = fetch_genesis_hash_from_peer::<N>(bootstrap_peer).await { 158 total_queries += 1; 159 if hash == genesis_hash { 160 matches += 1; 161 } 162 } 163 } 164 165 if total_queries == 0 { 166 return Err(anyhow!("Failed to query any governors")); 167 } 168 169 let consensus_rate = matches as f64 / governors.len() as f64; 170 let verified = consensus_rate >= config.min_governor_agreement; 171 172 if verified { 173 info!( 174 "✅ Genesis verified by {}/{} governors ({:.1}% consensus)", 175 matches, 176 governors.len(), 177 consensus_rate * 100.0 178 ); 179 } else { 180 info!( 181 "❌ Genesis rejected: only {}/{} governors agree ({:.1}% < {:.1}% required)", 182 matches, 183 governors.len(), 184 consensus_rate * 100.0, 185 config.min_governor_agreement * 100.0 186 ); 187 } 188 189 Ok(verified) 190 } 191 192 /// Loads genesis block from cache file. 193 fn load_genesis_from_cache<N: Network>(path: &PathBuf) -> Result<Block<N>> { 194 if !path.exists() { 195 bail!("Cache file does not exist: {}", path.display()); 196 } 197 198 Block::<N>::from_genesis_file(path) 199 } 200 201 /// Saves genesis block to cache file. 202 fn save_genesis_to_cache<N: Network>(genesis: &Block<N>, path: &PathBuf) -> Result<()> { 203 // Create parent directory if needed 204 if let Some(parent) = path.parent() { 205 std::fs::create_dir_all(parent)?; 206 } 207 208 genesis.to_genesis_file(path) 209 } 210 211 /// Fetches genesis block from bootstrap peer with multi-governor BFT verification. 212 /// 213 /// Process: 214 /// 1. Connect to bootstrap peer 215 /// 2. Fetch genesis candidate 216 /// 3. Discover governor list 217 /// 4. Query all governors for genesis hash 218 /// 5. Verify 67% BFT consensus 219 /// 6. Validate genesis structure 220 /// 221 /// Returns: Verified genesis block 222 pub async fn bootstrap_genesis<N: Network>(bootstrap_peer: SocketAddr, config: &GenesisConfig<N>) -> Result<Block<N>> { 223 info!("🔍 Bootstrapping genesis from peer: {}", bootstrap_peer); 224 225 // Step 1: Fetch genesis candidate from bootstrap peer 226 let genesis_candidate = fetch_genesis_from_peer::<N>(bootstrap_peer).await?; 227 let candidate_hash = genesis_candidate.hash(); 228 info!("📦 Fetched genesis candidate (hash: {})", candidate_hash); 229 230 // Step 2: Fetch governor list from bootstrap peer 231 let governors = fetch_governors_from_peer(bootstrap_peer).await?; 232 info!("👥 Discovered {} governors", governors.len()); 233 234 if governors.is_empty() { 235 bail!("No governors found - cannot verify genesis"); 236 } 237 238 // Step 3: Query all governors for genesis hash (BFT consensus) 239 if config.require_governor_consensus { 240 info!( 241 "🔐 Verifying genesis with {} governors (need {:.0}% consensus)...", 242 governors.len(), 243 config.min_governor_agreement * 100.0 244 ); 245 246 let mut genesis_votes: HashMap<String, usize> = HashMap::new(); 247 let mut successful_queries = 0; 248 249 for _governor in &governors { 250 // Parse governor address to socket addr 251 // For now, assume bootstrap peer represents all governors 252 // In production, each governor would have their own endpoint 253 if let Ok(hash) = fetch_genesis_hash_from_peer::<N>(bootstrap_peer).await { 254 let hash_str = format!("{}", hash); 255 *genesis_votes.entry(hash_str).or_insert(0) += 1; 256 successful_queries += 1; 257 } 258 } 259 260 if successful_queries == 0 { 261 bail!("Failed to query any governors for genesis hash"); 262 } 263 264 // Find majority hash 265 let (majority_hash, majority_count) = genesis_votes 266 .iter() 267 .max_by_key(|(_, count)| *count) 268 .ok_or_else(|| anyhow!("No genesis responses from governors"))?; 269 270 let consensus_rate = *majority_count as f64 / governors.len() as f64; 271 272 ensure!( 273 consensus_rate >= config.min_governor_agreement, 274 "Insufficient governor consensus: {:.1}% (need {:.1}%)", 275 consensus_rate * 100.0, 276 config.min_governor_agreement * 100.0 277 ); 278 279 // Verify candidate matches majority 280 let candidate_hash_str = format!("{}", candidate_hash); 281 ensure!( 282 candidate_hash_str == *majority_hash, 283 "Bootstrap peer provided non-consensus genesis (expected {}, got {})", 284 majority_hash, 285 candidate_hash_str 286 ); 287 288 info!( 289 "✅ Genesis validated by {}/{} governors ({:.1}% consensus)", 290 majority_count, 291 governors.len(), 292 consensus_rate * 100.0 293 ); 294 } 295 296 // Step 4: Optional expected hash verification 297 if let Some(expected) = config.expected_genesis_hash { 298 ensure!(candidate_hash == expected, "Genesis hash mismatch: got {}, expected {}", candidate_hash, expected); 299 info!("✅ Genesis matches expected hash"); 300 } 301 302 // Step 5: Verify genesis structure 303 ensure!( 304 genesis_candidate.height() == 0, 305 "Genesis candidate is not at height 0 (got height {})", 306 genesis_candidate.height() 307 ); 308 309 info!("🎉 Genesis bootstrap complete!"); 310 Ok(genesis_candidate) 311 } 312 313 /// Fetches genesis block from a peer via REST API. 314 async fn fetch_genesis_from_peer<N: Network>(peer: SocketAddr) -> Result<Block<N>> { 315 let network = match N::ID { 316 0 => "mainnet", 317 1 => "testnet", 318 _ => "devnet", 319 }; 320 321 let url = format!("http://{}/{}/genesis", peer, network); 322 let response = reqwest::get(&url).await.map_err(|e| anyhow!("Failed to fetch genesis from {}: {}", peer, e))?; 323 324 if !response.status().is_success() { 325 bail!("Peer {} returned error: {}", peer, response.status()); 326 } 327 328 let genesis: Block<N> = 329 response.json().await.map_err(|e| anyhow!("Failed to parse genesis from {}: {}", peer, e))?; 330 331 Ok(genesis) 332 } 333 334 /// Fetches genesis hash from a peer via REST API. 335 async fn fetch_genesis_hash_from_peer<N: Network>(peer: SocketAddr) -> Result<N::BlockHash> { 336 let network = match N::ID { 337 0 => "mainnet", 338 1 => "testnet", 339 _ => "devnet", 340 }; 341 342 let url = format!("http://{}/{}/genesis/hash", peer, network); 343 let response = 344 reqwest::get(&url).await.map_err(|e| anyhow!("Failed to fetch genesis hash from {}: {}", peer, e))?; 345 346 if !response.status().is_success() { 347 bail!("Peer {} returned error: {}", peer, response.status()); 348 } 349 350 let hash: N::BlockHash = 351 response.json().await.map_err(|e| anyhow!("Failed to parse genesis hash from {}: {}", peer, e))?; 352 353 Ok(hash) 354 } 355 356 /// Fetches governor list from a peer via REST API. 357 async fn fetch_governors_from_peer(peer: SocketAddr) -> Result<Vec<GovernorInfo>> { 358 let network = match peer.port() { 359 3030 => "testnet", // Heuristic based on port 360 _ => "testnet", 361 }; 362 363 let url = format!("http://{}/{}/governors", peer, network); 364 let response = reqwest::get(&url).await.map_err(|e| anyhow!("Failed to fetch governors from {}: {}", peer, e))?; 365 366 if !response.status().is_success() { 367 bail!("Peer {} returned error: {}", peer, response.status()); 368 } 369 370 let data: serde_json::Value = 371 response.json().await.map_err(|e| anyhow!("Failed to parse governors from {}: {}", peer, e))?; 372 373 let governors_array = data["governors"].as_array().ok_or_else(|| anyhow!("Invalid governors response format"))?; 374 375 let mut governors = Vec::new(); 376 for gov in governors_array { 377 let address = gov["address"].as_str().ok_or_else(|| anyhow!("Missing governor address"))?.to_string(); 378 let stake = gov["stake"].as_u64().ok_or_else(|| anyhow!("Missing governor stake"))?; 379 380 governors.push(GovernorInfo { address, stake }); 381 } 382 383 Ok(governors) 384 } 385 386 #[cfg(test)] 387 mod tests { 388 use super::*; 389 390 #[test] 391 fn test_genesis_config_defaults() { 392 use alphavm::prelude::MainnetV0; 393 let config = GenesisConfig::<MainnetV0>::default(); 394 assert!(config.require_governor_consensus); 395 assert_eq!(config.min_governor_agreement, 0.67); 396 assert!(config.expected_genesis_hash.is_none()); 397 assert!(config.reverify_cache); 398 assert!(config.cache_path.is_none()); 399 } 400 401 #[test] 402 fn test_cache_path_default() { 403 use alphavm::prelude::MainnetV0; 404 let config = GenesisConfig::<MainnetV0>::default(); 405 let path = config.get_cache_path(); 406 assert!(path.to_string_lossy().contains(".alphaos")); 407 assert!(path.to_string_lossy().contains("genesis.cache")); 408 } 409 410 #[test] 411 fn test_cache_path_custom() { 412 use alphavm::prelude::MainnetV0; 413 let custom_path = PathBuf::from("/tmp/custom-genesis.cache"); 414 let config = GenesisConfig::<MainnetV0> { cache_path: Some(custom_path.clone()), ..Default::default() }; 415 assert_eq!(config.get_cache_path(), custom_path); 416 } 417 }