/ node / sync / src / genesis.rs
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  }