1pub mod keystore;
9pub mod password;
10
11use crate::error::{Result, WalletError};
12pub use keystore::{Keystore, QuantumKeyPair, WalletData};
13use qp_rusty_crystals_hdwallet::{generate_mnemonic, HDLattice};
14use rand::{rng, RngCore};
15use serde::{Deserialize, Serialize};
16use sp_core::crypto::Ss58Codec;
17use sp_runtime::traits::IdentifyAccount;
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct WalletInfo {
21 pub name: String,
22 pub address: String,
23 pub created_at: chrono::DateTime<chrono::Utc>,
24 pub key_type: String,
25}
26
27pub struct WalletManager {
29 wallets_dir: std::path::PathBuf,
30}
31
32impl WalletManager {
33 pub fn new() -> Result<Self> {
35 let wallets_dir = dirs::home_dir()
36 .ok_or(WalletError::KeyGeneration)?
37 .join(".quantus")
38 .join("wallets");
39
40 std::fs::create_dir_all(&wallets_dir)?;
42
43 Ok(Self { wallets_dir })
44 }
45
46 pub async fn create_wallet(&self, name: &str, password: Option<&str>) -> Result<WalletInfo> {
48 let keystore = Keystore::new(&self.wallets_dir);
50 if keystore.load_wallet(name)?.is_some() {
51 return Err(WalletError::AlreadyExists.into());
52 }
53
54 let mut seed = [0u8; 32];
56 rng().fill_bytes(&mut seed);
57 let mnemonic = generate_mnemonic(24, seed).map_err(|_| WalletError::KeyGeneration)?;
58 let lattice =
59 HDLattice::from_mnemonic(&mnemonic, None).expect("Failed to generate lattice");
60 let dilithium_keypair = lattice.generate_keys();
61 let quantum_keypair = QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
62
63 let mut metadata = std::collections::HashMap::new();
65 metadata.insert("version".to_string(), "1.0.0".to_string());
66 metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
67
68 let address = quantum_keypair.to_account_id_ss58check();
70
71 let wallet_data = WalletData {
72 name: name.to_string(),
73 keypair: quantum_keypair,
74 mnemonic: Some(mnemonic.clone()),
75 metadata,
76 };
77
78 let password = password.unwrap_or(""); let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, password)?;
81 keystore.save_wallet(&encrypted_wallet)?;
82
83 Ok(WalletInfo {
84 name: name.to_string(),
85 address,
86 created_at: encrypted_wallet.created_at,
87 key_type: "Dilithium ML-DSA-87".to_string(),
88 })
89 }
90
91 pub async fn create_developer_wallet(&self, name: &str) -> Result<WalletInfo> {
93 let keystore = Keystore::new(&self.wallets_dir);
95
96 let resonance_pair = match name {
98 "crystal_alice" => qp_dilithium_crypto::crystal_alice(),
99 "crystal_bob" => qp_dilithium_crypto::dilithium_bob(),
100 "crystal_charlie" => qp_dilithium_crypto::crystal_charlie(),
101 _ => return Err(WalletError::KeyGeneration.into()),
102 };
103
104 let quantum_keypair = QuantumKeyPair::from_resonance_pair(&resonance_pair);
105
106 println!("🔑 Resonance pair: {:?}", resonance_pair.public().into_account().to_ss58check());
107 println!("🔑 Quantum keypair: {:?}", quantum_keypair.to_account_id_ss58check());
108
109 let mut metadata = std::collections::HashMap::new();
111 metadata.insert("version".to_string(), "1.0.0".to_string());
112 metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
113 metadata.insert("test_wallet".to_string(), "true".to_string());
114
115 let address = quantum_keypair.to_account_id_ss58check();
117
118 let wallet_data = WalletData {
119 name: name.to_string(),
120 keypair: quantum_keypair,
121 mnemonic: None, metadata,
123 };
124
125 let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, "")?;
127 keystore.save_wallet(&encrypted_wallet)?;
128
129 Ok(WalletInfo {
130 name: name.to_string(),
131 address,
132 created_at: encrypted_wallet.created_at,
133 key_type: "Dilithium ML-DSA-87".to_string(),
134 })
135 }
136
137 pub fn export_mnemonic(&self, name: &str, password: Option<&str>) -> Result<String> {
139 let final_password = password::get_wallet_password(name, password.map(String::from), None)?;
140
141 let wallet_data = self.load_wallet(name, &final_password)?;
142
143 wallet_data.mnemonic.ok_or_else(|| WalletError::MnemonicNotAvailable.into())
144 }
145
146 pub fn list_wallets(&self) -> Result<Vec<WalletInfo>> {
148 let keystore = Keystore::new(&self.wallets_dir);
149 let wallet_names = keystore.list_wallets()?;
150
151 let mut wallets = Vec::new();
152 for name in wallet_names {
153 if let Some(encrypted_wallet) = keystore.load_wallet(&name)? {
154 let wallet_info = WalletInfo {
156 name: encrypted_wallet.name,
157 address: encrypted_wallet.address, created_at: encrypted_wallet.created_at,
159 key_type: "Dilithium ML-DSA-87".to_string(),
160 };
161 wallets.push(wallet_info);
162 }
163 }
164
165 wallets.sort_by(|a, b| b.created_at.cmp(&a.created_at));
167 Ok(wallets)
168 }
169
170 pub async fn import_wallet(
172 &self,
173 name: &str,
174 mnemonic: &str,
175 password: Option<&str>,
176 ) -> Result<WalletInfo> {
177 let keystore = Keystore::new(&self.wallets_dir);
179 if keystore.load_wallet(name)?.is_some() {
180 return Err(WalletError::AlreadyExists.into());
181 }
182
183 let lattice =
185 HDLattice::from_mnemonic(mnemonic, None).map_err(|_| WalletError::InvalidMnemonic)?;
186 let dilithium_keypair = lattice.generate_keys();
187 let quantum_keypair = QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
188
189 let mut metadata = std::collections::HashMap::new();
191 metadata.insert("version".to_string(), "1.0.0".to_string());
192 metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
193 metadata.insert("imported".to_string(), "true".to_string());
194
195 let address = quantum_keypair.to_account_id_ss58check();
197
198 let wallet_data = WalletData {
199 name: name.to_string(),
200 keypair: quantum_keypair,
201 mnemonic: Some(mnemonic.to_string()),
202 metadata,
203 };
204
205 let password = password.unwrap_or(""); let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, password)?;
208 keystore.save_wallet(&encrypted_wallet)?;
209
210 Ok(WalletInfo {
211 name: name.to_string(),
212 address,
213 created_at: encrypted_wallet.created_at,
214 key_type: "Dilithium ML-DSA-87".to_string(),
215 })
216 }
217
218 pub async fn create_wallet_from_seed(
220 &self,
221 name: &str,
222 seed_hex: &str,
223 password: Option<&str>,
224 ) -> Result<WalletInfo> {
225 let keystore = Keystore::new(&self.wallets_dir);
227 if keystore.load_wallet(name)?.is_some() {
228 return Err(WalletError::AlreadyExists.into());
229 }
230
231 if seed_hex.len() != 64 {
233 return Err(WalletError::InvalidMnemonic.into()); }
235
236 let seed_bytes = hex::decode(seed_hex).map_err(|_| WalletError::InvalidMnemonic)?;
238 if seed_bytes.len() != 32 {
239 return Err(WalletError::InvalidMnemonic.into());
240 }
241
242 let seed_array: [u8; 32] =
244 seed_bytes.try_into().map_err(|_| WalletError::InvalidMnemonic)?;
245
246 println!("Debug: seed_array length: {}", seed_array.len());
247 println!("Debug: seed_hex: {}", seed_hex);
248 println!("Debug: calling DilithiumPair::from_seed");
249
250 let dilithium_pair = qp_dilithium_crypto::types::DilithiumPair::from_seed(&seed_array)
251 .map_err(|e| {
252 println!("Debug: DilithiumPair::from_seed failed with error: {:?}", e);
253 WalletError::InvalidMnemonic
254 })?;
255
256 println!("Debug: DilithiumPair created successfully");
257
258 let quantum_keypair = QuantumKeyPair::from_resonance_pair(&dilithium_pair);
260
261 let mut metadata = std::collections::HashMap::new();
263 metadata.insert("version".to_string(), "1.0.0".to_string());
264 metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
265 metadata.insert("from_seed".to_string(), "true".to_string());
266
267 let address = quantum_keypair.to_account_id_ss58check();
269
270 let wallet_data = WalletData {
271 name: name.to_string(),
272 keypair: quantum_keypair,
273 mnemonic: None, metadata,
275 };
276
277 let password = password.unwrap_or(""); let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, password)?;
280 keystore.save_wallet(&encrypted_wallet)?;
281
282 Ok(WalletInfo {
283 name: name.to_string(),
284 address,
285 created_at: encrypted_wallet.created_at,
286 key_type: "Dilithium ML-DSA-87".to_string(),
287 })
288 }
289
290 pub fn get_wallet(&self, name: &str, password: Option<&str>) -> Result<Option<WalletInfo>> {
292 let keystore = Keystore::new(&self.wallets_dir);
293
294 if let Some(encrypted_wallet) = keystore.load_wallet(name)? {
295 if let Some(pwd) = password {
296 match keystore.decrypt_wallet_data(&encrypted_wallet, pwd) {
298 Ok(wallet_data) => {
299 let address = wallet_data.keypair.to_account_id_ss58check();
300 Ok(Some(WalletInfo {
301 name: wallet_data.name,
302 address,
303 created_at: encrypted_wallet.created_at,
304 key_type: "Dilithium ML-DSA-87".to_string(),
305 }))
306 },
307 Err(_) => {
308 Ok(Some(WalletInfo {
310 name: encrypted_wallet.name,
311 address: "[Wrong password]".to_string(),
312 created_at: encrypted_wallet.created_at,
313 key_type: "Dilithium ML-DSA-87".to_string(),
314 }))
315 },
316 }
317 } else {
318 Ok(Some(WalletInfo {
320 name: encrypted_wallet.name,
321 address: encrypted_wallet.address, created_at: encrypted_wallet.created_at,
323 key_type: "Dilithium ML-DSA-87".to_string(),
324 }))
325 }
326 } else {
327 Ok(None)
328 }
329 }
330
331 pub fn load_wallet(&self, name: &str, password: &str) -> Result<WalletData> {
333 let keystore = Keystore::new(&self.wallets_dir);
334
335 let encrypted_wallet = keystore.load_wallet(name)?.ok_or(WalletError::NotFound)?;
337
338 let wallet_data = keystore.decrypt_wallet_data(&encrypted_wallet, password)?;
340
341 Ok(wallet_data)
342 }
343
344 pub fn delete_wallet(&self, name: &str) -> Result<bool> {
346 let keystore = Keystore::new(&self.wallets_dir);
347 keystore.delete_wallet(name)
348 }
349
350 pub fn find_wallet_address(&self, name: &str) -> Result<Option<String>> {
352 let keystore = Keystore::new(&self.wallets_dir);
353
354 if let Some(encrypted_wallet) = keystore.load_wallet(name)? {
355 Ok(Some(encrypted_wallet.address))
357 } else {
358 Ok(None)
359 }
360 }
361}
362
363pub fn load_keypair_from_wallet(
364 wallet_name: &str,
365 password: Option<String>,
366 password_file: Option<String>,
367) -> Result<QuantumKeyPair> {
368 let wallet_manager = WalletManager::new()?;
369 let wallet_password = password::get_wallet_password(wallet_name, password, password_file)?;
370 let wallet_data = wallet_manager.load_wallet(wallet_name, &wallet_password)?;
371 let keypair = wallet_data.keypair;
372 Ok(keypair)
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use std::fs;
379 use tempfile::TempDir;
380
381 async fn create_test_wallet_manager() -> (WalletManager, TempDir) {
382 let temp_dir = TempDir::new().expect("Failed to create temp directory");
383 let wallets_dir = temp_dir.path().join("wallets");
384 fs::create_dir_all(&wallets_dir).expect("Failed to create wallets directory");
385
386 let wallet_manager = WalletManager { wallets_dir };
387
388 (wallet_manager, temp_dir)
389 }
390
391 #[tokio::test]
392 async fn test_wallet_creation() {
393 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
394
395 let wallet_info = wallet_manager
397 .create_wallet("test-wallet", Some("test-password"))
398 .await
399 .expect("Failed to create wallet");
400
401 assert_eq!(wallet_info.name, "test-wallet");
403 assert!(wallet_info.address.starts_with("qz")); assert_eq!(wallet_info.key_type, "Dilithium ML-DSA-87");
405 assert!(wallet_info.created_at <= chrono::Utc::now());
406 }
407
408 #[tokio::test]
409 async fn test_wallet_already_exists() {
410 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
411
412 wallet_manager
414 .create_wallet("duplicate-wallet", None)
415 .await
416 .expect("Failed to create first wallet");
417
418 let result = wallet_manager.create_wallet("duplicate-wallet", None).await;
420
421 assert!(result.is_err());
422 match result.unwrap_err() {
423 crate::error::QuantusError::Wallet(WalletError::AlreadyExists) => {},
424 _ => panic!("Expected AlreadyExists error"),
425 }
426 }
427
428 #[tokio::test]
429 async fn test_wallet_file_creation() {
430 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
431
432 let _ = wallet_manager
434 .create_wallet("file-test-wallet", Some("password123"))
435 .await
436 .expect("Failed to create wallet");
437
438 let wallet_file = wallet_manager.wallets_dir.join("file-test-wallet.json");
440 assert!(wallet_file.exists(), "Wallet file should exist");
441
442 let file_size = fs::metadata(&wallet_file).expect("Failed to get file metadata").len();
444 assert!(file_size > 0, "Wallet file should not be empty");
445 }
446
447 #[tokio::test]
448 async fn test_keystore_encryption_decryption() {
449 let temp_dir = TempDir::new().expect("Failed to create temp directory");
450 let keystore = keystore::Keystore::new(temp_dir.path());
451
452 let entropy = [1u8; 32]; let dilithium_keypair =
455 qp_rusty_crystals_dilithium::ml_dsa_87::Keypair::generate(Some(&entropy));
456 let quantum_keypair = keystore::QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
457
458 let mut metadata = std::collections::HashMap::new();
459 metadata.insert("test_key".to_string(), "test_value".to_string());
460
461 let original_wallet_data = keystore::WalletData {
462 name: "test-wallet".to_string(),
463 keypair: quantum_keypair,
464 mnemonic: Some(
465 "test mnemonic phrase with twenty four words here for testing purposes only"
466 .to_string(),
467 ),
468 metadata,
469 };
470
471 let encrypted_wallet = keystore
473 .encrypt_wallet_data(&original_wallet_data, "test-password")
474 .expect("Failed to encrypt wallet data");
475
476 assert_eq!(encrypted_wallet.name, "test-wallet");
477 assert!(!encrypted_wallet.encrypted_data.is_empty());
478 assert!(!encrypted_wallet.argon2_salt.is_empty());
479 assert!(!encrypted_wallet.aes_nonce.is_empty());
480
481 let decrypted_wallet_data = keystore
483 .decrypt_wallet_data(&encrypted_wallet, "test-password")
484 .expect("Failed to decrypt wallet data");
485
486 assert_eq!(decrypted_wallet_data.name, original_wallet_data.name);
488 assert_eq!(decrypted_wallet_data.mnemonic, original_wallet_data.mnemonic);
489 assert_eq!(decrypted_wallet_data.metadata, original_wallet_data.metadata);
490 assert_eq!(
491 decrypted_wallet_data.keypair.public_key,
492 original_wallet_data.keypair.public_key
493 );
494 assert_eq!(
495 decrypted_wallet_data.keypair.private_key,
496 original_wallet_data.keypair.private_key
497 );
498 }
499
500 #[tokio::test]
501 async fn test_quantum_keypair_address_generation() {
502 let entropy = [2u8; 32]; let dilithium_keypair =
505 qp_rusty_crystals_dilithium::ml_dsa_87::Keypair::generate(Some(&entropy));
506 let quantum_keypair = keystore::QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
507
508 let account_id = quantum_keypair.to_account_id_32();
510 let ss58_address = quantum_keypair.to_account_id_ss58check();
511
512 assert!(ss58_address.starts_with("qz"), "SS58 address should start with 5");
514 assert!(ss58_address.len() >= 47, "SS58 address should be at least 47 characters");
515
516 let converted_account_bytes = keystore::QuantumKeyPair::ss58_to_account_id(&ss58_address);
518 let account_bytes: &[u8] = account_id.as_ref();
519 assert_eq!(converted_account_bytes, account_bytes);
520 }
521
522 #[tokio::test]
523 async fn test_keystore_save_and_load() {
524 let temp_dir = TempDir::new().expect("Failed to create temp directory");
525 let keystore = keystore::Keystore::new(temp_dir.path());
526
527 let entropy = [3u8; 32]; let dilithium_keypair =
530 qp_rusty_crystals_dilithium::ml_dsa_87::Keypair::generate(Some(&entropy));
531 let quantum_keypair = keystore::QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
532
533 let wallet_data = keystore::WalletData {
534 name: "save-load-test".to_string(),
535 keypair: quantum_keypair,
536 mnemonic: Some("save load test mnemonic phrase".to_string()),
537 metadata: std::collections::HashMap::new(),
538 };
539
540 let encrypted_wallet = keystore
541 .encrypt_wallet_data(&wallet_data, "save-load-password")
542 .expect("Failed to encrypt wallet");
543
544 keystore.save_wallet(&encrypted_wallet).expect("Failed to save wallet");
546
547 let loaded_wallet = keystore
549 .load_wallet("save-load-test")
550 .expect("Failed to load wallet")
551 .expect("Wallet should exist");
552
553 assert_eq!(loaded_wallet.name, encrypted_wallet.name);
555 assert_eq!(loaded_wallet.encrypted_data, encrypted_wallet.encrypted_data);
556 assert_eq!(loaded_wallet.argon2_salt, encrypted_wallet.argon2_salt);
557 assert_eq!(loaded_wallet.aes_nonce, encrypted_wallet.aes_nonce);
558
559 let non_existent = keystore
561 .load_wallet("non-existent-wallet")
562 .expect("Load should succeed but return None");
563 assert!(non_existent.is_none());
564 }
565
566 #[tokio::test]
567 async fn test_mnemonic_generation_and_key_derivation() {
568 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
569
570 let wallet1 = wallet_manager
572 .create_wallet("mnemonic-test-1", None)
573 .await
574 .expect("Failed to create wallet 1");
575
576 let wallet2 = wallet_manager
577 .create_wallet("mnemonic-test-2", None)
578 .await
579 .expect("Failed to create wallet 2");
580
581 assert_ne!(wallet1.address, wallet2.address);
583
584 assert!(wallet1.address.starts_with("qz"));
586 assert!(wallet2.address.starts_with("qz"));
587 }
588
589 #[tokio::test]
590 async fn test_wallet_import() {
591 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
592
593 let test_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art";
595
596 let imported_wallet = wallet_manager
598 .import_wallet("imported-test-wallet", test_mnemonic, Some("import-password"))
599 .await
600 .expect("Failed to import wallet");
601
602 assert_eq!(imported_wallet.name, "imported-test-wallet");
604 assert!(imported_wallet.address.starts_with("qz"));
605 assert_eq!(imported_wallet.key_type, "Dilithium ML-DSA-87");
606
607 let imported_wallet2 = wallet_manager
609 .import_wallet("imported-test-wallet-2", test_mnemonic, None)
610 .await
611 .expect("Failed to import wallet again");
612
613 assert_eq!(imported_wallet.address, imported_wallet2.address);
614 }
615
616 #[tokio::test]
617 async fn test_known_values() {
618 sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom(189));
619
620 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
621 let test_mnemonic = "orchard answer curve patient visual flower maze noise retreat penalty cage small earth domain scan pitch bottom crunch theme club client swap slice raven";
622 let expected_address = "qzpKnCCUvfXQdanRBkoPVDxcXbLja9JkYzv26hTQwP9C5mZWP";
623
624 let imported_wallet = wallet_manager
625 .import_wallet("imported-test-wallet", test_mnemonic, Some("import-password"))
626 .await
627 .expect("Failed to import wallet");
628
629 assert_eq!(imported_wallet.address, expected_address);
631 }
632
633 #[tokio::test]
634 async fn test_wallet_import_invalid_mnemonic() {
635 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
636
637 let invalid_mnemonic = "invalid mnemonic phrase that should not work";
639
640 let result = wallet_manager.import_wallet("invalid-wallet", invalid_mnemonic, None).await;
641
642 assert!(result.is_err());
643 match result.unwrap_err() {
644 crate::error::QuantusError::Wallet(WalletError::InvalidMnemonic) => {},
645 _ => panic!("Expected InvalidMnemonic error"),
646 }
647 }
648
649 #[tokio::test]
650 async fn test_wallet_import_already_exists() {
651 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
652
653 let test_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art";
654
655 wallet_manager
657 .import_wallet("duplicate-import-wallet", test_mnemonic, None)
658 .await
659 .expect("Failed to import first wallet");
660
661 let result = wallet_manager
663 .import_wallet("duplicate-import-wallet", test_mnemonic, None)
664 .await;
665
666 assert!(result.is_err());
667 match result.unwrap_err() {
668 crate::error::QuantusError::Wallet(WalletError::AlreadyExists) => {},
669 _ => panic!("Expected AlreadyExists error"),
670 }
671 }
672
673 #[tokio::test]
674 async fn test_list_wallets() {
675 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
676
677 let wallets = wallet_manager.list_wallets().expect("Failed to list wallets");
679 assert_eq!(wallets.len(), 0);
680
681 wallet_manager
683 .create_wallet("wallet-1", Some("password1"))
684 .await
685 .expect("Failed to create wallet 1");
686
687 wallet_manager
688 .create_wallet("wallet-2", None)
689 .await
690 .expect("Failed to create wallet 2");
691
692 let test_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art";
693 wallet_manager
694 .import_wallet("imported-wallet", test_mnemonic, Some("password3"))
695 .await
696 .expect("Failed to import wallet");
697
698 let wallets = wallet_manager.list_wallets().expect("Failed to list wallets");
700
701 assert_eq!(wallets.len(), 3);
702
703 let wallet_names: Vec<&String> = wallets.iter().map(|w| &w.name).collect();
705 assert!(wallet_names.contains(&&"wallet-1".to_string()));
706 assert!(wallet_names.contains(&&"wallet-2".to_string()));
707 assert!(wallet_names.contains(&&"imported-wallet".to_string()));
708
709 for wallet in &wallets {
711 assert!(wallet.address.starts_with("qz")); assert_eq!(wallet.key_type, "Dilithium ML-DSA-87");
713 }
714
715 assert!(wallets[0].created_at >= wallets[1].created_at);
717 assert!(wallets[1].created_at >= wallets[2].created_at);
718 }
719
720 #[tokio::test]
721 async fn test_get_wallet() {
722 let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
723
724 let created_wallet = wallet_manager
726 .create_wallet("test-get-wallet", Some("test-password"))
727 .await
728 .expect("Failed to create wallet");
729
730 let wallet_info = wallet_manager
732 .get_wallet("test-get-wallet", None)
733 .expect("Failed to get wallet")
734 .expect("Wallet should exist");
735
736 assert_eq!(wallet_info.name, "test-get-wallet");
737 assert_eq!(wallet_info.address, created_wallet.address); let wallet_info = wallet_manager
742 .get_wallet("test-get-wallet", Some("wrong-password"))
743 .expect("Failed to get wallet")
744 .expect("Wallet should exist");
745
746 assert_eq!(wallet_info.name, "test-get-wallet");
747 assert_eq!(wallet_info.address, "[Wrong password]");
749
750 let wallet_info = wallet_manager
752 .get_wallet("test-get-wallet", Some("test-password"))
753 .expect("Failed to get wallet")
754 .expect("Wallet should exist");
755
756 assert_eq!(wallet_info.name, "test-get-wallet");
757 assert_eq!(wallet_info.address, created_wallet.address);
758 assert!(wallet_info.address.starts_with("qz"));
759
760 let result = wallet_manager
762 .get_wallet("non-existent-wallet", None)
763 .expect("Should not error on non-existent wallet");
764
765 assert!(result.is_none());
766 }
767}