chaincraft_rust/crypto/
pow.rs1use crate::crypto::KeylessCryptoPrimitive;
4use crate::error::{ChaincraftError, CryptoError, Result};
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
9use std::sync::Arc;
10use tokio::task;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ProofOfWorkConfig {
15 pub difficulty: u32,
17 pub max_nonce: u64,
19 pub threads: usize,
21}
22
23impl Default for ProofOfWorkConfig {
24 fn default() -> Self {
25 Self {
26 difficulty: 4,
27 max_nonce: u64::MAX,
28 threads: num_cpus::get(),
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct PoWChallenge {
36 pub data: String,
37}
38
39impl PoWChallenge {
40 pub fn new(data: impl Into<String>) -> Self {
41 Self { data: data.into() }
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct PoWProof {
48 pub nonce: u64,
49 pub hash: String,
50}
51
52impl PoWProof {
53 pub fn new(nonce: u64, hash: String) -> Self {
54 Self { nonce, hash }
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct ProofOfWork {
61 config: ProofOfWorkConfig,
62}
63
64impl ProofOfWork {
65 pub fn new() -> Self {
67 Self {
68 config: ProofOfWorkConfig::default(),
69 }
70 }
71
72 pub fn with_config(config: ProofOfWorkConfig) -> Self {
74 Self { config }
75 }
76
77 pub fn with_difficulty(difficulty: u32) -> Self {
79 Self {
80 config: ProofOfWorkConfig {
81 difficulty,
82 ..ProofOfWorkConfig::default()
83 },
84 }
85 }
86
87 fn calculate_hash(data: &str, nonce: u64) -> String {
89 let mut hasher = Sha256::new();
90 hasher.update(data.as_bytes());
91 hasher.update(nonce.to_le_bytes());
92 hex::encode(hasher.finalize())
93 }
94
95 fn meets_difficulty(hash: &str, difficulty: u32) -> bool {
97 hash.starts_with(&"0".repeat(difficulty as usize))
98 }
99
100 async fn mine_worker(
102 data: String,
103 difficulty: u32,
104 start_nonce: u64,
105 nonce_step: u64,
106 max_nonce: u64,
107 should_stop: Arc<AtomicBool>,
108 best_nonce: Arc<AtomicU64>,
109 ) -> Option<PoWProof> {
110 task::spawn_blocking(move || {
111 let mut nonce = start_nonce;
112
113 while nonce < max_nonce && !should_stop.load(Ordering::Relaxed) {
114 let hash = Self::calculate_hash(&data, nonce);
115
116 if Self::meets_difficulty(&hash, difficulty) {
117 best_nonce.store(nonce, Ordering::Relaxed);
119 should_stop.store(true, Ordering::Relaxed);
120 return Some(PoWProof::new(nonce, hash));
121 }
122
123 nonce = nonce.saturating_add(nonce_step);
124
125 if nonce % 10000 == 0 && should_stop.load(Ordering::Relaxed) {
127 break;
128 }
129 }
130
131 None
132 })
133 .await
134 .unwrap_or(None)
135 }
136
137 pub fn verify_sync(&self, challenge: &PoWChallenge, proof: &PoWProof) -> Result<bool> {
139 let calculated_hash = Self::calculate_hash(&challenge.data, proof.nonce);
141 if calculated_hash != proof.hash {
142 return Ok(false);
143 }
144
145 Ok(Self::meets_difficulty(&proof.hash, self.config.difficulty))
147 }
148
149 pub fn difficulty(&self) -> u32 {
151 self.config.difficulty
152 }
153
154 pub fn set_difficulty(&mut self, difficulty: u32) {
156 self.config.difficulty = difficulty;
157 }
158
159 pub fn estimate_time(&self, hash_rate: f64) -> std::time::Duration {
161 let target = 2_u64.pow(self.config.difficulty);
162 let expected_hashes = target as f64;
163 let seconds = expected_hashes / hash_rate;
164 std::time::Duration::from_secs_f64(seconds)
165 }
166}
167
168impl Default for ProofOfWork {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174#[async_trait]
175impl KeylessCryptoPrimitive for ProofOfWork {
176 type Input = String;
177 type Output = String;
178 type Challenge = PoWChallenge;
179 type Proof = PoWProof;
180
181 async fn compute(&self, input: Self::Input) -> Result<Self::Output> {
182 let mut hasher = Sha256::new();
184 hasher.update(input.as_bytes());
185 Ok(hex::encode(hasher.finalize()))
186 }
187
188 async fn create_proof(&self, challenge: Self::Challenge) -> Result<Self::Proof> {
189 let should_stop = Arc::new(AtomicBool::new(false));
190 let best_nonce = Arc::new(AtomicU64::new(0));
191 let mut handles = Vec::new();
192
193 let threads = self.config.threads;
194 let nonce_step = threads as u64;
195
196 for i in 0..threads {
198 let data = challenge.data.clone();
199 let difficulty = self.config.difficulty;
200 let start_nonce = i as u64;
201 let max_nonce = self.config.max_nonce;
202 let should_stop_clone = should_stop.clone();
203 let best_nonce_clone = best_nonce.clone();
204
205 let handle = task::spawn(Self::mine_worker(
206 data,
207 difficulty,
208 start_nonce,
209 nonce_step,
210 max_nonce,
211 should_stop_clone,
212 best_nonce_clone,
213 ));
214
215 handles.push(handle);
216 }
217
218 for handle in handles {
220 if let Ok(Some(proof)) = handle.await {
221 should_stop.store(true, Ordering::Relaxed);
222 return Ok(proof);
223 }
224 }
225
226 Err(ChaincraftError::Crypto(CryptoError::ProofOfWorkFailed))
227 }
228
229 async fn verify_proof(&self, challenge: Self::Challenge, proof: Self::Proof) -> Result<bool> {
230 let difficulty = self.config.difficulty;
232 task::spawn_blocking(move || {
233 let calculated_hash = Self::calculate_hash(&challenge.data, proof.nonce);
234 if calculated_hash != proof.hash {
235 return false;
236 }
237 Self::meets_difficulty(&proof.hash, difficulty)
238 })
239 .await
240 .map_err(|_| ChaincraftError::Generic("Task join error".to_string()))
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[tokio::test]
249 async fn test_pow_creation_and_verification() {
250 let pow = ProofOfWork::with_difficulty(1); let challenge = PoWChallenge::new("test data");
252
253 let proof = pow.create_proof(challenge.clone()).await.unwrap();
254 let is_valid = pow.verify_proof(challenge, proof).await.unwrap();
255
256 assert!(is_valid);
257 }
258
259 #[test]
260 fn test_sync_verification() {
261 let pow = ProofOfWork::with_difficulty(2);
262 let challenge = PoWChallenge::new("test");
263
264 let proof = PoWProof::new(0, ProofOfWork::calculate_hash("test", 0));
266
267 for nonce in 0..10000 {
269 let hash = ProofOfWork::calculate_hash("test", nonce);
270 if ProofOfWork::meets_difficulty(&hash, 2) {
271 let proof = PoWProof::new(nonce, hash);
272 assert!(pow.verify_sync(&challenge, &proof).unwrap());
273 break;
274 }
275 }
276 }
277
278 #[test]
279 fn test_difficulty_check() {
280 assert!(ProofOfWork::meets_difficulty("00abc", 2));
281 assert!(!ProofOfWork::meets_difficulty("0abc", 2));
282 assert!(ProofOfWork::meets_difficulty("000abc", 3));
283 }
284}