Skip to content

Commit

Permalink
test: bump keychain coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
mikesposito committed Sep 21, 2023
1 parent 5cdde0f commit bcb8ab4
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 53 deletions.
19 changes: 14 additions & 5 deletions src/account/keychain/keychain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ impl Keychain {
/// assert!(!key.is_ok());
/// ```
pub fn lock(&mut self, password: &str) -> Result<(), String> {
self.store.update(|state| {
state.accounts = vec![];
});
self.vault.lock(password.as_bytes())
}

Expand All @@ -140,14 +143,20 @@ impl Keychain {
/// use walleth::Keychain;
///
/// let mut keychain = Keychain::new();
/// let account = keychain.add_account().unwrap();
/// keychain.lock("password").unwrap();
///
/// keychain.unlock("password");
/// let key = keychain.add_account();
/// let recovered_accounts = keychain.unlock("password").unwrap();
///
/// assert!(key.is_ok());
/// assert_eq!(account.address, recovered_accounts[0].address);
/// ```
pub fn unlock(&mut self, password: &str) -> Result<(), String> {
self.vault.unlock(password.as_bytes())
pub fn unlock(&mut self, password: &str) -> Result<&Vec<Account>, String> {
let recovered_accounts = self.vault.unlock(password.as_bytes())?;
self.store.update(|state| {
state.accounts = recovered_accounts.clone();
});

Ok(&self.store.get_state().accounts)
}
}

Expand Down
21 changes: 14 additions & 7 deletions src/account/vault/vault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,14 +205,15 @@ impl Vault {
///
/// let mut vault = Vault::new();
///
/// vault.add_key().unwrap();
/// let account = vault.add_key().unwrap();
///
/// vault.lock(b"my secret password").unwrap();
/// vault.unlock(b"my secret password").unwrap();
///
/// assert!(vault.add_key().is_ok());
/// let recovered_accounts = vault.unlock(b"my secret password").unwrap();
///
/// assert_eq!(recovered_accounts.len(), 1);
/// assert_eq!(account.address, recovered_accounts[0].address);
/// ```
pub fn unlock(&mut self, password: &[u8]) -> Result<(), String> {
pub fn unlock(&mut self, password: &[u8]) -> Result<Vec<Account>, String> {
match &self.safe {
Some(safe) => {
// The encryption key is recreated from the password and the salt
Expand All @@ -231,9 +232,15 @@ impl Vault {
// The HD wallet is stored in memory
self.hdwallet = Some(hdwallet);

Ok(())
Ok(
self
.private_keys
.iter()
.map(|key| Ok(Account::from_extended_public_key(&key.public_key())?))
.collect::<Result<Vec<Account>, String>>()?,
)
}
None => Ok(()),
None => Err("Vault is not locked".to_string()),
}
}

Expand Down
186 changes: 145 additions & 41 deletions tests/keychain.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,164 @@
use secp256k1::{PublicKey, Secp256k1};

use walleth::{Keychain, Signable, Controller};
use walleth::{Controller, Keychain, Signable};

const MNEMONIC: &str =
"grocery belt target explain clay essay focus spatial skull brain measure matrix toward visual protect owner stone scale slim ghost panda exact combine game";

#[test]
fn it_creates_new_vault() {
let keychain = Keychain::new();
assert_eq!(keychain.get_state().accounts.len(), 0);
mod new {
use super::*;

#[test]
fn it_creates_a_new_keychain() {
let keychain = Keychain::new();
assert_eq!(keychain.get_state().accounts.len(), 0);
}
}

#[test]
fn it_adds_a_new_account() {
let mut keychain = Keychain::new();
mod from_mnemonic {
use super::*;

keychain.add_account().unwrap();
#[test]
fn it_creates_a_new_keychain_from_mnemonic() {
let keychain = Keychain::from_mnemonic(MNEMONIC.to_string());
assert!(keychain.is_ok());
}

assert_eq!(keychain.get_state().accounts.len(), 1);
#[test]
fn it_fails_with_wrong_mnemonic() {
let keychain = Keychain::from_mnemonic("this is wrong".to_string());
assert!(!keychain.is_ok());
}
}

#[test]
fn it_creates_new_vault_from_existing_mnemonic() {
Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();
mod add_account {
use super::*;

#[test]
fn it_adds_a_new_account() {
let mut keychain = Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();

let key = keychain.add_account().unwrap();

assert_eq!(keychain.get_state().accounts.len(), 1);
assert_eq!(keychain.get_state().accounts[0].address, key.address);
assert_eq!(
keychain.get_state().accounts[0].address,
"0x356281bf5382846adf421cf4d4a9421f5f158592".to_string()
);
}

#[test]
fn it_fails_when_locked() {
let mut keychain = Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();
keychain.lock("my password").unwrap();

let account = keychain.add_account();

assert!(!account.is_ok());
}
}

#[test]
fn it_adds_the_same_key_given_the_same_mnemonic() {
let mut keychain = Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();
mod use_signer {
use super::*;
use secp256k1::{PublicKey, Secp256k1};

let key = keychain.add_account().unwrap();
#[test]
fn it_signs_a_message() {
let mut keychain = Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();
let key = keychain.add_account().unwrap();
let message = Signable::from_bytes(b"Hello world!").unwrap();

assert_eq!(keychain.get_state().accounts.len(), 1);
assert_eq!(keychain.get_state().accounts[0].address, key.address);
assert_eq!(
keychain.get_state().accounts[0].address,
"0x356281bf5382846adf421cf4d4a9421f5f158592".to_string()
);
let signature = keychain
.use_signer(key.address, |signer| signer.sign(&message).unwrap())
.unwrap();

assert!(Secp256k1::new()
.verify_ecdsa(
&message.to_signable_message(),
&signature,
&PublicKey::from_slice(&key.public_key.to_bytes()).unwrap()
)
.is_ok());
}
}

#[test]
fn it_signs_a_message_with_use_signer_hook() {
let mut keychain = Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();
let key = keychain.add_account().unwrap();
let message = Signable::from_bytes(b"Hello world!").unwrap();
let secp256k1 = Secp256k1::new();
mod lock {
use super::*;

#[test]
fn it_locks_the_keychain() {
let mut keychain = Keychain::from_mnemonic(MNEMONIC.to_string()).unwrap();

keychain.lock("my password").unwrap();

assert!(!keychain.add_account().is_ok());
}
}

mod unlock {
use super::*;

#[test]
fn it_unlocks_the_keychain() {
let mut keychain = Keychain::new();
let account = keychain.add_account().unwrap();
keychain.lock("password").unwrap();

let recovered_accounts = keychain.unlock("password").unwrap();

assert_eq!(account.address, recovered_accounts[0].address);
}
}

mod update {

use super::*;

#[test]
fn it_updates_the_keychain_store() {
let mut keychain = Keychain::new();
keychain.add_account().unwrap();

keychain.update(|state| {
state.accounts = vec![];
});

assert_eq!(keychain.get_state().accounts.len(), 0);
}
}

mod subscribe {
// use std::sync::mpsc::channel;
// use std::thread;

// TODO: .subscribe listener should implement `Send` trait
//#[test]
//fn it_subscribes_to_keychain_updates() {
// let mut keychain = Keychain::new();
// let (tx, rx) = channel();
//
// let handle = thread::spawn(move || {
// keychain.subscribe(move |state| {
// tx.send(state.accounts.len()).unwrap();
// });
// });
//
// keychain.add_account().unwrap();
//
// assert_eq!(rx.recv().unwrap(), 1);
//
// handle.join().unwrap();
//}
}

mod get_state {
use super::*;

#[test]
fn it_gets_the_keychain_state() {
let keychain = Keychain::new();

let signature = keychain
.use_signer(key.address, |signer| signer.sign(&message).unwrap())
.unwrap();
let state = keychain.get_state();

assert!(secp256k1
.verify_ecdsa(
&message.to_signable_message(),
&signature,
&PublicKey::from_slice(&key.public_key.to_bytes()).unwrap()
)
.is_ok());
assert_eq!(state.accounts.len(), 0);
}
}

0 comments on commit bcb8ab4

Please sign in to comment.