use bip32::XPrv;
use crate::{signer::Signer, Account, HDWallet, Safe, EncryptionKey};
#[derive(Clone)]
pub struct Vault {
hdwallet: Option<HDWallet>,
private_keys: Vec<XPrv>,
safe: Option<Safe<([u8; 16], usize)>>,
}
impl Vault {
pub fn new() -> Self {
Vault {
hdwallet: Some(HDWallet::new()),
private_keys: vec![],
safe: None,
}
}
pub fn from_phrase(phrase: String) -> Result<Self, String> {
Ok(Vault {
hdwallet: Some(HDWallet::from_mnemonic_str(phrase.as_str())?),
private_keys: vec![],
safe: None,
})
}
pub fn add_key(&mut self) -> Result<Account, String> {
let index = self.private_keys.len();
let hdwallet = self.get_hdwallet()?;
let (private_key, public_key) = hdwallet.keypair_at_path(0, 0, index)?;
self.private_keys.push(private_key);
Ok(Account::from_extended_public_key(&public_key)?)
}
pub fn pop_key(&mut self) -> Result<Account, String> {
match self.private_keys.pop() {
Some(private_key) => Ok(Account::from_extended_public_key(
&private_key.public_key(),
)?),
None => Err("No keys to pop".to_string()),
}
}
pub fn use_signer<T, R>(&self, key_index: usize, mut hook: T) -> Result<R, String>
where
T: FnMut(&Signer) -> R,
{
let signer = Signer::new(self.private_keys[key_index].clone())?;
Ok(hook(&signer))
}
pub fn lock(&mut self, password: &[u8]) -> Result<(), String> {
match &self.hdwallet {
Some(hdwallet) => {
let encryption_key = EncryptionKey::new(password, 1000);
self.safe = Some(Safe::from_plain_bytes(
(encryption_key.salt, self.private_keys.len()),
&encryption_key.pubk,
hdwallet.to_bytes(),
)?);
self.hdwallet = None;
self.private_keys = vec![];
Ok(())
}
None => Ok(()),
}
}
pub fn unlock(&mut self, password: &[u8]) -> Result<(), String> {
match &self.safe {
Some(safe) => {
let encryption_key = EncryptionKey::with_salt(
password,
safe.metadata.0,
1000,
);
let recovered_seed = safe.decrypt(&encryption_key.pubk)?;
let hdwallet = HDWallet::from_bytes(&recovered_seed)?;
self.private_keys = (0..safe.metadata.1)
.map(|index| hdwallet.private_key_at_path(0, 0, index))
.collect::<Result<Vec<XPrv>, String>>()?;
self.safe = None;
self.hdwallet = Some(hdwallet);
Ok(())
}
None => Ok(()),
}
}
fn get_hdwallet(&mut self) -> Result<&mut HDWallet, String> {
match &mut self.hdwallet {
Some(hdwallet) => Ok(hdwallet),
None => Err("Vault is locked".to_string()),
}
}
}