Skip to main content

hanzo_mining/
lib.rs

1use std::sync::Arc;
2use tokio::sync::RwLock;
3use serde::{Deserialize, Serialize};
4
5pub mod bridge;
6pub mod consensus;
7pub mod evm;
8pub mod ledger;
9pub mod wallet;
10
11pub use bridge::*;
12pub use consensus::*;
13pub use evm::*;
14pub use ledger::*;
15pub use wallet::*;
16
17/// AI Mining configuration for Hanzo Node
18/// Enables mining AI coins by offering GPU/CPU compute to the network
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct MiningConfig {
21    // Network selection
22    pub network: NetworkType,
23    pub enabled: bool,
24    pub auto_start: bool,
25
26    // Compute offering
27    pub offer_gpu: bool,
28    pub offer_cpu: bool,
29    pub max_gpu_usage: f32,  // 0.0 to 1.0
30    pub max_cpu_usage: f32,  // 0.0 to 1.0
31    pub reserved_ram_gb: f32, // RAM to keep free
32
33    // Mining wallet
34    pub wallet_address: String,
35    pub wallet_private_key: Option<String>, // Encrypted
36
37    // Earnings & rewards
38    pub payout_threshold: f64,
39    pub auto_withdraw: bool,
40    pub withdrawal_address: Option<String>,
41
42    // Performance settings
43    pub benchmark_on_start: bool,
44    pub adaptive_performance: bool,
45    pub min_job_reward: f64, // Minimum AI coin reward to accept job
46
47    // Connection settings
48    pub rpc_endpoints: Vec<String>,
49    pub p2p_port: u16,
50    pub api_port: u16,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
54pub enum NetworkType {
55    HanzoMainnet,     // hanzo.network - Main AI network
56    HanzoTestnet,     // hanzo-test.network - Test network
57    ZooMainnet,       // zoo.network - Zoo mainnet with AI protocol
58    ZooTestnet,       // zoo-test.network - Zoo testnet
59    Custom(String),   // Custom network endpoint
60}
61
62impl NetworkType {
63    pub fn rpc_endpoint(&self) -> String {
64        match self {
65            Self::HanzoMainnet => "https://rpc.hanzo.network".to_string(),
66            Self::HanzoTestnet => "https://rpc.hanzo-test.network".to_string(),
67            Self::ZooMainnet => "https://rpc.zoo.network".to_string(),
68            Self::ZooTestnet => "https://rpc.zoo-test.network".to_string(),
69            Self::Custom(endpoint) => endpoint.clone(),
70        }
71    }
72
73    pub fn chain_id(&self) -> u64 {
74        match self {
75            Self::HanzoMainnet => 36963,  // Hanzo mainnet chain ID
76            Self::HanzoTestnet => 36964,  // Hanzo testnet chain ID
77            Self::ZooMainnet => 200200,   // Zoo mainnet chain ID
78            Self::ZooTestnet => 200201,   // Zoo testnet chain ID
79            Self::Custom(_) => 0,
80        }
81    }
82
83    pub fn native_token(&self) -> &str {
84        match self {
85            Self::HanzoMainnet | Self::HanzoTestnet => "HAI",  // Hanzo AI coin
86            Self::ZooMainnet | Self::ZooTestnet => "ZOO",      // Zoo coin
87            Self::Custom(_) => "TOKEN",
88        }
89    }
90}
91
92impl Default for MiningConfig {
93    fn default() -> Self {
94        Self {
95            network: NetworkType::HanzoMainnet,
96            enabled: true,
97            auto_start: true,
98
99            offer_gpu: true,
100            offer_cpu: true,
101            max_gpu_usage: 0.8,  // Use up to 80% GPU
102            max_cpu_usage: 0.6,  // Use up to 60% CPU
103            reserved_ram_gb: 4.0, // Keep 4GB RAM free
104
105            wallet_address: String::new(),
106            wallet_private_key: None,
107
108            payout_threshold: 10.0,  // Withdraw after 10 AI coins
109            auto_withdraw: true,
110            withdrawal_address: None,
111
112            benchmark_on_start: true,
113            adaptive_performance: true,
114            min_job_reward: 0.001,  // Accept jobs paying at least 0.001 AI coins
115
116            rpc_endpoints: vec![
117                "https://rpc.hanzo.network".to_string(),
118                "https://rpc2.hanzo.network".to_string(),
119            ],
120            p2p_port: 3691,  // P2P consensus port (3690 + 1)
121            api_port: 3690,   // Main hanzod API port
122        }
123    }
124}
125
126/// AI Mining Manager - handles compute job execution and rewards
127pub struct MiningManager {
128    config: Arc<RwLock<MiningConfig>>,
129    is_mining: Arc<RwLock<bool>>,
130    current_jobs: Arc<RwLock<Vec<ComputeJob>>>,
131    total_earned: Arc<RwLock<f64>>,
132    performance_stats: Arc<RwLock<PerformanceStats>>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ComputeJob {
137    pub job_id: String,
138    pub job_type: JobType,
139    pub requester: String,
140    pub reward: f64,
141    pub deadline: u64,
142    pub input_data: Vec<u8>,
143    pub status: JobStatus,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub enum JobType {
148    Embedding(EmbeddingJob),
149    Reranking(RerankingJob),
150    Inference(InferenceJob),
151    Training(TrainingJob),
152    Custom(Vec<u8>),
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct EmbeddingJob {
157    pub model: String,
158    pub texts: Vec<String>,
159    pub batch_size: usize,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct RerankingJob {
164    pub model: String,
165    pub query: String,
166    pub documents: Vec<String>,
167    pub top_k: usize,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct InferenceJob {
172    pub model: String,
173    pub prompt: String,
174    pub max_tokens: usize,
175    pub temperature: f32,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct TrainingJob {
180    pub model: String,
181    pub dataset_url: String,
182    pub epochs: usize,
183    pub checkpoint_interval: usize,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
187pub enum JobStatus {
188    Pending,
189    Running,
190    Completed,
191    Failed,
192    Cancelled,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct PerformanceStats {
197    pub gpu_tflops: f32,
198    pub cpu_gflops: f32,
199    pub ram_gb: f32,
200    pub vram_gb: f32,
201    pub network_mbps: f32,
202    pub jobs_completed: u64,
203    pub jobs_failed: u64,
204    pub uptime_hours: f64,
205    pub reputation_score: f64,
206}
207
208impl MiningManager {
209    pub fn new(config: MiningConfig) -> Self {
210        Self {
211            config: Arc::new(RwLock::new(config)),
212            is_mining: Arc::new(RwLock::new(false)),
213            current_jobs: Arc::new(RwLock::new(Vec::new())),
214            total_earned: Arc::new(RwLock::new(0.0)),
215            performance_stats: Arc::new(RwLock::new(PerformanceStats::default())),
216        }
217    }
218
219    /// Start mining on the configured network
220    pub async fn start_mining(&self) -> Result<(), Box<dyn std::error::Error>> {
221        let config = self.config.read().await;
222
223        if !config.enabled {
224            return Err("Mining is disabled in config".into());
225        }
226
227        println!("🚀 Starting AI mining on {}", config.network.rpc_endpoint());
228        println!("⛏️  Offering: GPU={}, CPU={}", config.offer_gpu, config.offer_cpu);
229        println!("💰 Wallet: {}", config.wallet_address);
230
231        *self.is_mining.write().await = true;
232
233        // Connect to network
234        self.connect_to_network(&config).await?;
235
236        // Benchmark if needed
237        if config.benchmark_on_start {
238            self.run_benchmark().await?;
239        }
240
241        // Start job listener
242        self.start_job_listener().await;
243
244        Ok(())
245    }
246
247    /// Stop mining
248    pub async fn stop_mining(&self) -> Result<(), Box<dyn std::error::Error>> {
249        println!("⛔ Stopping AI mining...");
250        *self.is_mining.write().await = false;
251
252        // Cancel current jobs
253        let mut jobs = self.current_jobs.write().await;
254        for job in jobs.iter_mut() {
255            job.status = JobStatus::Cancelled;
256        }
257
258        Ok(())
259    }
260
261    /// Connect to the blockchain network
262    async fn connect_to_network(&self, config: &MiningConfig) -> Result<(), Box<dyn std::error::Error>> {
263        println!("🔗 Connecting to {} network...", match &config.network {
264            NetworkType::HanzoMainnet => "Hanzo Mainnet",
265            NetworkType::HanzoTestnet => "Hanzo Testnet",
266            NetworkType::ZooMainnet => "Zoo Mainnet",
267            NetworkType::ZooTestnet => "Zoo Testnet",
268            NetworkType::Custom(url) => url,
269        });
270
271        // TODO: Implement actual blockchain connection
272        // This would connect to the RPC endpoint and register as a compute provider
273
274        Ok(())
275    }
276
277    /// Run performance benchmark
278    async fn run_benchmark(&self) -> Result<(), Box<dyn std::error::Error>> {
279        println!("📊 Running performance benchmark...");
280
281        let mut stats = self.performance_stats.write().await;
282
283        // Detect GPU
284        if self.config.read().await.offer_gpu {
285            stats.gpu_tflops = self.benchmark_gpu().await?;
286            println!("  GPU: {:.2} TFLOPS", stats.gpu_tflops);
287        }
288
289        // Benchmark CPU
290        if self.config.read().await.offer_cpu {
291            stats.cpu_gflops = self.benchmark_cpu().await?;
292            println!("  CPU: {:.2} GFLOPS", stats.cpu_gflops);
293        }
294
295        // Check RAM
296        stats.ram_gb = self.get_available_ram();
297        println!("  RAM: {:.2} GB available", stats.ram_gb);
298
299        // Check VRAM
300        if self.config.read().await.offer_gpu {
301            stats.vram_gb = self.get_available_vram();
302            println!("  VRAM: {:.2} GB available", stats.vram_gb);
303        }
304
305        // Test network speed
306        stats.network_mbps = self.benchmark_network().await?;
307        println!("  Network: {:.2} Mbps", stats.network_mbps);
308
309        Ok(())
310    }
311
312    /// Start listening for compute jobs
313    async fn start_job_listener(&self) {
314        let is_mining = self.is_mining.clone();
315        let _current_jobs = self.current_jobs.clone();
316        let config = self.config.clone();
317
318        tokio::spawn(async move {
319            while *is_mining.read().await {
320                // Poll for new jobs from the network
321                // This would connect to the P2P network or smart contract
322
323                // Simulate receiving a job
324                tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
325
326                // Check if we should accept jobs
327                let cfg = config.read().await;
328                if !cfg.enabled {
329                    continue;
330                }
331
332                // TODO: Implement actual job fetching from blockchain
333                // For now, this is a placeholder
334
335                println!("👀 Checking for new compute jobs...");
336            }
337        });
338    }
339
340    /// Execute a compute job
341    pub async fn execute_job(&self, job: ComputeJob) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
342        println!("🔧 Executing job: {}", job.job_id);
343
344        let result = match job.job_type {
345            JobType::Embedding(ref params) => {
346                self.execute_embedding_job(params).await?
347            }
348            JobType::Reranking(ref params) => {
349                self.execute_reranking_job(params).await?
350            }
351            JobType::Inference(ref params) => {
352                self.execute_inference_job(params).await?
353            }
354            JobType::Training(ref params) => {
355                self.execute_training_job(params).await?
356            }
357            JobType::Custom(ref data) => {
358                self.execute_custom_job(data).await?
359            }
360        };
361
362        // Update stats
363        let mut stats = self.performance_stats.write().await;
364        stats.jobs_completed += 1;
365
366        // Add to earnings
367        *self.total_earned.write().await += job.reward;
368        println!("💰 Earned {} {} for job {}", job.reward,
369            self.config.read().await.network.native_token(), job.job_id);
370
371        Ok(result)
372    }
373
374    async fn execute_embedding_job(&self, params: &EmbeddingJob) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
375        // TODO: Use hanzo-engine to generate embeddings
376        println!("  Generating embeddings with model: {}", params.model);
377        Ok(vec![])
378    }
379
380    async fn execute_reranking_job(&self, params: &RerankingJob) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
381        // TODO: Use hanzo-engine for reranking
382        println!("  Reranking {} documents", params.documents.len());
383        Ok(vec![])
384    }
385
386    async fn execute_inference_job(&self, params: &InferenceJob) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
387        // TODO: Use hanzo-engine for inference
388        println!("  Running inference with model: {}", params.model);
389        Ok(vec![])
390    }
391
392    async fn execute_training_job(&self, params: &TrainingJob) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
393        // TODO: Use hanzo-engine for training
394        println!("  Training model: {} for {} epochs", params.model, params.epochs);
395        Ok(vec![])
396    }
397
398    async fn execute_custom_job(&self, data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
399        println!("  Executing custom job ({} bytes)", data.len());
400        Ok(vec![])
401    }
402
403    /// Get mining statistics
404    pub async fn get_stats(&self) -> MiningStats {
405        MiningStats {
406            is_mining: *self.is_mining.read().await,
407            network: self.config.read().await.network.clone(),
408            total_earned: *self.total_earned.read().await,
409            current_jobs: self.current_jobs.read().await.len(),
410            performance: self.performance_stats.read().await.clone(),
411        }
412    }
413
414    // Benchmark helpers
415    async fn benchmark_gpu(&self) -> Result<f32, Box<dyn std::error::Error>> {
416        // TODO: Actual GPU benchmark using hanzo-engine
417        Ok(15.7) // Example: 15.7 TFLOPS for RTX 3080
418    }
419
420    async fn benchmark_cpu(&self) -> Result<f32, Box<dyn std::error::Error>> {
421        // TODO: Actual CPU benchmark
422        Ok(450.0) // Example: 450 GFLOPS
423    }
424
425    fn get_available_ram(&self) -> f32 {
426        // TODO: Get actual available RAM
427        16.0 // Example: 16 GB
428    }
429
430    fn get_available_vram(&self) -> f32 {
431        // TODO: Get actual VRAM from GPU
432        10.0 // Example: 10 GB for RTX 3080
433    }
434
435    async fn benchmark_network(&self) -> Result<f32, Box<dyn std::error::Error>> {
436        // TODO: Actual network speed test
437        Ok(100.0) // Example: 100 Mbps
438    }
439}
440
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct MiningStats {
443    pub is_mining: bool,
444    pub network: NetworkType,
445    pub total_earned: f64,
446    pub current_jobs: usize,
447    pub performance: PerformanceStats,
448}
449
450impl Default for PerformanceStats {
451    fn default() -> Self {
452        Self {
453            gpu_tflops: 0.0,
454            cpu_gflops: 0.0,
455            ram_gb: 0.0,
456            vram_gb: 0.0,
457            network_mbps: 0.0,
458            jobs_completed: 0,
459            jobs_failed: 0,
460            uptime_hours: 0.0,
461            reputation_score: 100.0,
462        }
463    }
464}
465
466/// Initialize mining with default settings
467pub async fn init_mining() -> Result<MiningManager, Box<dyn std::error::Error>> {
468    let config_path = dirs::home_dir()
469        .expect("Could not find home directory")
470        .join(".hanzo/config/mining.toml");
471
472    let config = if config_path.exists() {
473        let contents = std::fs::read_to_string(&config_path)?;
474        toml::from_str(&contents)?
475    } else {
476        let config = MiningConfig::default();
477        std::fs::create_dir_all(config_path.parent().unwrap())?;
478        std::fs::write(&config_path, toml::to_string_pretty(&config)?)?;
479        config
480    };
481
482    let manager = MiningManager::new(config);
483
484    if manager.config.read().await.auto_start {
485        let _ = manager.start_mining().await;
486    }
487
488    Ok(manager)
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494
495    #[test]
496    fn test_network_endpoints() {
497        assert_eq!(NetworkType::HanzoMainnet.rpc_endpoint(), "https://rpc.hanzo.network");
498        assert_eq!(NetworkType::ZooMainnet.rpc_endpoint(), "https://rpc.zoo.network");
499        assert_eq!(NetworkType::HanzoMainnet.native_token(), "HAI");
500        assert_eq!(NetworkType::ZooMainnet.native_token(), "ZOO");
501        assert_eq!(NetworkType::HanzoMainnet.chain_id(), 36963);
502        assert_eq!(NetworkType::ZooMainnet.chain_id(), 200200);
503    }
504
505    #[tokio::test]
506    async fn test_mining_manager() {
507        let config = MiningConfig {
508            enabled: false, // Don't actually start mining in tests
509            ..Default::default()
510        };
511
512        let manager = MiningManager::new(config);
513        let stats = manager.get_stats().await;
514        assert!(!stats.is_mining);
515        assert_eq!(stats.total_earned, 0.0);
516    }
517}