use super::Mnemonic;
use crate::bip39::Bip39Error;
use bitcoin::bip32::Xpriv;
use std::str::FromStr;
pub trait Bip39 {
fn from_mnemonic(mnemonic: &str, salt: &str) -> Result<Xpriv, Bip39Error> {
let mnemonic = Mnemonic::from_str(mnemonic)?.to_string();
let seed = {
use pbkdf2::pbkdf2_hmac;
let salt = format!("mnemonic{salt}").into_bytes();
let mut output: [u8; 64] = [0; 64];
pbkdf2_hmac::<sha2::Sha512>(mnemonic.as_bytes(), &salt, u32::pow(2, 11), &mut output);
output
};
let xpriv = Xpriv::new_master(crate::NETWORK, &seed)?;
Ok(xpriv)
}
}
impl Bip39 for Xpriv {}
#[cfg(test)]
mod bip39_test_english {
use super::*;
#[test]
fn test_bip39() -> Result<(), Bip39Error> {
#[cfg(not(feature = "testnet"))]
const TEST_DATA: &[[&str; 3]] = &[
["theme rain hollow final expire proud detect wife hotel taxi witness strategy park head forest", "๐๐๐ญ๐",
"xprv9s21ZrQH143K2k5PPw697AeKWWdeQueM2JCKu8bsmF7M7dDmPGHecHJJNGeujWTJ97Fy9PfobsgZfxhcpWaYyAauFMxcy4fo3x7JNnbYQyD"],
];
#[cfg(feature = "testnet")]
const TEST_DATA: &[[&str; 3]] = &[
["theme rain hollow final expire proud detect wife hotel taxi witness strategy park head forest", "๐๐๐ญ๐",
"tprv8ZgxMBicQKsPdZJv4VweGpGJpe3reRgMMr7SmZ2LFDbpuDxrNddQ82fkHSpZjsqcWYnk9VHZmEGN8pFMwivVnDrVn1AvdRPqy3ripW55kfq"]
];
for x in TEST_DATA {
let xpriv = Xpriv::from_mnemonic(x[0], x[1])?;
assert_eq!(xpriv.to_string(), x[2]);
}
Ok(())
}
}
#[cfg(not(feature = "testnet"))]
#[cfg(test)]
mod bip39_test_multilingual {
use super::*;
#[test]
fn test_bip39() -> Result<(), Bip39Error> {
const TEST_DATA: &[[&str; 3]] = &[
["solda osso frasco encontro donzela oficina colono vidraria fruteira sinal visto sacola mirtilo flamingo ereto", "",
"xprv9s21ZrQH143K2KFS6iHoFXZC9Y5AWVKwxZis4GMRkQeaTFHiNRTkrjCsnBZ46s7VNihoMapH64FE93ZbzZ28Ld2oiHh6FYQx4eA8jEisYsc"],
["ๅฒ ่ทจ ๅฐ ๅ ่ ้ฆ ่ฐ ๆ ๆ
ข ๅญ ็
ๅ ้ป ๅฅ ็ฎ", "้ปๅฅ็ฎ",
"xprv9s21ZrQH143K2SwhdXXWCKa3Sj3mw6123eUe4osWEbHavCv7FDqgFChzfedPDmgnHm9qnQrdveb8sVrywNxxBYCXTdaeNyxRRmhF4q33ovb"],
["แแ
ณแฏแ
แ
ฅแธ แแ
กแจแแ
ก แแ
ฉแแ
ฅแฏ แแ
ฎแแ
ฉแจ แแ
งแฏแแ
ฉ แแ
ตแฏแแ
ฅแผ แแ
ฉแแ
ณแท แแ
ชแจแแ
กแผ แแ
ฉแแ
งแผ แแ
ฉแทแแ
ณแฏแ
แ
ฆแจแแ
ณ แแ
ฌแแ
ฉแจ แแ
ฉแบแแ
ฎแฏ แแ
ฑแแ
ฅแผ แแ
ฅแผแแ
งแฏ แแ
ตแแ
กแ
แ
กแท", "๐",
"xprv9s21ZrQH143K43d7XRnapkCsoE2bLUJfA57hYseNpDaJxf5rpuhHgHjSXNMGMpaGYNNZfxxBzv1e2kW5CSy7p1rddfWYXtvYhgC6MPfHd9Z"],
["theme rain hollow final expire proud detect wife hotel taxi witness strategy park head forest", "๐๐๐ญ๐",
"xprv9s21ZrQH143K2k5PPw697AeKWWdeQueM2JCKu8bsmF7M7dDmPGHecHJJNGeujWTJ97Fy9PfobsgZfxhcpWaYyAauFMxcy4fo3x7JNnbYQyD"],
];
for x in TEST_DATA {
let xpriv = Xpriv::from_mnemonic(x[0], x[1])?;
assert_eq!(xpriv.to_string(), x[2]);
}
const INVALID_CHECKSUM: &[&str] = &[
"solda osso frasco encontro donzela oficina colono vidraria fruteira sinal visto sacola mirtilo flamingo final",
"theme rain hollow sinal expire proud detect wife hotel taxi witness strategy park head forest",
"ๅฒ ่ทจ ๅฐ ๅ ่ ้ฆ ่ฐ ๆ ๆ
ข ๅญ ็
ๅฅ ๅ ้ป ็ฎ"
];
for x in INVALID_CHECKSUM {
let r = Xpriv::from_mnemonic(*x, Default::default());
assert!(matches!(r, Err(Bip39Error::InvalidChecksum)));
}
const INVALID_LENGTH: &[&str] = &[
" ่ทจ ๅฐ ๅ ่ ้ฆ ่ฐ ๆ ๆ
ข ๅญ ็
ๅฅ ๅ ้ป ็ฎ",
"theme rain hollow sinal expire proud detect wife hotel taxi witness",
];
for x in INVALID_LENGTH {
let r = Xpriv::from_mnemonic(*x, Default::default());
assert!(matches!(r, Err(Bip39Error::InvalidLength)));
}
Ok(())
}
}