quantus_cli/wallet/
mod.rs

1/// Wallet management module
2///
3/// This module provides functionality for:
4/// - Creating quantum-safe wallets using Dilithium keys
5/// - Importing/exporting wallets with mnemonic phrases
6/// - Encrypting/decrypting wallet data
7/// - Managing multiple wallets
8pub 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 serde::{Deserialize, Serialize};
15use sp_core::crypto::Ss58Codec;
16use sp_runtime::traits::IdentifyAccount;
17/// Wallet information structure
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct WalletInfo {
20	pub name: String,
21	pub address: String,
22	pub created_at: chrono::DateTime<chrono::Utc>,
23	pub key_type: String,
24}
25
26/// Main wallet manager
27pub struct WalletManager {
28	wallets_dir: std::path::PathBuf,
29}
30
31impl WalletManager {
32	/// Create a new wallet manager
33	pub fn new() -> Result<Self> {
34		let wallets_dir = dirs::home_dir()
35			.ok_or(WalletError::KeyGeneration)?
36			.join(".quantus")
37			.join("wallets");
38
39		// Create directory if it doesn't exist
40		std::fs::create_dir_all(&wallets_dir)?;
41
42		Ok(Self { wallets_dir })
43	}
44
45	/// Create a new wallet
46	pub async fn create_wallet(&self, name: &str, password: Option<&str>) -> Result<WalletInfo> {
47		// Check if wallet already exists
48		let keystore = Keystore::new(&self.wallets_dir);
49		if keystore.load_wallet(name)?.is_some() {
50			return Err(WalletError::AlreadyExists.into());
51		}
52
53		// Generate a new Dilithium keypair'
54		let mnemonic = generate_mnemonic(24).map_err(|_| WalletError::KeyGeneration)?;
55		let lattice =
56			HDLattice::from_mnemonic(&mnemonic, None).expect("Failed to generate lattice");
57		let dilithium_keypair = lattice.generate_keys();
58		let quantum_keypair = QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
59
60		// Create wallet data
61		let mut metadata = std::collections::HashMap::new();
62		metadata.insert("version".to_string(), "1.0.0".to_string());
63		metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
64
65		// Generate address from public key (simplified version)
66		let address = quantum_keypair.to_account_id_ss58check();
67
68		let wallet_data = WalletData {
69			name: name.to_string(),
70			keypair: quantum_keypair,
71			mnemonic: Some(mnemonic.clone()),
72			metadata,
73		};
74
75		// Encrypt and save the wallet
76		let password = password.unwrap_or(""); // Use empty password if none provided
77		let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, password)?;
78		keystore.save_wallet(&encrypted_wallet)?;
79
80		Ok(WalletInfo {
81			name: name.to_string(),
82			address,
83			created_at: encrypted_wallet.created_at,
84			key_type: "Dilithium ML-DSA-87".to_string(),
85		})
86	}
87
88	/// Create a new developer wallet
89	pub async fn create_developer_wallet(&self, name: &str) -> Result<WalletInfo> {
90		// Check if wallet already exists
91		let keystore = Keystore::new(&self.wallets_dir);
92
93		// Generate the appropriate test keypair
94		let resonance_pair = match name {
95			"crystal_alice" => qp_dilithium_crypto::crystal_alice(),
96			"crystal_bob" => qp_dilithium_crypto::dilithium_bob(),
97			"crystal_charlie" => qp_dilithium_crypto::crystal_charlie(),
98			_ => return Err(WalletError::KeyGeneration.into()),
99		};
100
101		let quantum_keypair = QuantumKeyPair::from_resonance_pair(&resonance_pair);
102
103		println!("🔑 Resonance pair: {:?}", resonance_pair.public().into_account().to_ss58check());
104		println!("🔑 Quantum keypair: {:?}", quantum_keypair.to_account_id_ss58check());
105
106		// Create wallet data
107		let mut metadata = std::collections::HashMap::new();
108		metadata.insert("version".to_string(), "1.0.0".to_string());
109		metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
110		metadata.insert("test_wallet".to_string(), "true".to_string());
111
112		// Generate address from public key
113		let address = quantum_keypair.to_account_id_ss58check();
114
115		let wallet_data = WalletData {
116			name: name.to_string(),
117			keypair: quantum_keypair,
118			mnemonic: None, // Test wallets don't have mnemonics
119			metadata,
120		};
121
122		// Encrypt and save the wallet with empty password for test wallets
123		let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, "")?;
124		keystore.save_wallet(&encrypted_wallet)?;
125
126		Ok(WalletInfo {
127			name: name.to_string(),
128			address,
129			created_at: encrypted_wallet.created_at,
130			key_type: "Dilithium ML-DSA-87".to_string(),
131		})
132	}
133
134	/// Export a wallet's mnemonic phrase
135	pub fn export_mnemonic(&self, name: &str, password: Option<&str>) -> Result<String> {
136		let final_password = password::get_wallet_password(name, password.map(String::from), None)?;
137
138		let wallet_data = self.load_wallet(name, &final_password)?;
139
140		wallet_data.mnemonic.ok_or_else(|| WalletError::MnemonicNotAvailable.into())
141	}
142
143	/// List all wallets
144	pub fn list_wallets(&self) -> Result<Vec<WalletInfo>> {
145		let keystore = Keystore::new(&self.wallets_dir);
146		let wallet_names = keystore.list_wallets()?;
147
148		let mut wallets = Vec::new();
149		for name in wallet_names {
150			if let Some(encrypted_wallet) = keystore.load_wallet(&name)? {
151				// Create wallet info using stored public address
152				let wallet_info = WalletInfo {
153					name: encrypted_wallet.name,
154					address: encrypted_wallet.address, // Address is stored unencrypted
155					created_at: encrypted_wallet.created_at,
156					key_type: "Dilithium ML-DSA-87".to_string(),
157				};
158				wallets.push(wallet_info);
159			}
160		}
161
162		// Sort by creation date (newest first)
163		wallets.sort_by(|a, b| b.created_at.cmp(&a.created_at));
164		Ok(wallets)
165	}
166
167	/// Import wallet from mnemonic phrase
168	pub async fn import_wallet(
169		&self,
170		name: &str,
171		mnemonic: &str,
172		password: Option<&str>,
173	) -> Result<WalletInfo> {
174		// Check if wallet already exists
175		let keystore = Keystore::new(&self.wallets_dir);
176		if keystore.load_wallet(name)?.is_some() {
177			return Err(WalletError::AlreadyExists.into());
178		}
179
180		// Validate and import from mnemonic
181		let lattice =
182			HDLattice::from_mnemonic(mnemonic, None).map_err(|_| WalletError::InvalidMnemonic)?;
183		let dilithium_keypair = lattice.generate_keys();
184		let quantum_keypair = QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
185
186		// Create wallet data
187		let mut metadata = std::collections::HashMap::new();
188		metadata.insert("version".to_string(), "1.0.0".to_string());
189		metadata.insert("algorithm".to_string(), "ML-DSA-87".to_string());
190		metadata.insert("imported".to_string(), "true".to_string());
191
192		// Generate address from public key
193		let address = quantum_keypair.to_account_id_ss58check();
194
195		let wallet_data = WalletData {
196			name: name.to_string(),
197			keypair: quantum_keypair,
198			mnemonic: Some(mnemonic.to_string()),
199			metadata,
200		};
201
202		// Encrypt and save the wallet
203		let password = password.unwrap_or(""); // Use empty password if none provided
204		let encrypted_wallet = keystore.encrypt_wallet_data(&wallet_data, password)?;
205		keystore.save_wallet(&encrypted_wallet)?;
206
207		Ok(WalletInfo {
208			name: name.to_string(),
209			address,
210			created_at: encrypted_wallet.created_at,
211			key_type: "Dilithium ML-DSA-87".to_string(),
212		})
213	}
214
215	/// Get wallet by name with password for decryption
216	pub fn get_wallet(&self, name: &str, password: Option<&str>) -> Result<Option<WalletInfo>> {
217		let keystore = Keystore::new(&self.wallets_dir);
218
219		if let Some(encrypted_wallet) = keystore.load_wallet(name)? {
220			if let Some(pwd) = password {
221				// Decrypt and show full details
222				match keystore.decrypt_wallet_data(&encrypted_wallet, pwd) {
223					Ok(wallet_data) => {
224						let address = wallet_data.keypair.to_account_id_ss58check();
225						Ok(Some(WalletInfo {
226							name: wallet_data.name,
227							address,
228							created_at: encrypted_wallet.created_at,
229							key_type: "Dilithium ML-DSA-87".to_string(),
230						}))
231					},
232					Err(_) => {
233						// Wrong password, return basic info
234						Ok(Some(WalletInfo {
235							name: encrypted_wallet.name,
236							address: "[Wrong password]".to_string(),
237							created_at: encrypted_wallet.created_at,
238							key_type: "Dilithium ML-DSA-87".to_string(),
239						}))
240					},
241				}
242			} else {
243				// No password provided, return basic info with public address
244				Ok(Some(WalletInfo {
245					name: encrypted_wallet.name,
246					address: encrypted_wallet.address, // Address is public
247					created_at: encrypted_wallet.created_at,
248					key_type: "Dilithium ML-DSA-87".to_string(),
249				}))
250			}
251		} else {
252			Ok(None)
253		}
254	}
255
256	/// Load a wallet from disk and decrypt it with the provided password
257	pub fn load_wallet(&self, name: &str, password: &str) -> Result<WalletData> {
258		let keystore = Keystore::new(&self.wallets_dir);
259
260		// Load the encrypted wallet
261		let encrypted_wallet = keystore.load_wallet(name)?.ok_or(WalletError::NotFound)?;
262
263		// Decrypt the wallet data using the provided password
264		let wallet_data = keystore.decrypt_wallet_data(&encrypted_wallet, password)?;
265
266		Ok(wallet_data)
267	}
268
269	/// Delete a wallet
270	pub fn delete_wallet(&self, name: &str) -> Result<bool> {
271		let keystore = Keystore::new(&self.wallets_dir);
272		keystore.delete_wallet(name)
273	}
274
275	/// Find wallet by name and return its address
276	pub fn find_wallet_address(&self, name: &str) -> Result<Option<String>> {
277		let keystore = Keystore::new(&self.wallets_dir);
278
279		if let Some(encrypted_wallet) = keystore.load_wallet(name)? {
280			// Return the stored address (it's stored unencrypted)
281			Ok(Some(encrypted_wallet.address))
282		} else {
283			Ok(None)
284		}
285	}
286}
287
288pub fn load_keypair_from_wallet(
289	wallet_name: &str,
290	password: Option<String>,
291	password_file: Option<String>,
292) -> Result<QuantumKeyPair> {
293	let wallet_manager = WalletManager::new()?;
294	let wallet_password = password::get_wallet_password(wallet_name, password, password_file)?;
295	let wallet_data = wallet_manager.load_wallet(wallet_name, &wallet_password)?;
296	let keypair = wallet_data.keypair;
297	Ok(keypair)
298}
299
300#[cfg(test)]
301mod tests {
302	use super::*;
303	use std::fs;
304	use tempfile::TempDir;
305
306	async fn create_test_wallet_manager() -> (WalletManager, TempDir) {
307		let temp_dir = TempDir::new().expect("Failed to create temp directory");
308		let wallets_dir = temp_dir.path().join("wallets");
309		fs::create_dir_all(&wallets_dir).expect("Failed to create wallets directory");
310
311		let wallet_manager = WalletManager { wallets_dir };
312
313		(wallet_manager, temp_dir)
314	}
315
316	#[tokio::test]
317	async fn test_wallet_creation() {
318		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
319
320		// Test wallet creation
321		let wallet_info = wallet_manager
322			.create_wallet("test-wallet", Some("test-password"))
323			.await
324			.expect("Failed to create wallet");
325
326		// Verify wallet info
327		assert_eq!(wallet_info.name, "test-wallet");
328		assert!(wallet_info.address.starts_with("5")); // SS58 addresses start with 5
329		assert_eq!(wallet_info.key_type, "Dilithium ML-DSA-87");
330		assert!(wallet_info.created_at <= chrono::Utc::now());
331	}
332
333	#[tokio::test]
334	async fn test_wallet_already_exists() {
335		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
336
337		// Create first wallet
338		wallet_manager
339			.create_wallet("duplicate-wallet", None)
340			.await
341			.expect("Failed to create first wallet");
342
343		// Try to create wallet with same name
344		let result = wallet_manager.create_wallet("duplicate-wallet", None).await;
345
346		assert!(result.is_err());
347		match result.unwrap_err() {
348			crate::error::QuantusError::Wallet(WalletError::AlreadyExists) => {},
349			_ => panic!("Expected AlreadyExists error"),
350		}
351	}
352
353	#[tokio::test]
354	async fn test_wallet_file_creation() {
355		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
356
357		// Create wallet
358		let _ = wallet_manager
359			.create_wallet("file-test-wallet", Some("password123"))
360			.await
361			.expect("Failed to create wallet");
362
363		// Check if wallet file exists
364		let wallet_file = wallet_manager.wallets_dir.join("file-test-wallet.json");
365		assert!(wallet_file.exists(), "Wallet file should exist");
366
367		// Verify file is not empty
368		let file_size = fs::metadata(&wallet_file).expect("Failed to get file metadata").len();
369		assert!(file_size > 0, "Wallet file should not be empty");
370	}
371
372	#[tokio::test]
373	async fn test_keystore_encryption_decryption() {
374		let temp_dir = TempDir::new().expect("Failed to create temp directory");
375		let keystore = keystore::Keystore::new(temp_dir.path());
376
377		// Create test wallet data
378		let entropy = [1u8; 32]; // Use fixed entropy for deterministic tests
379		let dilithium_keypair =
380			qp_rusty_crystals_dilithium::ml_dsa_87::Keypair::generate(Some(&entropy));
381		let quantum_keypair = keystore::QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
382
383		let mut metadata = std::collections::HashMap::new();
384		metadata.insert("test_key".to_string(), "test_value".to_string());
385
386		let original_wallet_data = keystore::WalletData {
387			name: "test-wallet".to_string(),
388			keypair: quantum_keypair,
389			mnemonic: Some(
390				"test mnemonic phrase with twenty four words here for testing purposes only"
391					.to_string(),
392			),
393			metadata,
394		};
395
396		// Test encryption
397		let encrypted_wallet = keystore
398			.encrypt_wallet_data(&original_wallet_data, "test-password")
399			.expect("Failed to encrypt wallet data");
400
401		assert_eq!(encrypted_wallet.name, "test-wallet");
402		assert!(!encrypted_wallet.encrypted_data.is_empty());
403		assert!(!encrypted_wallet.argon2_salt.is_empty());
404		assert!(!encrypted_wallet.aes_nonce.is_empty());
405
406		// Test decryption
407		let decrypted_wallet_data = keystore
408			.decrypt_wallet_data(&encrypted_wallet, "test-password")
409			.expect("Failed to decrypt wallet data");
410
411		// Verify decrypted data matches original
412		assert_eq!(decrypted_wallet_data.name, original_wallet_data.name);
413		assert_eq!(decrypted_wallet_data.mnemonic, original_wallet_data.mnemonic);
414		assert_eq!(decrypted_wallet_data.metadata, original_wallet_data.metadata);
415		assert_eq!(
416			decrypted_wallet_data.keypair.public_key,
417			original_wallet_data.keypair.public_key
418		);
419		assert_eq!(
420			decrypted_wallet_data.keypair.private_key,
421			original_wallet_data.keypair.private_key
422		);
423	}
424
425	#[tokio::test]
426	async fn test_quantum_keypair_address_generation() {
427		// Generate keypair
428		let entropy = [2u8; 32]; // Use different entropy for variety
429		let dilithium_keypair =
430			qp_rusty_crystals_dilithium::ml_dsa_87::Keypair::generate(Some(&entropy));
431		let quantum_keypair = keystore::QuantumKeyPair::from_dilithium_keypair(&dilithium_keypair);
432
433		// Test address generation
434		let account_id = quantum_keypair.to_account_id_32();
435		let ss58_address = quantum_keypair.to_account_id_ss58check();
436
437		// Verify SS58 address format
438		assert!(ss58_address.starts_with("5"), "SS58 address should start with 5");
439		assert!(ss58_address.len() >= 47, "SS58 address should be at least 47 characters");
440
441		// Test round-trip conversion
442		let converted_account_bytes = keystore::QuantumKeyPair::ss58_to_account_id(&ss58_address);
443		let account_bytes: &[u8] = account_id.as_ref();
444		assert_eq!(converted_account_bytes, account_bytes);
445	}
446
447	#[tokio::test]
448	async fn test_keystore_save_and_load() {
449		let temp_dir = TempDir::new().expect("Failed to create temp directory");
450		let keystore = keystore::Keystore::new(temp_dir.path());
451
452		// Create and encrypt wallet data
453		let entropy = [3u8; 32]; // Use different entropy for each test
454		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 wallet_data = keystore::WalletData {
459			name: "save-load-test".to_string(),
460			keypair: quantum_keypair,
461			mnemonic: Some("save load test mnemonic phrase".to_string()),
462			metadata: std::collections::HashMap::new(),
463		};
464
465		let encrypted_wallet = keystore
466			.encrypt_wallet_data(&wallet_data, "save-load-password")
467			.expect("Failed to encrypt wallet");
468
469		// Save wallet
470		keystore.save_wallet(&encrypted_wallet).expect("Failed to save wallet");
471
472		// Load wallet
473		let loaded_wallet = keystore
474			.load_wallet("save-load-test")
475			.expect("Failed to load wallet")
476			.expect("Wallet should exist");
477
478		// Verify loaded wallet matches saved wallet
479		assert_eq!(loaded_wallet.name, encrypted_wallet.name);
480		assert_eq!(loaded_wallet.encrypted_data, encrypted_wallet.encrypted_data);
481		assert_eq!(loaded_wallet.argon2_salt, encrypted_wallet.argon2_salt);
482		assert_eq!(loaded_wallet.aes_nonce, encrypted_wallet.aes_nonce);
483
484		// Test loading non-existent wallet
485		let non_existent = keystore
486			.load_wallet("non-existent-wallet")
487			.expect("Load should succeed but return None");
488		assert!(non_existent.is_none());
489	}
490
491	#[tokio::test]
492	async fn test_mnemonic_generation_and_key_derivation() {
493		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
494
495		// Create multiple wallets to test mnemonic uniqueness
496		let wallet1 = wallet_manager
497			.create_wallet("mnemonic-test-1", None)
498			.await
499			.expect("Failed to create wallet 1");
500
501		let wallet2 = wallet_manager
502			.create_wallet("mnemonic-test-2", None)
503			.await
504			.expect("Failed to create wallet 2");
505
506		// Addresses should be different (extremely unlikely to be the same)
507		assert_ne!(wallet1.address, wallet2.address);
508
509		// Both should be valid SS58 addresses
510		assert!(wallet1.address.starts_with("5"));
511		assert!(wallet2.address.starts_with("5"));
512	}
513
514	#[tokio::test]
515	async fn test_wallet_import() {
516		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
517
518		// Test mnemonic phrase (24 words)
519		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";
520
521		// Import wallet
522		let imported_wallet = wallet_manager
523			.import_wallet("imported-test-wallet", test_mnemonic, Some("import-password"))
524			.await
525			.expect("Failed to import wallet");
526
527		// Verify wallet info
528		assert_eq!(imported_wallet.name, "imported-test-wallet");
529		assert!(imported_wallet.address.starts_with("5"));
530		assert_eq!(imported_wallet.key_type, "Dilithium ML-DSA-87");
531
532		// Import the same mnemonic again should create the same address
533		let imported_wallet2 = wallet_manager
534			.import_wallet("imported-test-wallet-2", test_mnemonic, None)
535			.await
536			.expect("Failed to import wallet again");
537
538		assert_eq!(imported_wallet.address, imported_wallet2.address);
539	}
540
541	#[tokio::test]
542	async fn test_wallet_import_invalid_mnemonic() {
543		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
544
545		// Test with invalid mnemonic
546		let invalid_mnemonic = "invalid mnemonic phrase that should not work";
547
548		let result = wallet_manager.import_wallet("invalid-wallet", invalid_mnemonic, None).await;
549
550		assert!(result.is_err());
551		match result.unwrap_err() {
552			crate::error::QuantusError::Wallet(WalletError::InvalidMnemonic) => {},
553			_ => panic!("Expected InvalidMnemonic error"),
554		}
555	}
556
557	#[tokio::test]
558	async fn test_wallet_import_already_exists() {
559		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
560
561		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";
562
563		// Import first wallet
564		wallet_manager
565			.import_wallet("duplicate-import-wallet", test_mnemonic, None)
566			.await
567			.expect("Failed to import first wallet");
568
569		// Try to import with same name
570		let result = wallet_manager
571			.import_wallet("duplicate-import-wallet", test_mnemonic, None)
572			.await;
573
574		assert!(result.is_err());
575		match result.unwrap_err() {
576			crate::error::QuantusError::Wallet(WalletError::AlreadyExists) => {},
577			_ => panic!("Expected AlreadyExists error"),
578		}
579	}
580
581	#[tokio::test]
582	async fn test_list_wallets() {
583		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
584
585		// Initially should be empty
586		let wallets = wallet_manager.list_wallets().expect("Failed to list wallets");
587		assert_eq!(wallets.len(), 0);
588
589		// Create some wallets
590		wallet_manager
591			.create_wallet("wallet-1", Some("password1"))
592			.await
593			.expect("Failed to create wallet 1");
594
595		wallet_manager
596			.create_wallet("wallet-2", None)
597			.await
598			.expect("Failed to create wallet 2");
599
600		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";
601		wallet_manager
602			.import_wallet("imported-wallet", test_mnemonic, Some("password3"))
603			.await
604			.expect("Failed to import wallet");
605
606		// List wallets
607		let wallets = wallet_manager.list_wallets().expect("Failed to list wallets");
608
609		assert_eq!(wallets.len(), 3);
610
611		// Check that all wallet names are present
612		let wallet_names: Vec<&String> = wallets.iter().map(|w| &w.name).collect();
613		assert!(wallet_names.contains(&&"wallet-1".to_string()));
614		assert!(wallet_names.contains(&&"wallet-2".to_string()));
615		assert!(wallet_names.contains(&&"imported-wallet".to_string()));
616
617		// Check that addresses are real addresses (now stored unencrypted)
618		for wallet in &wallets {
619			assert!(wallet.address.starts_with("5")); // Real SS58 addresses start with 5
620			assert_eq!(wallet.key_type, "Dilithium ML-DSA-87");
621		}
622
623		// Check sorting (newest first)
624		assert!(wallets[0].created_at >= wallets[1].created_at);
625		assert!(wallets[1].created_at >= wallets[2].created_at);
626	}
627
628	#[tokio::test]
629	async fn test_get_wallet() {
630		let (wallet_manager, _temp_dir) = create_test_wallet_manager().await;
631
632		// Create a wallet
633		let created_wallet = wallet_manager
634			.create_wallet("test-get-wallet", Some("test-password"))
635			.await
636			.expect("Failed to create wallet");
637
638		// Test getting wallet without password
639		let wallet_info = wallet_manager
640			.get_wallet("test-get-wallet", None)
641			.expect("Failed to get wallet")
642			.expect("Wallet should exist");
643
644		assert_eq!(wallet_info.name, "test-get-wallet");
645		assert_eq!(wallet_info.address, created_wallet.address); // Now returns real address
646
647		// Test getting wallet with wrong password
648		// Now with real quantum-safe encryption, wrong password should be detected
649		let wallet_info = wallet_manager
650			.get_wallet("test-get-wallet", Some("wrong-password"))
651			.expect("Failed to get wallet")
652			.expect("Wallet should exist");
653
654		assert_eq!(wallet_info.name, "test-get-wallet");
655		// With real encryption, wrong password returns placeholder text
656		assert_eq!(wallet_info.address, "[Wrong password]");
657
658		// Test getting wallet with correct password
659		let wallet_info = wallet_manager
660			.get_wallet("test-get-wallet", Some("test-password"))
661			.expect("Failed to get wallet")
662			.expect("Wallet should exist");
663
664		assert_eq!(wallet_info.name, "test-get-wallet");
665		assert_eq!(wallet_info.address, created_wallet.address);
666		assert!(wallet_info.address.starts_with("5"));
667
668		// Test getting non-existent wallet
669		let result = wallet_manager
670			.get_wallet("non-existent-wallet", None)
671			.expect("Should not error on non-existent wallet");
672
673		assert!(result.is_none());
674	}
675}