use ::walletd_bip39::Seed;
use crate::{Bip39Mnemonic, CryptoWallet, CryptoWalletBuilder, HDKey, HDNetworkType, Mnemonic};
use crate::Error;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyPair {
style: MnemonicKeyPairType,
mnemonic_seed: Seed,
mnemonic_phrase: String,
passphrase: Option<String>,
network_type: HDNetworkType,
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
pub enum MnemonicKeyPairType {
#[default]
HDBip39,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct KeyPairBuilder {
mnemonic_phrase: Option<String>,
mnemonic_seed: Option<Seed>,
passphrase: Option<String>,
network_type: HDNetworkType,
style: MnemonicKeyPairType,
}
impl KeyPairBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn mnemonic_phrase(&mut self, mnemonic_phrase: String) -> &mut Self {
self.mnemonic_phrase = Some(mnemonic_phrase);
self
}
pub fn mnemonic_seed(&mut self, mnemonic_seed: Seed) -> &mut Self {
self.mnemonic_seed = Some(mnemonic_seed);
self
}
pub fn passphrase(&mut self, passphrase: String) -> &mut Self {
self.passphrase = Some(passphrase);
self
}
pub fn network_type(&mut self, network_type: HDNetworkType) -> &mut Self {
self.network_type = network_type;
self
}
pub fn style(&mut self, style: MnemonicKeyPairType) -> &mut Self {
self.style = style;
self
}
pub fn no_mnemonic_phrase(&mut self) -> &mut Self {
self.mnemonic_phrase = None;
self
}
pub fn no_mnemonic_seed(&mut self) -> &mut Self {
self.mnemonic_seed = None;
self
}
pub fn no_passphrase(&mut self) -> &mut Self {
self.passphrase = None;
self
}
pub fn build(&mut self) -> Result<KeyPair, Error> {
let mnemonic_phrase = match &self.mnemonic_phrase {
None => {
if self.mnemonic_seed.is_none() {
return Err(Error::MissingKeyPairInfo(
"Neither the mnemonic phrase nor the mnemonic seed was provided"
.to_string(),
));
} else {
"".to_string()
}
}
Some(phrase) => phrase.clone(),
};
let mnemonic_seed: Seed = match &self.mnemonic_seed {
Some(seed) => seed.clone(),
None => match &self.style {
MnemonicKeyPairType::HDBip39 => {
Bip39Mnemonic::detect_language(&mnemonic_phrase, self.passphrase.as_deref())?
.to_seed()
}
},
};
Ok(KeyPair::new(
mnemonic_seed,
mnemonic_phrase,
self.style,
self.passphrase.as_deref(),
self.network_type,
))
}
}
impl KeyPair {
pub fn new(
mnemonic_seed: Seed,
mnemonic_phrase: String,
style: MnemonicKeyPairType,
passphrase_str: Option<&str>,
network_type: HDNetworkType,
) -> Self {
let passphrase = passphrase_str.map(|p| p.to_string());
Self {
style,
mnemonic_seed,
mnemonic_phrase,
passphrase,
network_type,
}
}
pub fn builder() -> KeyPairBuilder {
KeyPairBuilder::new()
}
pub fn mnemonic_seed(&self) -> Seed {
self.mnemonic_seed.clone()
}
pub fn mnemonic_phrase(&self) -> &str {
self.mnemonic_phrase.as_str()
}
pub fn passphrase(&self) -> Option<&str> {
self.passphrase.as_deref()
}
pub fn to_master_key(&self) -> HDKey {
HDKey::new_master(self.mnemonic_seed.to_owned(), self.network_type)
.expect("Failed to create master key")
}
pub fn network_type(&self) -> HDNetworkType {
self.network_type
}
pub fn style(&self) -> MnemonicKeyPairType {
self.style
}
pub fn derive_wallet<T>(&self) -> Result<T, Error>
where
T: CryptoWallet,
T::WalletBuilder: CryptoWalletBuilder<T>,
T::ErrorType: std::fmt::Display,
{
let wallet: T = T::builder()
.master_hd_key(self.to_master_key())
.build()
.map_err(|e| Error::DeriveWallet(e.to_string()))?;
Ok(wallet)
}
}
#[cfg(test)]
mod test_keypair_builder;
#[cfg(test)]
mod test_keypair;