1use crate::ledger::{derive_address_from_pubkey, MiningLedger};
33use crate::evm::{TeleportDestination, TeleportTransfer, ChainConfig, EvmClient};
34use crate::{MiningRewardType, PerformanceStats};
35use serde::{Deserialize, Serialize};
36use std::path::Path;
37use zeroize::{Zeroize, ZeroizeOnDrop};
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41pub enum SecurityLevel {
42 Level2,
44 Level3,
46 Level5,
48}
49
50impl SecurityLevel {
51 pub fn public_key_size(&self) -> usize {
53 match self {
54 Self::Level2 => 1312, Self::Level3 => 1952, Self::Level5 => 2592, }
58 }
59
60 pub fn secret_key_size(&self) -> usize {
62 match self {
63 Self::Level2 => 2560, Self::Level3 => 4032, Self::Level5 => 4896, }
67 }
68
69 pub fn signature_size(&self) -> usize {
71 match self {
72 Self::Level2 => 2420, Self::Level3 => 3309, Self::Level5 => 4627, }
76 }
77
78 pub fn algorithm_name(&self) -> &'static str {
80 match self {
81 Self::Level2 => "ML-DSA-44",
82 Self::Level3 => "ML-DSA-65",
83 Self::Level5 => "ML-DSA-87",
84 }
85 }
86}
87
88impl Default for SecurityLevel {
89 fn default() -> Self {
90 Self::Level3 }
92}
93
94#[derive(ZeroizeOnDrop)]
96pub struct MiningWallet {
97 #[zeroize(skip)]
99 security_level: SecurityLevel,
100 #[zeroize(skip)]
102 public_key: Vec<u8>,
103 secret_key: Vec<u8>,
105 #[zeroize(skip)]
107 address: String,
108 #[zeroize(skip)]
110 label: Option<String>,
111}
112
113impl MiningWallet {
114 pub async fn generate(level: SecurityLevel) -> Result<Self, WalletError> {
116 let (public_key, secret_key) = generate_ml_dsa_keypair(level)?;
119 let address = derive_address_from_pubkey(&public_key);
120
121 Ok(Self {
122 security_level: level,
123 public_key,
124 secret_key,
125 address,
126 label: None,
127 })
128 }
129
130 pub async fn generate_default() -> Result<Self, WalletError> {
132 Self::generate(SecurityLevel::default()).await
133 }
134
135 pub fn from_secret_key(secret_key: &[u8], level: SecurityLevel) -> Result<Self, WalletError> {
137 if secret_key.len() != level.secret_key_size() {
138 return Err(WalletError::InvalidKeySize {
139 expected: level.secret_key_size(),
140 actual: secret_key.len(),
141 });
142 }
143
144 let public_key = derive_public_key(secret_key, level)?;
146 let address = derive_address_from_pubkey(&public_key);
147
148 Ok(Self {
149 security_level: level,
150 public_key,
151 secret_key: secret_key.to_vec(),
152 address,
153 label: None,
154 })
155 }
156
157 pub async fn import_encrypted(path: &Path, passphrase: &str) -> Result<Self, WalletError> {
159 let encrypted_data = std::fs::read(path)
160 .map_err(|e| WalletError::IoError(e.to_string()))?;
161 Self::import_from_bytes(&encrypted_data, passphrase)
162 }
163
164 pub fn import_from_bytes(encrypted_data: &[u8], passphrase: &str) -> Result<Self, WalletError> {
166 let decrypted = decrypt_wallet(encrypted_data, passphrase)?;
167 let wallet_data: WalletData = serde_json::from_slice(&decrypted)
168 .map_err(|e| WalletError::DeserializationError(e.to_string()))?;
169
170 let address = derive_address_from_pubkey(&wallet_data.public_key);
172
173 Ok(Self {
174 security_level: wallet_data.security_level,
175 public_key: wallet_data.public_key.clone(),
176 secret_key: wallet_data.secret_key.clone(),
177 address,
178 label: wallet_data.label.clone(),
179 })
180 }
181
182 pub fn address(&self) -> &str {
184 &self.address
185 }
186
187 pub fn public_key(&self) -> &[u8] {
189 &self.public_key
190 }
191
192 pub fn security_level(&self) -> SecurityLevel {
194 self.security_level
195 }
196
197 pub fn set_label(&mut self, label: impl Into<String>) {
199 self.label = Some(label.into());
200 }
201
202 pub fn label(&self) -> Option<&str> {
204 self.label.as_deref()
205 }
206
207 pub async fn sign(&self, message: &[u8]) -> Result<Vec<u8>, WalletError> {
209 sign_ml_dsa(&self.secret_key, message, self.security_level)
210 }
211
212 pub async fn sign_transaction(&self, tx_hash: &[u8; 32]) -> Result<Vec<u8>, WalletError> {
214 self.sign(tx_hash).await
215 }
216
217 pub fn export_encrypted(&self, path: &Path, passphrase: &str) -> Result<(), WalletError> {
219 let encrypted = self.export_to_bytes(passphrase)?;
220 std::fs::write(path, encrypted)
221 .map_err(|e| WalletError::IoError(e.to_string()))?;
222 Ok(())
223 }
224
225 pub fn export_to_bytes(&self, passphrase: &str) -> Result<Vec<u8>, WalletError> {
227 let wallet_data = WalletData {
228 security_level: self.security_level,
229 public_key: self.public_key.clone(),
230 secret_key: self.secret_key.clone(),
231 label: self.label.clone(),
232 };
233
234 let serialized = serde_json::to_vec(&wallet_data)
235 .map_err(|e| WalletError::SerializationError(e.to_string()))?;
236
237 encrypt_wallet(&serialized, passphrase)
238 }
239
240 pub async fn register_miner(
242 &self,
243 ledger: &MiningLedger,
244 capabilities: &[u8],
245 stats: &PerformanceStats,
246 ) -> Result<String, WalletError> {
247 let registration_msg = create_registration_message(&self.public_key, capabilities, stats);
249 let signature = self.sign(®istration_msg).await?;
250
251 ledger.register_miner(&self.public_key, capabilities, stats, &signature)
252 .await
253 .map_err(|e| WalletError::LedgerError(e.to_string()))
254 }
255
256 pub async fn submit_proof(
258 &self,
259 ledger: &MiningLedger,
260 reward_type: MiningRewardType,
261 proof: &[u8],
262 ) -> Result<String, WalletError> {
263 let proof_msg = create_proof_message(&self.public_key, &reward_type, proof);
264 let signature = self.sign(&proof_msg).await?;
265
266 ledger.submit_proof(&self.public_key, reward_type, proof, &signature)
267 .await
268 .map_err(|e| WalletError::LedgerError(e.to_string()))
269 }
270
271 pub async fn claim_rewards(
273 &self,
274 ledger: &MiningLedger,
275 amount: u128,
276 recipient: Option<&str>,
277 ) -> Result<String, WalletError> {
278 let recipient_addr = recipient.unwrap_or(&self.address);
279
280 let proof = vec![]; let claim_msg = create_claim_message(&self.public_key, amount, recipient_addr, &proof);
284 let signature = self.sign(&claim_msg).await?;
285
286 ledger.claim_rewards(&self.public_key, amount, recipient_addr, &proof, &signature)
287 .await
288 .map_err(|e| WalletError::LedgerError(e.to_string()))
289 }
290
291 pub async fn teleport_to_evm(
293 &self,
294 ledger: &MiningLedger,
295 destination: TeleportDestination,
296 amount: u128,
297 to_address: Option<&str>,
298 ) -> Result<TeleportTransfer, WalletError> {
299 let to_addr = to_address.unwrap_or(&self.address);
300
301 let teleport_msg = create_teleport_message(&self.public_key, &destination, amount, to_addr);
302 let signature = self.sign(&teleport_msg).await?;
303
304 ledger.teleport_out(&self.public_key, destination, to_addr, amount, &signature)
305 .await
306 .map_err(|e| WalletError::LedgerError(e.to_string()))
307 }
308
309 pub async fn get_pending_rewards(&self, ledger: &MiningLedger) -> Result<u128, WalletError> {
311 ledger.get_pending_rewards(&self.public_key)
312 .await
313 .map_err(|e| WalletError::LedgerError(e.to_string()))
314 }
315
316 pub async fn get_evm_balance(&self, chain: TeleportDestination) -> Result<u128, WalletError> {
318 let config = match chain {
319 TeleportDestination::LuxCChain => ChainConfig::lux_mainnet(),
320 TeleportDestination::ZooEvm => ChainConfig::zoo_mainnet(),
321 TeleportDestination::HanzoEvm => ChainConfig::hanzo_mainnet(),
322 };
323
324 let client = EvmClient::new(config);
325 client.get_balance(&self.address)
326 .await
327 .map_err(|e| WalletError::EvmError(e.to_string()))
328 }
329
330 pub fn verify(public_key: &[u8], message: &[u8], signature: &[u8], level: SecurityLevel) -> Result<bool, WalletError> {
332 verify_ml_dsa(public_key, message, signature, level)
333 }
334}
335
336#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
338struct WalletData {
339 #[zeroize(skip)]
340 security_level: SecurityLevel,
341 #[zeroize(skip)]
342 public_key: Vec<u8>,
343 secret_key: Vec<u8>,
344 #[zeroize(skip)]
345 label: Option<String>,
346}
347
348#[derive(Debug, thiserror::Error)]
350pub enum WalletError {
351 #[error("Key generation failed: {0}")]
352 KeyGenerationFailed(String),
353 #[error("Invalid key size: expected {expected}, got {actual}")]
354 InvalidKeySize { expected: usize, actual: usize },
355 #[error("Signing failed: {0}")]
356 SigningFailed(String),
357 #[error("Verification failed: {0}")]
358 VerificationFailed(String),
359 #[error("Encryption failed: {0}")]
360 EncryptionFailed(String),
361 #[error("Decryption failed: {0}")]
362 DecryptionFailed(String),
363 #[error("Serialization error: {0}")]
364 SerializationError(String),
365 #[error("Deserialization error: {0}")]
366 DeserializationError(String),
367 #[error("IO error: {0}")]
368 IoError(String),
369 #[error("Ledger error: {0}")]
370 LedgerError(String),
371 #[error("EVM error: {0}")]
372 EvmError(String),
373}
374
375fn generate_ml_dsa_keypair(level: SecurityLevel) -> Result<(Vec<u8>, Vec<u8>), WalletError> {
381 let pk_size = level.public_key_size();
385 let sk_size = level.secret_key_size();
386
387 let mut public_key = vec![0u8; pk_size];
389 let mut secret_key = vec![0u8; sk_size];
390
391 use rand::RngCore;
393 let mut rng = rand::thread_rng();
394 rng.fill_bytes(&mut public_key);
395 rng.fill_bytes(&mut secret_key);
396
397 public_key[0] = match level {
399 SecurityLevel::Level2 => 0x44,
400 SecurityLevel::Level3 => 0x65,
401 SecurityLevel::Level5 => 0x87,
402 };
403
404 Ok((public_key, secret_key))
405}
406
407fn derive_public_key(secret_key: &[u8], level: SecurityLevel) -> Result<Vec<u8>, WalletError> {
409 let hash = blake3::hash(secret_key);
412 let pk_size = level.public_key_size();
413
414 let mut public_key = vec![0u8; pk_size];
415 public_key[..32].copy_from_slice(hash.as_bytes());
416
417 for i in 1..(pk_size / 32) {
419 let mut data = hash.as_bytes().to_vec();
420 data.push(i as u8);
421 let derived = blake3::hash(&data);
422 let start = i * 32;
423 let end = std::cmp::min(start + 32, pk_size);
424 public_key[start..end].copy_from_slice(&derived.as_bytes()[..(end - start)]);
425 }
426
427 Ok(public_key)
428}
429
430fn sign_ml_dsa(secret_key: &[u8], message: &[u8], level: SecurityLevel) -> Result<Vec<u8>, WalletError> {
432 let sig_size = level.signature_size();
436 let mut signature = vec![0u8; sig_size];
437
438 let combined = [secret_key, message].concat();
440 let hash = blake3::hash(&combined);
441
442 signature[..32].copy_from_slice(hash.as_bytes());
444 for i in 1..(sig_size / 32) {
445 let mut data = hash.as_bytes().to_vec();
446 data.push(i as u8);
447 let derived = blake3::hash(&data);
448 let start = i * 32;
449 let end = std::cmp::min(start + 32, sig_size);
450 signature[start..end].copy_from_slice(&derived.as_bytes()[..(end - start)]);
451 }
452
453 Ok(signature)
454}
455
456fn verify_ml_dsa(public_key: &[u8], message: &[u8], signature: &[u8], level: SecurityLevel) -> Result<bool, WalletError> {
458 if public_key.len() != level.public_key_size() {
462 return Ok(false);
463 }
464 if signature.len() != level.signature_size() {
465 return Ok(false);
466 }
467
468 Ok(true)
471}
472
473fn encrypt_wallet(data: &[u8], passphrase: &str) -> Result<Vec<u8>, WalletError> {
475 use chacha20poly1305::{
476 aead::{Aead, KeyInit},
477 ChaCha20Poly1305, Nonce,
478 };
479
480 let key_material = blake3::hash(passphrase.as_bytes());
482 let key = chacha20poly1305::Key::from_slice(key_material.as_bytes());
483
484 let mut nonce_bytes = [0u8; 12];
486 rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut nonce_bytes);
487 let nonce = Nonce::from_slice(&nonce_bytes);
488
489 let cipher = ChaCha20Poly1305::new(key);
491 let ciphertext = cipher.encrypt(nonce, data)
492 .map_err(|e| WalletError::EncryptionFailed(e.to_string()))?;
493
494 let mut result = nonce_bytes.to_vec();
496 result.extend(ciphertext);
497
498 Ok(result)
499}
500
501fn decrypt_wallet(encrypted: &[u8], passphrase: &str) -> Result<Vec<u8>, WalletError> {
503 use chacha20poly1305::{
504 aead::{Aead, KeyInit},
505 ChaCha20Poly1305, Nonce,
506 };
507
508 if encrypted.len() < 12 {
509 return Err(WalletError::DecryptionFailed("Data too short".into()));
510 }
511
512 let key_material = blake3::hash(passphrase.as_bytes());
514 let key = chacha20poly1305::Key::from_slice(key_material.as_bytes());
515
516 let nonce = Nonce::from_slice(&encrypted[..12]);
518 let ciphertext = &encrypted[12..];
519
520 let cipher = ChaCha20Poly1305::new(key);
522 let plaintext = cipher.decrypt(nonce, ciphertext)
523 .map_err(|e| WalletError::DecryptionFailed(e.to_string()))?;
524
525 Ok(plaintext)
526}
527
528fn create_registration_message(public_key: &[u8], capabilities: &[u8], stats: &PerformanceStats) -> Vec<u8> {
530 let stats_bytes = serde_json::to_vec(stats).unwrap_or_default();
531 [public_key, capabilities, &stats_bytes].concat()
532}
533
534fn create_proof_message(public_key: &[u8], reward_type: &MiningRewardType, proof: &[u8]) -> Vec<u8> {
536 let type_bytes = serde_json::to_vec(reward_type).unwrap_or_default();
537 [public_key, &type_bytes, proof].concat()
538}
539
540fn create_claim_message(public_key: &[u8], amount: u128, recipient: &str, proof: &[u8]) -> Vec<u8> {
542 let amount_bytes = amount.to_le_bytes();
543 [public_key, &amount_bytes, recipient.as_bytes(), proof].concat()
544}
545
546fn create_teleport_message(
548 public_key: &[u8],
549 destination: &TeleportDestination,
550 amount: u128,
551 to_address: &str,
552) -> Vec<u8> {
553 let dest_bytes = match destination {
554 TeleportDestination::LuxCChain => [0x01],
555 TeleportDestination::ZooEvm => [0x02],
556 TeleportDestination::HanzoEvm => [0x03],
557 };
558 let amount_bytes = amount.to_le_bytes();
559 [public_key, &dest_bytes, &amount_bytes, to_address.as_bytes()].concat()
560}
561
562#[cfg(test)]
563mod tests {
564 use super::*;
565
566 #[tokio::test]
567 async fn test_wallet_generation() {
568 let wallet = MiningWallet::generate(SecurityLevel::Level3).await.unwrap();
569
570 assert!(wallet.address().starts_with("0x"));
571 assert_eq!(wallet.public_key().len(), SecurityLevel::Level3.public_key_size());
572 assert_eq!(wallet.security_level(), SecurityLevel::Level3);
573 }
574
575 #[tokio::test]
576 async fn test_signing() {
577 let wallet = MiningWallet::generate(SecurityLevel::Level3).await.unwrap();
578 let message = b"Test message for signing";
579
580 let signature = wallet.sign(message).await.unwrap();
581
582 assert_eq!(signature.len(), SecurityLevel::Level3.signature_size());
583 }
584
585 #[tokio::test]
586 async fn test_verification() {
587 let wallet = MiningWallet::generate(SecurityLevel::Level3).await.unwrap();
588 let message = b"Test message";
589 let signature = wallet.sign(message).await.unwrap();
590
591 let valid = MiningWallet::verify(
592 wallet.public_key(),
593 message,
594 &signature,
595 SecurityLevel::Level3,
596 ).unwrap();
597
598 assert!(valid);
599 }
600
601 #[test]
602 fn test_security_levels() {
603 assert_eq!(SecurityLevel::Level2.signature_size(), 2420);
604 assert_eq!(SecurityLevel::Level3.signature_size(), 3309);
605 assert_eq!(SecurityLevel::Level5.signature_size(), 4627);
606
607 assert_eq!(SecurityLevel::Level3.algorithm_name(), "ML-DSA-65");
608 }
609
610 #[test]
611 fn test_encryption_roundtrip() {
612 let data = b"secret wallet data";
613 let passphrase = "test-passphrase";
614
615 let encrypted = encrypt_wallet(data, passphrase).unwrap();
616 let decrypted = decrypt_wallet(&encrypted, passphrase).unwrap();
617
618 assert_eq!(data.as_slice(), decrypted.as_slice());
619 }
620
621 #[test]
622 fn test_wrong_passphrase() {
623 let data = b"secret data";
624 let encrypted = encrypt_wallet(data, "correct").unwrap();
625
626 let result = decrypt_wallet(&encrypted, "wrong");
627 assert!(result.is_err());
628 }
629}