Skip to main content

ai3_lib/
mining.rs

1//! Mining task types, results, miner capabilities, and task distribution.
2
3use crate::tensor::Tensor;
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// A single mining task (operation, input tensors, difficulty, expiry).
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct MiningTask {
11    /// Unique task id.
12    pub id: String,
13    /// Operation type (e.g. matrix_multiply, relu).
14    pub operation_type: String,
15    /// Input tensors for the operation.
16    pub input_tensors: Vec<Tensor>,
17    /// Difficulty (leading zeros required in hash).
18    pub difficulty: u64,
19    /// Reward amount.
20    pub reward: u64,
21    /// Max computation time in seconds.
22    pub max_computation_time: u64,
23    /// Requester/miner id.
24    pub requester: String,
25    /// Creation timestamp.
26    pub created_at: DateTime<Utc>,
27    /// Expiry timestamp.
28    pub expires_at: DateTime<Utc>,
29}
30
31impl MiningTask {
32    /// Creates a new mining task with generated id and expiry.
33    pub fn new(
34        operation_type: String,
35        input_tensors: Vec<Tensor>,
36        difficulty: u64,
37        reward: u64,
38        max_computation_time: u64,
39        requester: String,
40    ) -> Self {
41        let now = Utc::now();
42        Self {
43            id: uuid::Uuid::new_v4().to_string(),
44            operation_type,
45            input_tensors,
46            difficulty,
47            reward,
48            max_computation_time,
49            requester,
50            created_at: now,
51            expires_at: now + chrono::Duration::seconds(max_computation_time as i64),
52        }
53    }
54
55    /// Returns true if the task has passed its expiry time.
56    pub fn is_expired(&self) -> bool {
57        Utc::now() > self.expires_at
58    }
59
60    /// Returns true if the given hash has at least `difficulty` leading zeros.
61    pub fn meets_difficulty(&self, hash: &str) -> bool {
62        let leading_zeros = self.difficulty as usize;
63        hash.chars().take(leading_zeros).all(|c| c == '0')
64    }
65}
66
67/// Result of a completed mining task (output tensor, nonce, validity).
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct MiningResult {
70    pub task_id: String,
71    pub miner_id: String,
72    pub nonce: u64,
73    pub hash: String,
74    pub output_tensor: Tensor,
75    pub computation_time: u64,
76    pub timestamp: DateTime<Utc>,
77    pub is_valid: bool,
78}
79
80/// Capabilities advertised by a miner (operations, tensor size, ESP flag).
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct MinerCapabilities {
83    pub supported_operations: Vec<String>,
84    pub max_tensor_size: usize,
85    pub is_esp_device: bool,
86    pub max_computation_time: u64,
87}
88
89impl Default for MinerCapabilities {
90    fn default() -> Self {
91        Self {
92            supported_operations: vec![
93                "matrix_multiply".into(),
94                "convolution".into(),
95                "relu".into(),
96                "sigmoid".into(),
97                "tanh".into(),
98                "dot_product".into(),
99                "normalize".into(),
100            ],
101            max_tensor_size: 64 * 64 * 4, // 64x64 f32
102            is_esp_device: false,
103            max_computation_time: 300,
104        }
105    }
106}
107
108/// Aggregate stats for a miner (completed/failed tasks, compute time).
109#[derive(Debug, Clone, Default, Serialize, Deserialize)]
110pub struct MinerStats {
111    /// Number of tasks completed successfully.
112    pub tasks_completed: u64,
113    /// Number of tasks that failed.
114    pub tasks_failed: u64,
115    /// Total compute time in ms.
116    pub total_compute_time: u64,
117    /// Average compute time per task.
118    pub average_compute_time: f64,
119}
120
121/// Distributes mining tasks to available miners (from .AI3).
122#[derive(Debug, Default)]
123pub struct TaskDistributor {
124    /// Task id -> task.
125    pub pending_tasks: HashMap<String, MiningTask>,
126}
127
128impl TaskDistributor {
129    /// Creates an empty distributor.
130    pub fn new() -> Self {
131        Self::default()
132    }
133
134    /// Adds a task to the pending set.
135    pub fn add_task(&mut self, task: MiningTask) {
136        self.pending_tasks.insert(task.id.clone(), task);
137    }
138
139    /// Returns all pending tasks.
140    pub fn get_pending_tasks(&self) -> Vec<&MiningTask> {
141        self.pending_tasks.values().collect()
142    }
143
144    /// Removes and returns a task by id.
145    pub fn remove_task(&mut self, task_id: &str) -> Option<MiningTask> {
146        self.pending_tasks.remove(task_id)
147    }
148
149    /// Removes expired tasks from the pending set.
150    pub fn cleanup_expired_tasks(&mut self) {
151        self.pending_tasks.retain(|_, task| !task.is_expired());
152    }
153}