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#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct MiningConfig {
21 pub network: NetworkType,
23 pub enabled: bool,
24 pub auto_start: bool,
25
26 pub offer_gpu: bool,
28 pub offer_cpu: bool,
29 pub max_gpu_usage: f32, pub max_cpu_usage: f32, pub reserved_ram_gb: f32, pub wallet_address: String,
35 pub wallet_private_key: Option<String>, pub payout_threshold: f64,
39 pub auto_withdraw: bool,
40 pub withdrawal_address: Option<String>,
41
42 pub benchmark_on_start: bool,
44 pub adaptive_performance: bool,
45 pub min_job_reward: f64, 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, HanzoTestnet, ZooMainnet, ZooTestnet, Custom(String), }
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, Self::HanzoTestnet => 36964, Self::ZooMainnet => 200200, Self::ZooTestnet => 200201, Self::Custom(_) => 0,
80 }
81 }
82
83 pub fn native_token(&self) -> &str {
84 match self {
85 Self::HanzoMainnet | Self::HanzoTestnet => "HAI", Self::ZooMainnet | Self::ZooTestnet => "ZOO", 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, max_cpu_usage: 0.6, reserved_ram_gb: 4.0, wallet_address: String::new(),
106 wallet_private_key: None,
107
108 payout_threshold: 10.0, auto_withdraw: true,
110 withdrawal_address: None,
111
112 benchmark_on_start: true,
113 adaptive_performance: true,
114 min_job_reward: 0.001, rpc_endpoints: vec![
117 "https://rpc.hanzo.network".to_string(),
118 "https://rpc2.hanzo.network".to_string(),
119 ],
120 p2p_port: 3691, api_port: 3690, }
123 }
124}
125
126pub 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 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 self.connect_to_network(&config).await?;
235
236 if config.benchmark_on_start {
238 self.run_benchmark().await?;
239 }
240
241 self.start_job_listener().await;
243
244 Ok(())
245 }
246
247 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 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 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 Ok(())
275 }
276
277 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 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 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 stats.ram_gb = self.get_available_ram();
297 println!(" RAM: {:.2} GB available", stats.ram_gb);
298
299 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 stats.network_mbps = self.benchmark_network().await?;
307 println!(" Network: {:.2} Mbps", stats.network_mbps);
308
309 Ok(())
310 }
311
312 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 tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
325
326 let cfg = config.read().await;
328 if !cfg.enabled {
329 continue;
330 }
331
332 println!("👀 Checking for new compute jobs...");
336 }
337 });
338 }
339
340 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 let mut stats = self.performance_stats.write().await;
364 stats.jobs_completed += 1;
365
366 *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 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 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 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 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 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 async fn benchmark_gpu(&self) -> Result<f32, Box<dyn std::error::Error>> {
416 Ok(15.7) }
419
420 async fn benchmark_cpu(&self) -> Result<f32, Box<dyn std::error::Error>> {
421 Ok(450.0) }
424
425 fn get_available_ram(&self) -> f32 {
426 16.0 }
429
430 fn get_available_vram(&self) -> f32 {
431 10.0 }
434
435 async fn benchmark_network(&self) -> Result<f32, Box<dyn std::error::Error>> {
436 Ok(100.0) }
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
466pub 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, ..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}