diff --git a/dash-spv/tests/dashd_sync/tests_transaction.rs b/dash-spv/tests/dashd_sync/tests_transaction.rs index f0f005225..e9180c806 100644 --- a/dash-spv/tests/dashd_sync/tests_transaction.rs +++ b/dash-spv/tests/dashd_sync/tests_transaction.rs @@ -17,7 +17,7 @@ use dashcore::PublicKey; use key_wallet::account::ManagedAccountTrait; use key_wallet::bip32::{ChildNumber, ExtendedPrivKey}; use key_wallet::gap_limit::DEFAULT_EXTERNAL_GAP_LIMIT; -use key_wallet::mnemonic::{Language, Mnemonic}; +use key_wallet::mnemonic::Mnemonic; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::coin_selection::SelectionStrategy; use key_wallet::wallet::managed_wallet_info::fee::FeeRate; @@ -263,7 +263,7 @@ const MEMPOOL_TIMEOUT: Duration = Duration::from_secs(30); /// independently of any wallet state, so a test can pay addresses far beyond /// the pre-generated pool window. fn derive_external_addresses(mnemonic: &str, count: u32) -> Vec
{ - let mnemonic = Mnemonic::from_phrase(mnemonic, Language::English).expect("mnemonic"); + let mnemonic = Mnemonic::from_phrase(mnemonic).expect("mnemonic"); let seed = mnemonic.to_seed(""); let secp = Secp256k1::new(); let master = ExtendedPrivKey::new_master(Network::Regtest, &seed).expect("master key"); diff --git a/key-wallet-ffi/src/account_derivation.rs b/key-wallet-ffi/src/account_derivation.rs index 999aeb88e..a8827a305 100644 --- a/key-wallet-ffi/src/account_derivation.rs +++ b/key-wallet-ffi/src/account_derivation.rs @@ -10,7 +10,6 @@ use crate::keys::{FFIExtendedPrivKey, FFIPrivateKey}; use crate::{check_ptr, deref_ptr, unwrap_or_return}; use key_wallet::account::derivation::AccountDerivation; use key_wallet::account::AccountTrait; -use key_wallet::Mnemonic; use std::ffi::CString; use std::os::raw::{c_char, c_uint}; use std::ptr; @@ -124,15 +123,8 @@ pub unsafe extern "C" fn bls_account_derive_private_key_from_mnemonic( } else { Some(unwrap_or_return!(std::ffi::CStr::from_ptr(passphrase).to_str(), error)) }; - let mnemonic_lang = - unwrap_or_return!(Mnemonic::from_phrase_in_any_language(mnemonic_str), error).language(); let sk = unwrap_or_return!( - account.inner().derive_from_mnemonic_private_key_at( - mnemonic_str, - passphrase_str, - mnemonic_lang, - index, - ), + account.inner().derive_from_mnemonic_private_key_at(mnemonic_str, passphrase_str, index,), error ); unwrap_or_return!(CString::new(hex::encode(sk.to_be_bytes())), error).into_raw() @@ -203,15 +195,8 @@ pub unsafe extern "C" fn eddsa_account_derive_private_key_from_mnemonic( } else { Some(unwrap_or_return!(std::ffi::CStr::from_ptr(passphrase).to_str(), error)) }; - let mnemonic_lang = - unwrap_or_return!(Mnemonic::from_phrase_in_any_language(mnemonic_str), error).language(); let sk = unwrap_or_return!( - account.inner().derive_from_mnemonic_private_key_at( - mnemonic_str, - passphrase_str, - mnemonic_lang, - index, - ), + account.inner().derive_from_mnemonic_private_key_at(mnemonic_str, passphrase_str, index,), error ); unwrap_or_return!(CString::new(hex::encode(sk.to_bytes())), error).into_raw() @@ -360,15 +345,10 @@ pub unsafe extern "C" fn account_derive_extended_private_key_from_mnemonic( } else { Some(unwrap_or_return!(std::ffi::CStr::from_ptr(passphrase).to_str(), error)) }; - let mnemonic_lang = - unwrap_or_return!(Mnemonic::from_phrase_in_any_language(mnemonic_str), error).language(); let derived = unwrap_or_return!( - account.inner().derive_from_mnemonic_extended_xpriv_at( - mnemonic_str, - passphrase_str, - mnemonic_lang, - index, - ), + account + .inner() + .derive_from_mnemonic_extended_xpriv_at(mnemonic_str, passphrase_str, index,), error ); Box::into_raw(Box::new(FFIExtendedPrivKey::from_inner(derived))) @@ -400,15 +380,10 @@ pub unsafe extern "C" fn account_derive_private_key_from_mnemonic( } else { Some(unwrap_or_return!(std::ffi::CStr::from_ptr(passphrase).to_str(), error)) }; - let mnemonic_lang = - unwrap_or_return!(Mnemonic::from_phrase_in_any_language(mnemonic_str), error).language(); let derived = unwrap_or_return!( - account.inner().derive_from_mnemonic_extended_xpriv_at( - mnemonic_str, - passphrase_str, - mnemonic_lang, - index, - ), + account + .inner() + .derive_from_mnemonic_extended_xpriv_at(mnemonic_str, passphrase_str, index,), error ); Box::into_raw(Box::new(FFIPrivateKey::from_secret(derived.private_key))) diff --git a/key-wallet-ffi/src/mnemonic.rs b/key-wallet-ffi/src/mnemonic.rs index 7c5a8ab91..260cd0d72 100644 --- a/key-wallet-ffi/src/mnemonic.rs +++ b/key-wallet-ffi/src/mnemonic.rs @@ -164,7 +164,7 @@ pub unsafe extern "C" fn mnemonic_validate(mnemonic: *const c_char, error: *mut let mnemonic = deref_ptr!(mnemonic, error); let mnemonic_str = unwrap_or_return!(CStr::from_ptr(mnemonic).to_str(), error); - if Mnemonic::from_phrase_in_any_language(mnemonic_str).is_err() { + if Mnemonic::from_phrase(mnemonic_str).is_err() { (*error).set( FFIErrorCode::InvalidMnemonic, "Invalid mnemonic: does not match any supported language", @@ -205,7 +205,7 @@ pub unsafe extern "C" fn mnemonic_to_seed( unwrap_or_return!(CStr::from_ptr(passphrase).to_str(), error) }; - let m = unwrap_or_return!(Mnemonic::from_phrase_in_any_language(mnemonic_str), error); + let m = unwrap_or_return!(Mnemonic::from_phrase(mnemonic_str), error); let seed = m.to_seed(passphrase_str); let seed_bytes: &[u8] = seed.as_ref(); diff --git a/key-wallet-ffi/src/wallet.rs b/key-wallet-ffi/src/wallet.rs index 8354642ea..49edb7546 100644 --- a/key-wallet-ffi/src/wallet.rs +++ b/key-wallet-ffi/src/wallet.rs @@ -40,7 +40,7 @@ pub unsafe extern "C" fn wallet_create_from_mnemonic_with_options( let mnemonic = deref_ptr!(mnemonic, error); let mnemonic_str = unwrap_or_return!(CStr::from_ptr(mnemonic).to_str(), error); - let mnemonic = unwrap_or_return!(Mnemonic::from_phrase_in_any_language(mnemonic_str), error); + let mnemonic = unwrap_or_return!(Mnemonic::from_phrase(mnemonic_str), error); let network_rust: Network = network.into(); let creation_options = if account_options.is_null() { diff --git a/key-wallet-ffi/tests/test_valid_addr.rs b/key-wallet-ffi/tests/test_valid_addr.rs index 2c34a054b..c1d23e54e 100644 --- a/key-wallet-ffi/tests/test_valid_addr.rs +++ b/key-wallet-ffi/tests/test_valid_addr.rs @@ -7,8 +7,7 @@ fn test_valid_testnet_address() { use key_wallet::{Mnemonic, Network, Wallet}; let mnemonic_str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = - Mnemonic::from_phrase(mnemonic_str, key_wallet::mnemonic::Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(mnemonic_str).unwrap(); let wallet = Wallet::from_mnemonic(mnemonic, Network::Testnet, WalletAccountCreationOptions::Default) diff --git a/key-wallet-manager/src/accessors.rs b/key-wallet-manager/src/accessors.rs index edee935b5..a35c10acc 100644 --- a/key-wallet-manager/src/accessors.rs +++ b/key-wallet-manager/src/accessors.rs @@ -272,12 +272,12 @@ impl WalletManager { mod tests { use super::*; use crate::test_helpers::TEST_MNEMONIC; - use key_wallet::mnemonic::{Language, Mnemonic}; + use key_wallet::mnemonic::Mnemonic; use key_wallet::wallet::initialization::WalletAccountCreationOptions; use key_wallet::wallet::managed_wallet_info::ManagedWalletInfo; fn build_wallet() -> Wallet { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); Wallet::from_mnemonic(mnemonic, Network::Testnet, WalletAccountCreationOptions::Default) .expect("wallet from mnemonic") } diff --git a/key-wallet-manager/src/lib.rs b/key-wallet-manager/src/lib.rs index 7cfe06aff..798f1ce17 100644 --- a/key-wallet-manager/src/lib.rs +++ b/key-wallet-manager/src/lib.rs @@ -277,7 +277,7 @@ impl WalletManager { birth_height: CoreBlockHeight, account_creation_options: key_wallet::wallet::initialization::WalletAccountCreationOptions, ) -> Result { - let mnemonic_obj = Mnemonic::from_phrase_in_any_language(mnemonic) + let mnemonic_obj = Mnemonic::from_phrase(mnemonic) .map_err(|e| WalletError::InvalidMnemonic(e.to_string()))?; let wallet = Wallet::from_mnemonic(mnemonic_obj, self.network, account_creation_options) @@ -339,7 +339,7 @@ impl WalletManager { ) -> Result<(Vec, WalletId), WalletError> { use zeroize::Zeroize; - let mnemonic_obj = Mnemonic::from_phrase_in_any_language(mnemonic) + let mnemonic_obj = Mnemonic::from_phrase(mnemonic) .map_err(|e| WalletError::InvalidMnemonic(e.to_string()))?; let mut wallet = diff --git a/key-wallet/examples/account_types.rs b/key-wallet/examples/account_types.rs index 7ecc65b08..7f5b5eb32 100644 --- a/key-wallet/examples/account_types.rs +++ b/key-wallet/examples/account_types.rs @@ -7,7 +7,7 @@ use key_wallet::account::{ }; use key_wallet::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; use key_wallet::managed_account::address_pool::AddressPoolType; -use key_wallet::mnemonic::{Language, Mnemonic}; +use key_wallet::mnemonic::Mnemonic; use key_wallet::Network; use secp256k1::Secp256k1; @@ -18,10 +18,7 @@ use key_wallet::derivation_slip10::ExtendedEd25519PrivKey; fn main() -> Result<(), Box> { // Generate a mnemonic for testing - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - )?; + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")?; let seed = mnemonic.to_seed(""); // Create master key diff --git a/key-wallet/examples/basic_usage.rs b/key-wallet/examples/basic_usage.rs index 96d79ce3c..5995a347c 100644 --- a/key-wallet/examples/basic_usage.rs +++ b/key-wallet/examples/basic_usage.rs @@ -3,7 +3,6 @@ use core::str::FromStr; use dashcore::{Address, Network as DashNetwork}; use key_wallet::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; -use key_wallet::mnemonic::Language; use key_wallet::prelude::*; use key_wallet::Network; @@ -12,10 +11,7 @@ fn main() -> core::result::Result<(), Box> { // 1. Create a mnemonic println!("1. Creating mnemonic..."); - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English - )?; + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about")?; println!(" Mnemonic: {}", mnemonic.phrase()); println!(" Word count: {}", mnemonic.word_count()); diff --git a/key-wallet/src/account/account_collection_test.rs b/key-wallet/src/account/account_collection_test.rs index e6ee1eabc..4a7bbf90a 100644 --- a/key-wallet/src/account/account_collection_test.rs +++ b/key-wallet/src/account/account_collection_test.rs @@ -7,7 +7,7 @@ mod tests { Account, AccountCollection, AccountType, BLSAccount, EdDSAAccount, StandardAccountType, }; use crate::bip32::{ExtendedPrivKey, ExtendedPubKey}; - use crate::mnemonic::{Language, Mnemonic}; + use crate::mnemonic::Mnemonic; use crate::Network; use secp256k1::Secp256k1; @@ -16,10 +16,7 @@ mod tests { let mut collection = AccountCollection::new(); // Create test keys - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); let secp = Secp256k1::new(); diff --git a/key-wallet/src/account/derivation.rs b/key-wallet/src/account/derivation.rs index 45a32e45a..e1f3d6ccd 100644 --- a/key-wallet/src/account/derivation.rs +++ b/key-wallet/src/account/derivation.rs @@ -1,5 +1,4 @@ use crate::managed_account::address_pool::AddressPoolType; -use crate::mnemonic::Language; use crate::{ChildNumber, DerivationPath, Error, Mnemonic}; use dashcore::Address; @@ -199,27 +198,27 @@ pub trait AccountDerivation ) -> Result; /// Derive an extended private key from a BIP39 mnemonic and optional passphrase at the given index. + /// The mnemonic's wordlist is auto-detected ([`Mnemonic::from_phrase`]). fn derive_from_mnemonic_extended_xpriv_at( &self, mnemonic: &str, passphrase: Option<&str>, - language: Language, index: u32, ) -> Result { - let m = Mnemonic::from_phrase(mnemonic, language)?; + let m = Mnemonic::from_phrase(mnemonic)?; let seed = m.to_seed(passphrase.unwrap_or("")); self.derive_from_seed_extended_xpriv_at(&seed, index) } /// Derive a private key from a BIP39 mnemonic and optional passphrase at the given index. + /// The mnemonic's wordlist is auto-detected ([`Mnemonic::from_phrase`]). fn derive_from_mnemonic_private_key_at( &self, mnemonic: &str, passphrase: Option<&str>, - language: Language, index: u32, ) -> Result { - let m = Mnemonic::from_phrase(mnemonic, language)?; + let m = Mnemonic::from_phrase(mnemonic)?; let seed = m.to_seed(passphrase.unwrap_or("")); self.derive_from_seed_private_key_at(&seed, index) } diff --git a/key-wallet/src/account/mod.rs b/key-wallet/src/account/mod.rs index 444ee9768..d30e96b85 100644 --- a/key-wallet/src/account/mod.rs +++ b/key-wallet/src/account/mod.rs @@ -400,13 +400,10 @@ impl fmt::Display for Account { mod tests { use super::*; use crate::bip32::ChildNumber; - use crate::mnemonic::{Language, Mnemonic}; + use crate::mnemonic::Mnemonic; pub(crate) fn test_account() -> Account { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); diff --git a/key-wallet/src/derivation.rs b/key-wallet/src/derivation.rs index b401e8e44..5d342db5f 100644 --- a/key-wallet/src/derivation.rs +++ b/key-wallet/src/derivation.rs @@ -429,10 +429,7 @@ mod tests { // ✓ Test special derivation paths (from DashSync special purpose paths) #[test] fn test_special_derivation_paths() { - let mnemonic = Mnemonic::from_phrase( - "upper renew that grow pelican pave subway relief describe enforce suit hedgehog blossom dose swallow", - crate::mnemonic::Language::English - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("upper renew that grow pelican pave subway relief describe enforce suit hedgehog blossom dose swallow").unwrap(); let seed = mnemonic.to_seed(""); let master_key = ExtendedPrivKey::new_master(crate::Network::Mainnet, &seed).unwrap(); @@ -537,10 +534,7 @@ mod tests { // ✓ Test derivation path builder pattern #[test] fn test_derivation_path_builder() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - crate::mnemonic::Language::English - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master_key = ExtendedPrivKey::new_master(crate::Network::Testnet, &seed).unwrap(); @@ -584,10 +578,7 @@ mod tests { // ✓ Test key signing and verification #[test] fn test_key_signing_deterministic() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - crate::mnemonic::Language::English - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master_key = ExtendedPrivKey::new_master(crate::Network::Testnet, &seed).unwrap(); @@ -629,10 +620,7 @@ mod tests { // ✓ Test key recovery from signature #[test] fn test_key_recovery_from_signature() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - crate::mnemonic::Language::English - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master_key = ExtendedPrivKey::new_master(crate::Network::Testnet, &seed).unwrap(); @@ -676,7 +664,6 @@ mod tests { let mnemonic = Mnemonic::from_phrase( "birth kingdom trash renew flavor utility donkey gasp regular alert pave layer", - crate::mnemonic::Language::English, ) .unwrap(); diff --git a/key-wallet/src/managed_account/address_pool.rs b/key-wallet/src/managed_account/address_pool.rs index 57c021bed..346455abb 100644 --- a/key-wallet/src/managed_account/address_pool.rs +++ b/key-wallet/src/managed_account/address_pool.rs @@ -1329,13 +1329,10 @@ impl Default for AddressPoolBuilder { #[cfg(test)] mod tests { use super::*; - use crate::mnemonic::{Language, Mnemonic}; + use crate::mnemonic::Mnemonic; fn test_key_source() -> KeySource { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); diff --git a/key-wallet/src/mnemonic.rs b/key-wallet/src/mnemonic.rs index dd85645b7..c8446989d 100644 --- a/key-wallet/src/mnemonic.rs +++ b/key-wallet/src/mnemonic.rs @@ -106,11 +106,11 @@ fn word_in_any_list(word: &str) -> bool { /// `true` if the (already-normalized) phrase decodes (all words present + /// valid checksum) in *some* supported language — the boolean face of -/// [`Mnemonic::from_phrase_in_any_language`]. Note this enforces the BIP-39 +/// [`Mnemonic::from_phrase`]. Note this enforces the BIP-39 /// ≥12-word floor; inert here — its only caller is /// [`Mnemonic::cleanup_phrase`]'s early-return gate. fn phrase_is_valid_any(normalized: &str) -> bool { - Mnemonic::from_phrase_in_any_language(normalized).is_ok() + Mnemonic::from_phrase(normalized).is_ok() } /// BIP39 Mnemonic phrase @@ -138,9 +138,9 @@ impl bincode::Decode for Mnemonic { decoder: &mut D, ) -> core::result::Result { let phrase: String = bincode::Decode::decode(decoder)?; - // Same deterministic per-language walk as `from_phrase_in_any_language` + // Same deterministic per-language walk as `from_phrase` // (bip39's autodetect can fail with AmbiguousLanguages on shared words). - Mnemonic::from_phrase_in_any_language(&phrase) + Mnemonic::from_phrase(&phrase) .map_err(|e| bincode::error::DecodeError::OtherString(e.to_string())) } } @@ -151,7 +151,7 @@ impl<'de, C> bincode::BorrowDecode<'de, C> for Mnemonic { decoder: &mut D, ) -> core::result::Result { let phrase: String = bincode::BorrowDecode::borrow_decode(decoder)?; - Mnemonic::from_phrase_in_any_language(&phrase) + Mnemonic::from_phrase(&phrase) .map_err(|e| bincode::error::DecodeError::OtherString(e.to_string())) } } @@ -199,36 +199,43 @@ impl Mnemonic { Err(Error::InvalidMnemonic("Mnemonic generation requires getrandom feature".into())) } - /// Create a mnemonic from a phrase - pub fn from_phrase(phrase: &str, language: Language) -> Result { - let mnemonic = bip39_crate::Mnemonic::parse_in(language.into(), phrase) - .map_err(|e| Error::InvalidMnemonic(e.to_string()))?; - - Ok(Self { - inner: mnemonic, - }) - } - - /// Create a mnemonic from a phrase, trying every supported language. + /// Create a mnemonic from a phrase. /// - /// Languages are tried in [`Language::ALL`] order (English first); the - /// first language in which the phrase fully parses (all words present + - /// valid checksum) wins. This deliberately avoids bip39's autodetecting - /// `Mnemonic::parse`, whose `language_of` fails with `AmbiguousLanguages` - /// when every word is shared across wordlists. First-match is - /// deterministic, and because the BIP-39 seed is PBKDF2 over the phrase - /// text itself (not the resolved language), a hypothetical cross-language - /// full-phrase collision could only affect [`Self::language`] reporting, - /// never the derived seed. - pub fn from_phrase_in_any_language(phrase: &str) -> Result { - for language in Language::ALL { + /// This is THE parse path — the wordlist is auto-detected, so anything + /// [`Self::validate`] accepts also parses wherever key material is + /// derived. Languages are tried in [`Language::ALL`] order (English + /// first); the first language in which the phrase fully parses (all + /// words present + valid checksum) wins. This deliberately avoids + /// bip39's autodetecting `Mnemonic::parse`, whose `language_of` fails + /// with `AmbiguousLanguages` when every word is shared across wordlists. + /// First-match is deterministic, and because the BIP-39 seed is PBKDF2 + /// over the phrase text itself (not the resolved language), a + /// hypothetical cross-language full-phrase collision could only affect + /// [`Self::language`] reporting, never the derived seed. When no + /// language matches, the English parse error is kept in the message — + /// it carries the most useful diagnostics (unknown-word index, bad + /// checksum) for the most common case. + pub fn from_phrase(phrase: &str) -> Result { + let english_error = + match bip39_crate::Mnemonic::parse_in(bip39_crate::Language::English, phrase) { + Ok(inner) => { + return Ok(Self { + inner, + }) + } + Err(error) => error, + }; + for &language in &Language::ALL[1..] { if let Ok(inner) = bip39_crate::Mnemonic::parse_in(language.into(), phrase) { return Ok(Self { inner, }); } } - Err(Error::InvalidMnemonic("does not match any supported language".into())) + Err(Error::InvalidMnemonic(format!( + "phrase does not match any supported BIP-39 wordlist ({})", + english_error + ))) } /// The wordlist language this mnemonic was parsed or generated in. @@ -273,9 +280,12 @@ impl Mnemonic { ExtendedPrivKey::new_master(network, &seed).map_err(Into::into) } - /// Validate a mnemonic phrase - pub fn validate(phrase: &str, language: Language) -> bool { - bip39_crate::Mnemonic::parse_in(language.into(), phrase).is_ok() + /// Validate a mnemonic phrase against every supported wordlist. + /// + /// Defined as "does [`Self::from_phrase`] succeed" so validation and + /// parsing share one path and can never disagree. + pub fn validate(phrase: &str) -> bool { + Self::from_phrase(phrase).is_ok() } /// Normalize a phrase for lenient validation / wordlist-membership input: @@ -416,7 +426,7 @@ impl FromStr for Mnemonic { type Err = Error; fn from_str(s: &str) -> Result { - Self::from_phrase_in_any_language(s) + Self::from_phrase(s) } } @@ -510,12 +520,12 @@ mod tests { #[test] fn test_mnemonic_validation() { let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - assert!(Mnemonic::validate(phrase, Language::English)); + assert!(Mnemonic::validate(phrase)); // Test invalid checksum (from DashSync tests) let invalid_phrase = "bless cloud wheel regular tiny venue bird web grief security dignity zoo"; - assert!(!Mnemonic::validate(invalid_phrase, Language::English)); + assert!(!Mnemonic::validate(invalid_phrase)); } // ✓ Test from DashSync DSBIP39Tests.m - BIP39 test vectors @@ -636,8 +646,8 @@ mod tests { // Note: In a real implementation we'd need to handle Czech language, // but for now we can test that the Unicode normalization works in principle // by testing that the same normalized string produces the same results - let mnemonic1 = Mnemonic::from_phrase(words_nfc, Language::English); - let mnemonic2 = Mnemonic::from_phrase(words_nfkd, Language::English); + let mnemonic1 = Mnemonic::from_phrase(words_nfc); + let mnemonic2 = Mnemonic::from_phrase(words_nfkd); // Both should fail to parse as English, but they should fail consistently assert_eq!(mnemonic1.is_ok(), mnemonic2.is_ok()); @@ -663,23 +673,19 @@ mod tests { Language::ChineseTraditional, ] { let phrase = Mnemonic::from_entropy(&entropy, language).unwrap().phrase(); - assert!( - Mnemonic::validate(&phrase, language), - "{language:?} phrase should validate in its own language" - ); + assert!(Mnemonic::validate(&phrase), "{language:?} phrase should validate"); } - // Cross-language negative: a French phrase is not valid as Japanese - // (disjoint wordlists). + // Auto-detection resolves the phrase to its own wordlist. let french = Mnemonic::from_entropy(&entropy, Language::French).unwrap().phrase(); - assert!(!Mnemonic::validate(&french, Language::Japanese)); + assert_eq!(Mnemonic::from_phrase(&french).unwrap().language(), Language::French); } #[test] fn test_multiple_languages() { // English let phrase_en = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic_en = Mnemonic::from_phrase(phrase_en, Language::English).unwrap(); + let mnemonic_en = Mnemonic::from_phrase(phrase_en).unwrap(); assert_eq!(mnemonic_en.word_count(), 12); // Test that we can create mnemonics in different languages @@ -719,12 +725,12 @@ mod tests { // Test parsing a Portuguese mnemonic phrase let phrase_pt = "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abater"; - let parsed_mnemonic = Mnemonic::from_phrase(phrase_pt, Language::Portuguese).unwrap(); + let parsed_mnemonic = Mnemonic::from_phrase(phrase_pt).unwrap(); assert_eq!(parsed_mnemonic.word_count(), 12); // Test validation - assert!(Mnemonic::validate(phrase_pt, Language::Portuguese)); - assert!(!Mnemonic::validate("palavra invalida teste", Language::Portuguese)); + assert!(Mnemonic::validate(phrase_pt)); + assert!(!Mnemonic::validate("palavra invalida teste")); } // ✓ Test edge cases and error conditions @@ -743,13 +749,13 @@ mod tests { assert!(Mnemonic::from_entropy(&invalid_entropy, Language::English).is_err()); // Test empty phrase - assert!(Mnemonic::from_phrase("", Language::English).is_err()); + assert!(Mnemonic::from_phrase("").is_err()); // Test phrase with invalid word - assert!(Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon invalidword", Language::English).is_err()); + assert!(Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon invalidword").is_err()); // Test phrase with wrong word count - assert!(Mnemonic::from_phrase("abandon abandon abandon", Language::English).is_err()); + assert!(Mnemonic::from_phrase("abandon abandon abandon").is_err()); } // ✓ Test from_str implementation @@ -764,7 +770,7 @@ mod tests { #[test] fn test_display() { let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(phrase).unwrap(); assert_eq!(format!("{}", mnemonic), phrase); } @@ -1129,20 +1135,59 @@ mod tests { ]; #[test] - fn test_from_phrase_in_any_language_round_trip_all_languages() { + fn test_ambiguous_wordlist_phrase_is_seed_equivalent() { + // This phrase (entropy 2aef2e023642257119dbff0ae316a5d4) is checksum- + // valid under BOTH Chinese wordlists — the worst case for first-match- + // wins auto-detection. It is seed-safe because bip39's to_seed hashes + // the matched word strings themselves (bip39 2.2.2 reconstructs + // `lang.word_list()[idx]`, a lossless round-trip of the input words), + // so whichever list wins, the seed equals PBKDF2 over the sentence. + // The expected value below is exactly that sentence-PBKDF2, computed + // independently of this codebase — if a future bip39 bump ever made + // the seed depend on which wordlist matched, this assertion catches it. + const DUAL_LIST_PHRASE: &str = "名 苗 壤 喜 七 隆 盾 竟 加 常 弄 幼"; + const DUAL_LIST_SEED_HEX: &str = "fbfe5b8c860317d75fc6f7de4057bc4e67bef2cf408e5b19e1eda451e50b6c65a4272d46317eda3121a36c561f9e84e83b7c3cc8b7a711d3e4c34640c2ecdd73"; + + let mnemonic = Mnemonic::from_phrase(DUAL_LIST_PHRASE).expect("phrase must parse"); + assert!(matches!( + mnemonic.language(), + Language::ChineseSimplified | Language::ChineseTraditional + )); + assert_eq!(mnemonic.to_seed("").to_vec(), Vec::from_hex(DUAL_LIST_SEED_HEX).unwrap()); + } + + #[test] + fn test_from_phrase_seed_with_passphrase_reference_vector() { + // Non-English + passphrase, pinned against an independently computed + // reference (python: pbkdf2_hmac('sha512', NFKD(phrase), + // NFKD('mnemonic'+'TREZOR'), 2048)). Entropy 000102…0f, official + // French wordlist. + const FRENCH_PHRASE: &str = "abaisser agréable inductif agréable éligible achat bolide boucle amateur exister dérober bloquer"; + const FRENCH_SEED_TREZOR_HEX: &str = "984ede340ea47fbf2794c9dcde0c4e2e92bf16a5e172083e0c734835c33c6f667a2c635ce38b0819fab9397c683692cc6f28523072d80b96e031022bbb532992"; + + let mnemonic = Mnemonic::from_phrase(FRENCH_PHRASE).expect("French phrase must parse"); + assert_eq!(mnemonic.language(), Language::French); + assert_eq!( + mnemonic.to_seed("TREZOR").to_vec(), + Vec::from_hex(FRENCH_SEED_TREZOR_HEX).unwrap() + ); + } + + #[test] + fn test_from_phrase_round_trip_all_languages() { for lang in Language::ALL { let phrase = Mnemonic::from_entropy(&ANY_LANGUAGE_TEST_ENTROPY, lang).unwrap().phrase(); - let parsed = Mnemonic::from_phrase_in_any_language(&phrase) + let parsed = Mnemonic::from_phrase(&phrase) .unwrap_or_else(|e| panic!("{lang:?} phrase must parse: {e}")); assert_eq!(parsed.language(), lang, "{lang:?} must be detected"); assert_eq!(parsed.phrase(), phrase); - let direct = Mnemonic::from_phrase(&phrase, lang).unwrap(); + let direct = Mnemonic::from_entropy(&ANY_LANGUAGE_TEST_ENTROPY, lang).unwrap(); assert_eq!(parsed.to_seed(""), direct.to_seed(""), "{lang:?} seed must match"); } } #[test] - fn test_from_phrase_in_any_language_nfc_input_matches_nfkd() { + fn test_from_phrase_nfc_input_matches_nfkd() { use unicode_normalization::UnicodeNormalization; let mnemonic = Mnemonic::from_entropy(&ANY_LANGUAGE_TEST_ENTROPY, Language::French).unwrap(); @@ -1153,34 +1198,36 @@ mod tests { nfc_phrase, nfkd_phrase, "fixture must contain decomposable accents to cover NFC input" ); - let parsed = Mnemonic::from_phrase_in_any_language(&nfc_phrase).unwrap(); + let parsed = Mnemonic::from_phrase(&nfc_phrase).unwrap(); assert_eq!(parsed.language(), Language::French); assert_eq!(parsed.to_seed(""), mnemonic.to_seed("")); } #[test] - fn test_from_phrase_in_any_language_rejects_invalid() { + fn test_from_phrase_rejects_invalid() { let bad_checksum = "abandon abandon abandon abandon abandon abandon abandon abandon \ abandon abandon abandon abandon"; let gibberish = "definitely not a valid mnemonic in any supported wordlist language"; for phrase in [bad_checksum, "", " ", gibberish] { - let err = Mnemonic::from_phrase_in_any_language(phrase).unwrap_err(); + let err = Mnemonic::from_phrase(phrase).unwrap_err(); + // The message names the any-wordlist failure and keeps the + // English parse diagnostics (unknown word, bad checksum) inside. assert!( - err.to_string().contains("does not match any supported language"), + err.to_string().contains("does not match any supported BIP-39 wordlist"), "unexpected error for {phrase:?}: {err}" ); } } #[test] - fn test_from_phrase_in_any_language_english_first() { + fn test_from_phrase_english_first() { // The ordering contract: English is tried first, so English phrases // keep parsing byte-identically to the pre-any-language behavior. assert_eq!(Language::ALL[0], Language::English); assert_eq!(Language::ALL.len(), 10); let phrase = Mnemonic::from_entropy(&ANY_LANGUAGE_TEST_ENTROPY, Language::English).unwrap().phrase(); - let parsed = Mnemonic::from_phrase_in_any_language(&phrase).unwrap(); + let parsed = Mnemonic::from_phrase(&phrase).unwrap(); assert_eq!(parsed.language(), Language::English); } diff --git a/key-wallet/src/mnemonic_tests.rs b/key-wallet/src/mnemonic_tests.rs index f70020d63..3a328e47d 100644 --- a/key-wallet/src/mnemonic_tests.rs +++ b/key-wallet/src/mnemonic_tests.rs @@ -16,14 +16,14 @@ mod tests { assert_eq!(words.len(), 12); // Verify the mnemonic is valid - assert!(Mnemonic::validate(&mnemonic.phrase(), Language::English)); + assert!(Mnemonic::validate(&mnemonic.phrase())); } #[test] fn test_mnemonic_from_phrase() { // Test with a known valid mnemonic let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English); + let mnemonic = Mnemonic::from_phrase(phrase); assert!(mnemonic.is_ok()); let mnemonic = mnemonic.unwrap(); @@ -40,7 +40,7 @@ mod tests { ]; for phrase in invalid_phrases { - let result = Mnemonic::from_phrase(phrase, Language::English); + let result = Mnemonic::from_phrase(phrase); assert!(result.is_err()); } } @@ -49,7 +49,7 @@ mod tests { fn test_mnemonic_to_seed() { // Test seed generation with known test vector let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(phrase).unwrap(); // Test without passphrase let seed = mnemonic.to_seed(""); @@ -71,7 +71,7 @@ mod tests { ]; for (phrase, expected_words) in test_cases { - let mnemonic = Mnemonic::from_phrase(phrase, Language::English); + let mnemonic = Mnemonic::from_phrase(phrase); assert!(mnemonic.is_ok()); let mnemonic = mnemonic.unwrap(); @@ -91,22 +91,14 @@ mod tests { ]; for phrase in valid_phrases { - assert!( - Mnemonic::validate(phrase, Language::English), - "Failed to validate: {}", - phrase - ); + assert!(Mnemonic::validate(phrase), "Failed to validate: {}", phrase); } // Invalid mnemonics let invalid_phrases = vec!["invalid words here", "", " "]; for phrase in invalid_phrases { - assert!( - !Mnemonic::validate(phrase, Language::English), - "Should not validate: {}", - phrase - ); + assert!(!Mnemonic::validate(phrase), "Should not validate: {}", phrase); } } @@ -117,7 +109,7 @@ mod tests { let phrase = original.phrase().to_string(); // Recover from the phrase - let recovered = Mnemonic::from_phrase(&phrase, Language::English).unwrap(); + let recovered = Mnemonic::from_phrase(&phrase).unwrap(); // They should produce the same seed let original_seed = original.to_seed(""); @@ -130,10 +122,7 @@ mod tests { #[test] fn test_mnemonic_with_different_passphrases() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); // Different passphrases should produce different seeds let seed1 = mnemonic.to_seed(""); @@ -154,8 +143,8 @@ mod tests { // Same phrase should always produce same seed let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic1 = Mnemonic::from_phrase(phrase, Language::English).unwrap(); - let mnemonic2 = Mnemonic::from_phrase(phrase, Language::English).unwrap(); + let mnemonic1 = Mnemonic::from_phrase(phrase).unwrap(); + let mnemonic2 = Mnemonic::from_phrase(phrase).unwrap(); let seed1 = mnemonic1.to_seed("test"); let seed2 = mnemonic2.to_seed("test"); diff --git a/key-wallet/src/tests/account_tests.rs b/key-wallet/src/tests/account_tests.rs index 585e35fb0..a232d70c7 100644 --- a/key-wallet/src/tests/account_tests.rs +++ b/key-wallet/src/tests/account_tests.rs @@ -6,16 +6,13 @@ use crate::account::{Account, AccountType, StandardAccountType}; use crate::bip32::{ExtendedPrivKey, ExtendedPubKey}; use crate::managed_account::address_pool::KeySource; use crate::managed_account::managed_account_type::ManagedAccountType; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::Network; use secp256k1::Secp256k1; /// Helper function to create a test wallet with deterministic mnemonic fn create_test_mnemonic() -> Mnemonic { - Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap() + Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap() } /// Helper function to create a test extended private key diff --git a/key-wallet/src/tests/address_pool_tests.rs b/key-wallet/src/tests/address_pool_tests.rs index 473f22730..1d1a7a9ee 100644 --- a/key-wallet/src/tests/address_pool_tests.rs +++ b/key-wallet/src/tests/address_pool_tests.rs @@ -4,16 +4,13 @@ use crate::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; use crate::managed_account::address_pool::{AddressPool, AddressPoolType, KeySource}; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::Network; use secp256k1::Secp256k1; use std::collections::HashSet; fn test_key_source() -> (KeySource, ExtendedPubKey) { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); diff --git a/key-wallet/src/tests/address_reservation_tests.rs b/key-wallet/src/tests/address_reservation_tests.rs index 58c748dbb..f9e14d8b7 100644 --- a/key-wallet/src/tests/address_reservation_tests.rs +++ b/key-wallet/src/tests/address_reservation_tests.rs @@ -7,7 +7,7 @@ use crate::account::{Account, AccountType, ManagedCoreFundsAccount}; use crate::managed_account::address_pool::KeySource; use crate::managed_account::managed_account_trait::ManagedAccountTrait; use crate::managed_account::managed_account_type::ManagedAccountType; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::test_utils::TestWalletContext; use crate::transaction_checking::TransactionContext; use crate::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; @@ -90,10 +90,7 @@ fn test_reserve_receive_without_key_source_errors() { #[test] fn test_reserve_receive_on_non_standard_account_errors() { let network = Network::Testnet; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(network, &seed).unwrap(); diff --git a/key-wallet/src/tests/backup_restore_tests.rs b/key-wallet/src/tests/backup_restore_tests.rs index 49a5bf02a..a03e4d850 100644 --- a/key-wallet/src/tests/backup_restore_tests.rs +++ b/key-wallet/src/tests/backup_restore_tests.rs @@ -3,16 +3,13 @@ //! Tests wallet export, import, and recovery scenarios. use crate::account::{AccountType, StandardAccountType}; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::wallet::{Wallet, WalletType}; use crate::Network; #[test] fn test_wallet_mnemonic_export() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let wallet = Wallet::from_mnemonic( @@ -196,10 +193,7 @@ fn test_wallet_metadata_backup() { #[test] fn test_multi_network_backup_restore() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); // Create separate wallets for each network diff --git a/key-wallet/src/tests/edge_case_tests.rs b/key-wallet/src/tests/edge_case_tests.rs index d99a71183..9e37d9dcf 100644 --- a/key-wallet/src/tests/edge_case_tests.rs +++ b/key-wallet/src/tests/edge_case_tests.rs @@ -4,7 +4,7 @@ use crate::account::{AccountType, StandardAccountType}; use crate::bip32::{ChildNumber, DerivationPath}; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::wallet::Wallet; use crate::Network; use dashcore::hashes::Hash; @@ -47,10 +47,7 @@ fn test_invalid_derivation_paths() { #[test] fn test_corrupted_wallet_data_recovery() { // Test recovery from corrupted wallet data - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let wallet = Wallet::from_mnemonic( @@ -75,10 +72,7 @@ fn test_corrupted_wallet_data_recovery() { #[test] fn test_network_mismatch_handling() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); // Create wallet for testnet @@ -197,7 +191,7 @@ fn test_invalid_mnemonic_words() { ]; for phrase in invalid_mnemonics { - let result = Mnemonic::from_phrase(phrase, Language::English); + let result = Mnemonic::from_phrase(phrase); assert!(result.is_err()); } } @@ -292,10 +286,7 @@ fn test_derivation_path_depth_limits() { #[test] fn test_wallet_recovery_with_missing_accounts() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let mut wallet = Wallet::from_mnemonic( diff --git a/key-wallet/src/tests/integration_tests.rs b/key-wallet/src/tests/integration_tests.rs index ebe235ab1..71a2e48d9 100644 --- a/key-wallet/src/tests/integration_tests.rs +++ b/key-wallet/src/tests/integration_tests.rs @@ -3,16 +3,13 @@ //! Tests full wallet lifecycle, account discovery, and complex scenarios. use crate::account::{AccountType, StandardAccountType}; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::wallet::Wallet; use crate::Network; #[test] fn test_wallet_multiple_accounts() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); // Create wallet and add accounts @@ -43,10 +40,7 @@ fn test_wallet_multiple_accounts() { #[test] fn test_separate_wallets_per_network() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); // Create separate wallets for each network diff --git a/key-wallet/src/tests/performance_tests.rs b/key-wallet/src/tests/performance_tests.rs index 9285ee54c..9603bea77 100644 --- a/key-wallet/src/tests/performance_tests.rs +++ b/key-wallet/src/tests/performance_tests.rs @@ -4,7 +4,7 @@ use crate::account::{AccountType, StandardAccountType}; use crate::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey}; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::wallet::Wallet; use crate::Network; use secp256k1::Secp256k1; @@ -54,10 +54,7 @@ impl PerformanceMetrics { #[test] fn test_key_derivation_performance() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); let secp = Secp256k1::new(); @@ -121,10 +118,7 @@ fn test_account_creation_performance() { #[test] fn test_wallet_recovery_performance() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let iterations = 10; @@ -167,10 +161,7 @@ fn test_wallet_recovery_performance() { fn test_address_generation_batch_performance() { use crate::managed_account::address_pool::{AddressPool, AddressPoolType, KeySource}; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); @@ -236,10 +227,7 @@ fn test_concurrent_derivation_performance() { use std::sync::Arc; use std::thread; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = Arc::new(ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap()); @@ -317,10 +305,7 @@ fn test_wallet_serialization_performance() { fn test_gap_limit_scan_performance() { use crate::managed_account::address_pool::{AddressPool, AddressPoolType, KeySource}; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); @@ -359,10 +344,7 @@ fn test_gap_limit_scan_performance() { #[test] fn test_worst_case_derivation_path() { // Test performance with maximum depth derivation path - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).unwrap(); let secp = Secp256k1::new(); diff --git a/key-wallet/src/tests/provider_key_derivation_tests.rs b/key-wallet/src/tests/provider_key_derivation_tests.rs index 4973e3180..697ecdbcd 100644 --- a/key-wallet/src/tests/provider_key_derivation_tests.rs +++ b/key-wallet/src/tests/provider_key_derivation_tests.rs @@ -8,7 +8,7 @@ use crate::account::derivation::AccountDerivation; use crate::account::AccountType; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::wallet::initialization::WalletAccountCreationOptions; use crate::wallet::Wallet; use crate::{ChildNumber, Network}; @@ -21,7 +21,7 @@ const TEST_SEED_HEX: &str = "5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4"; fn test_wallet(network: Network) -> Wallet { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); Wallet::from_mnemonic(mnemonic, network, WalletAccountCreationOptions::Default).unwrap() } diff --git a/key-wallet/src/tests/scan_script_pubkeys_tests.rs b/key-wallet/src/tests/scan_script_pubkeys_tests.rs index 56351741b..0158a6f52 100644 --- a/key-wallet/src/tests/scan_script_pubkeys_tests.rs +++ b/key-wallet/src/tests/scan_script_pubkeys_tests.rs @@ -20,9 +20,7 @@ const TEST_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; fn setup_wallet_info() -> ManagedWalletInfo { - let mnemonic = - crate::mnemonic::Mnemonic::from_phrase(TEST_MNEMONIC, crate::mnemonic::Language::English) - .unwrap(); + let mnemonic = crate::mnemonic::Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let wallet = Wallet::from_mnemonic(mnemonic, Network::Testnet, WalletAccountCreationOptions::Default) .unwrap(); diff --git a/key-wallet/src/tests/unit_variant_wallet_tests.rs b/key-wallet/src/tests/unit_variant_wallet_tests.rs index 67b5ceeab..84e274475 100644 --- a/key-wallet/src/tests/unit_variant_wallet_tests.rs +++ b/key-wallet/src/tests/unit_variant_wallet_tests.rs @@ -8,7 +8,7 @@ use crate::account::account_collection::AccountCollection; use crate::account::{AccountType, StandardAccountType}; use crate::error::Error; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::wallet::initialization::WalletAccountCreationOptions; use crate::wallet::{Wallet, WalletType}; use crate::Network; @@ -17,7 +17,7 @@ const TEST_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; fn built_full_wallet() -> Wallet { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); Wallet::from_mnemonic(mnemonic, Network::Testnet, WalletAccountCreationOptions::Default) .unwrap() } diff --git a/key-wallet/src/tests/wallet_tests.rs b/key-wallet/src/tests/wallet_tests.rs index 420f1500d..834d1ff97 100644 --- a/key-wallet/src/tests/wallet_tests.rs +++ b/key-wallet/src/tests/wallet_tests.rs @@ -4,7 +4,7 @@ use crate::account::account_collection::AccountCollection; use crate::account::{AccountType, StandardAccountType}; -use crate::mnemonic::{Language, Mnemonic}; +use crate::mnemonic::Mnemonic; use crate::seed::Seed; use crate::wallet::root_extended_keys::RootExtendedPrivKey; use crate::wallet::{Wallet, WalletType}; @@ -36,7 +36,7 @@ fn test_wallet_creation_random() { #[test] fn test_wallet_creation_from_mnemonic() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let wallet = Wallet::from_mnemonic( mnemonic.clone(), @@ -93,7 +93,7 @@ fn test_wallet_creation_from_seed() { #[test] fn test_wallet_creation_from_extended_key() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let seed = mnemonic.to_seed(""); let root_key = RootExtendedPrivKey::new_master(&seed).unwrap(); let master_key = root_key.to_extended_priv_key(Network::Testnet); @@ -123,7 +123,7 @@ fn test_wallet_creation_from_extended_key() { #[test] fn test_wallet_creation_watch_only() { // First create a normal wallet to get the public key - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let seed = mnemonic.to_seed(""); let root_priv_key = RootExtendedPrivKey::new_master(&seed).unwrap(); let root_pub_key = root_priv_key.to_root_extended_pub_key(); @@ -152,7 +152,7 @@ fn test_wallet_creation_watch_only() { #[test] fn test_wallet_id_computation() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let seed = mnemonic.to_seed(""); let root_priv_key = RootExtendedPrivKey::new_master(&seed).unwrap(); let root_pub_key = root_priv_key.to_root_extended_pub_key(); @@ -178,7 +178,7 @@ fn test_wallet_id_computation() { #[test] fn test_wallet_recovery_same_mnemonic() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); // Create two wallets from the same mnemonic let wallet1 = Wallet::from_mnemonic( @@ -314,7 +314,7 @@ fn test_wallet_special_accounts() { #[test] fn test_wallet_deterministic_key_derivation() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let wallet = Wallet::from_mnemonic( mnemonic, @@ -325,7 +325,7 @@ fn test_wallet_deterministic_key_derivation() { // Add same account multiple times to different wallets for _ in 0..3 { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let mut test_wallet = Wallet::from_mnemonic( mnemonic, @@ -354,7 +354,7 @@ fn test_wallet_deterministic_key_derivation() { #[test] fn test_wallet_external_signable() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let seed = mnemonic.to_seed(""); let root_priv_key = RootExtendedPrivKey::new_master(&seed).unwrap(); let root_pub_key = root_priv_key.to_root_extended_pub_key(); diff --git a/key-wallet/src/wallet/backup.rs b/key-wallet/src/wallet/backup.rs index f87886dbe..b1c461096 100644 --- a/key-wallet/src/wallet/backup.rs +++ b/key-wallet/src/wallet/backup.rs @@ -57,17 +57,14 @@ impl Wallet { #[cfg(all(test, feature = "bincode"))] mod tests { use super::*; - use crate::mnemonic::{Language, Mnemonic}; + use crate::mnemonic::Mnemonic; use crate::wallet::initialization::WalletAccountCreationOptions; use crate::Network; #[test] fn test_backup_restore() { // Create a wallet - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let original = Wallet::from_mnemonic( mnemonic, diff --git a/key-wallet/src/wallet/mod.rs b/key-wallet/src/wallet/mod.rs index 43f5001b0..ecfea1243 100644 --- a/key-wallet/src/wallet/mod.rs +++ b/key-wallet/src/wallet/mod.rs @@ -244,7 +244,7 @@ mod tests { use super::*; use crate::account::account_collection::AccountCollection; use crate::account::{AccountType, StandardAccountType}; - use crate::mnemonic::Language; + use crate::wallet::managed_wallet_info::wallet_info_interface::WalletInfoInterface; #[test] @@ -262,10 +262,7 @@ mod tests { #[test] fn test_wallet_from_mnemonic() { - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let wallet = Wallet::from_mnemonic( mnemonic, @@ -344,7 +341,7 @@ mod tests { #[test] fn test_wallet_creation_from_known_mnemonic() { let mnemonic_phrase = "upper renew that grow pelican pave subway relief describe enforce suit hedgehog blossom dose swallow"; - let mnemonic = Mnemonic::from_phrase(mnemonic_phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(mnemonic_phrase).unwrap(); let wallet = Wallet::from_mnemonic( mnemonic, @@ -362,7 +359,7 @@ mod tests { #[test] fn test_wallet_recovery_from_seed() { let mnemonic_phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(mnemonic_phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(mnemonic_phrase).unwrap(); // Create first wallet let wallet1 = Wallet::from_mnemonic( @@ -589,10 +586,7 @@ mod tests { assert_eq!(wallet.wallet_id, computed_id); // Test that wallets from the same mnemonic have the same ID - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ).unwrap(); + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about").unwrap(); let wallet1 = Wallet::from_mnemonic( mnemonic.clone(), @@ -615,7 +609,7 @@ mod tests { "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; fn fixture_root_pub_key(network: Network) -> RootExtendedPubKey { - let mnemonic = Mnemonic::from_phrase(FIXTURE_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(FIXTURE_MNEMONIC).unwrap(); let wallet = Wallet::from_mnemonic( mnemonic, network, @@ -661,7 +655,7 @@ mod tests { #[test] fn test_wallet_id_is_network_scoped_by_default() { let make = |network| { - let mnemonic = Mnemonic::from_phrase(FIXTURE_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(FIXTURE_MNEMONIC).unwrap(); Wallet::from_mnemonic( mnemonic, network, @@ -694,7 +688,7 @@ mod tests { // (c) Same seed + same network => stable id across calls and across wallets. #[test] fn test_wallet_id_is_stable() { - let mnemonic = Mnemonic::from_phrase(FIXTURE_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(FIXTURE_MNEMONIC).unwrap(); let wallet = Wallet::from_mnemonic( mnemonic, Network::Testnet, @@ -705,7 +699,7 @@ mod tests { let first = wallet.compute_wallet_id(); assert_eq!(first, wallet.compute_wallet_id(), "id must be stable across calls"); - let mnemonic2 = Mnemonic::from_phrase(FIXTURE_MNEMONIC, Language::English).unwrap(); + let mnemonic2 = Mnemonic::from_phrase(FIXTURE_MNEMONIC).unwrap(); let wallet2 = Wallet::from_mnemonic( mnemonic2, Network::Testnet, diff --git a/key-wallet/src/wallet_comprehensive_tests.rs b/key-wallet/src/wallet_comprehensive_tests.rs index 80bc14a6a..ce9610b54 100644 --- a/key-wallet/src/wallet_comprehensive_tests.rs +++ b/key-wallet/src/wallet_comprehensive_tests.rs @@ -8,7 +8,7 @@ mod tests { use crate::account::account_collection::AccountCollection; use crate::account::{AccountType, StandardAccountType}; - use crate::mnemonic::{Language, Mnemonic}; + use crate::mnemonic::Mnemonic; use crate::wallet::Wallet; use crate::Network; @@ -36,7 +36,7 @@ mod tests { #[test] fn test_wallet_recovery_from_mnemonic() { - let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(TEST_MNEMONIC).unwrap(); let wallet1 = Wallet::from_mnemonic( mnemonic.clone(), diff --git a/key-wallet/tests/derivation_tests.rs b/key-wallet/tests/derivation_tests.rs index 39eee9d7f..22f38ce35 100644 --- a/key-wallet/tests/derivation_tests.rs +++ b/key-wallet/tests/derivation_tests.rs @@ -6,7 +6,7 @@ //! `key-wallet/src/dip9.rs`, and `key-wallet/src/tests/account_tests.rs`. use dashcore::hashes::Hash; -use key_wallet::mnemonic::{Language, Mnemonic}; +use key_wallet::mnemonic::Mnemonic; use key_wallet::{DerivationPath, ExtendedPrivKey, ExtendedPubKey, Network}; use secp256k1::Secp256k1; use std::str::FromStr; @@ -31,10 +31,7 @@ use std::str::FromStr; fn test_dip17_platform_payment_vector1_mainnet() { use dashcore::crypto::key::PublicKey; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let seed = mnemonic.to_seed(""); @@ -78,10 +75,7 @@ fn test_dip17_platform_payment_vector1_mainnet() { fn test_dip17_platform_payment_vector1_testnet() { use dashcore::crypto::key::PublicKey; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); let seed = mnemonic.to_seed(""); @@ -112,10 +106,7 @@ fn test_dip17_platform_payment_vector1_testnet() { fn test_dip17_platform_payment_vector2() { use dashcore::crypto::key::PublicKey; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); // Test mainnet @@ -174,10 +165,7 @@ fn test_dip17_platform_payment_vector2() { fn test_dip17_platform_payment_vector3_non_default_key_class() { use dashcore::crypto::key::PublicKey; - let mnemonic = Mnemonic::from_phrase( - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - Language::English, - ) + let mnemonic = Mnemonic::from_phrase("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") .unwrap(); // Test mainnet with key_class' = 1' diff --git a/key-wallet/tests/mnemonic_tests.rs b/key-wallet/tests/mnemonic_tests.rs index 96bd6c145..efd262679 100644 --- a/key-wallet/tests/mnemonic_tests.rs +++ b/key-wallet/tests/mnemonic_tests.rs @@ -7,17 +7,17 @@ use key_wallet::Network; fn test_mnemonic_validation() { // Valid 12-word mnemonic let valid_phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - assert!(Mnemonic::validate(valid_phrase, Language::English)); + assert!(Mnemonic::validate(valid_phrase)); // Invalid mnemonic (wrong checksum) let invalid_phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"; - assert!(!Mnemonic::validate(invalid_phrase, Language::English)); + assert!(!Mnemonic::validate(invalid_phrase)); } #[test] fn test_mnemonic_from_phrase() { let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(phrase).unwrap(); assert_eq!(mnemonic.word_count(), 12); assert_eq!(mnemonic.phrase(), phrase); @@ -26,7 +26,7 @@ fn test_mnemonic_from_phrase() { #[test] fn test_mnemonic_to_seed() { let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(phrase).unwrap(); // Test with empty passphrase let seed1 = mnemonic.to_seed(""); @@ -43,7 +43,7 @@ fn test_mnemonic_to_seed() { #[test] fn test_mnemonic_to_extended_key() { let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); + let mnemonic = Mnemonic::from_phrase(phrase).unwrap(); let xprv = mnemonic.to_extended_key("", Network::Mainnet).unwrap(); assert_eq!(xprv.network, Network::Mainnet); @@ -97,7 +97,7 @@ fn test_mnemonic_generation() { assert_eq!(mnemonic.word_count(), word_count); // Generated mnemonic should be valid - assert!(Mnemonic::validate(&mnemonic.phrase(), Language::English)); + assert!(Mnemonic::validate(&mnemonic.phrase())); } } @@ -106,7 +106,7 @@ fn test_different_languages() { let phrase_en = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; // Test English - let mnemonic_en = Mnemonic::from_phrase(phrase_en, Language::English).unwrap(); + let mnemonic_en = Mnemonic::from_phrase(phrase_en).unwrap(); assert!(mnemonic_en.word_count() == 12); // Same seed regardless of language (for same phrase)