use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
use bip39::Mnemonic;
use secp256k1::{Secp256k1, Signing};
mod bip32;
use self::bip32::{ChildNumber, Xpriv};
#[cfg(feature = "std")]
use crate::SECP256K1;
use crate::{Keys, SecretKey};
const PURPOSE: u32 = 44;
const COIN: u32 = 1237;
#[derive(Debug, Eq, PartialEq)]
pub enum Error {
BIP32(bip32::Error),
BIP39(bip39::Error),
}
impl core::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BIP32(e) => e.fmt(f),
Self::BIP39(e) => e.fmt(f),
}
}
}
impl From<bip32::Error> for Error {
fn from(e: bip32::Error) -> Self {
Self::BIP32(e)
}
}
impl From<bip39::Error> for Error {
fn from(e: bip39::Error) -> Self {
Self::BIP39(e)
}
}
pub trait FromMnemonic: Sized {
type Err;
#[inline]
#[cfg(feature = "std")]
fn from_mnemonic<S>(mnemonic: S, passphrase: Option<S>) -> Result<Self, Self::Err>
where
S: AsRef<str>,
{
Self::from_mnemonic_with_account(mnemonic, passphrase, None)
}
#[inline]
#[cfg(feature = "std")]
fn from_mnemonic_with_account<S>(
mnemonic: S,
passphrase: Option<S>,
account: Option<u32>,
) -> Result<Self, Self::Err>
where
S: AsRef<str>,
{
Self::from_mnemonic_advanced(mnemonic, passphrase, account, None, None)
}
#[inline]
#[cfg(feature = "std")]
fn from_mnemonic_advanced<S>(
mnemonic: S,
passphrase: Option<S>,
account: Option<u32>,
r#type: Option<u32>,
index: Option<u32>,
) -> Result<Self, Self::Err>
where
S: AsRef<str>,
{
Self::from_mnemonic_with_ctx(&SECP256K1, mnemonic, passphrase, account, r#type, index)
}
fn from_mnemonic_with_ctx<C, S>(
secp: &Secp256k1<C>,
mnemonic: S,
passphrase: Option<S>,
account: Option<u32>,
r#type: Option<u32>,
index: Option<u32>,
) -> Result<Self, Self::Err>
where
C: Signing,
S: AsRef<str>;
}
impl FromMnemonic for Keys {
type Err = Error;
fn from_mnemonic_with_ctx<C, S>(
secp: &Secp256k1<C>,
mnemonic: S,
passphrase: Option<S>,
account: Option<u32>,
r#type: Option<u32>,
index: Option<u32>,
) -> Result<Self, Self::Err>
where
C: Signing,
S: AsRef<str>,
{
let mnemonic: Mnemonic = Mnemonic::parse_normalized(mnemonic.as_ref())?;
let seed: [u8; 64] = mnemonic
.to_seed_normalized(passphrase.as_ref().map(|s| s.as_ref()).unwrap_or_default());
let root_key: Xpriv = Xpriv::new_master(&seed)?;
let account: u32 = account.unwrap_or_default();
let _type: u32 = r#type.unwrap_or_default();
let index: u32 = index.unwrap_or_default();
let path: Vec<ChildNumber> = vec![
ChildNumber::from_hardened_idx(PURPOSE)?,
ChildNumber::from_hardened_idx(COIN)?,
ChildNumber::from_hardened_idx(account)?,
ChildNumber::from_normal_idx(_type)?,
ChildNumber::from_normal_idx(index)?,
];
let child_xprv = root_key.derive_xpriv(secp, path);
let secret_key = SecretKey::from(child_xprv.private_key);
Ok(Self::new_with_ctx(secp, secret_key))
}
}
#[cfg(test)]
mod tests {
use core::str::FromStr;
use super::*;
#[test]
fn test_nip06() {
let secp = Secp256k1::new();
let list = vec![
(
"equal dragon fabric refuse stable cherry smoke allow alley easy never medal attend together lumber movie what sad siege weather matrix buffalo state shoot",
"06992419a8fe821dd8de03d4c300614e8feefb5ea936b76f89976dcace8aebee",
),
(
"leader monkey parrot ring guide accident before fence cannon height naive bean",
"7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a",
),
(
"what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade",
"c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add",
),
];
for (mnemonic, expected_secret_key) in list.into_iter() {
let keys =
Keys::from_mnemonic_with_ctx(&secp, mnemonic, None, None, None, None).unwrap();
assert_eq!(
keys.secret_key(),
&SecretKey::from_str(expected_secret_key).unwrap()
);
}
}
}