lighthouse/crypto/eth2_wallet/tests/tests.rs

282 lines
7.9 KiB
Rust
Raw Normal View History

#![cfg(not(debug_assertions))]
EIP-2386 (draft): Eth2 wallet (#1117) * Add test to understand flow of key storage * First commit * Committing to save trait stuff * Working naive design * Add keystore struct * Move keystore files into their own module * Add serde (de)serialize_with magic * Add keystore test * Fix tests * Add comments and minor fixes * Pass optional params to `to_keystore` function * Add `path` field to keystore * Add function to read Keystore from file * Add test vectors and fix Version serialization * Checksum params is empty object * Add public key to Keystore * Add function for saving keystore into file * Deleted account_manager main.rs * Move keystore module to validator_client * Add save_keystore method to validator_directory * Add load_keystore function. Minor refactorings * Fixed dependencies * Address some review comments * Add Password newtype; derive Zeroize * Fix test * Move keystore into own crate * Remove padding * Add error enum, zeroize more things * Fix comment * Add keystore builder * Remove keystore stuff from val client * Add more tests, comments * Add more comments, test vectors * Progress on improving JSON validation * More JSON verification * Start moving JSON into own mod * Remove old code * Add more tests, reader/writers * Tidy * Move keystore into own file * Move more logic into keystore file * Tidy * Tidy * Allow for odd-character hex * Add more json missing field checks * Use scrypt by default * Tidy, address comments * Test path and uuid in vectors * Fix comment * Add checks for kdf params * Enforce empty kdf message * Expose json_keystore mod * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * First commits on path derivation * Progress with implementation * Move key derivation into own crate * Start defining JSON wallet * Add progress * Split out encrypt/decrypt * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Replace some password usage with slice * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Expose PlainText struct * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add builder * Expose consts, remove Password * Minor progress * Expose SALT_SIZE * First compiling version * Add test vectors * Move dbg assert statement * Add mnemonic, tidy * Tidy * Add testing * Fix broken test * Address review comments Co-authored-by: pawan <pawandhananjay@gmail.com>
2020-05-11 22:54:59 +00:00
use eth2_wallet::{
bip39::{Language, Mnemonic, Seed},
recover_validator_secret, DerivedKey, Error, KeyType, KeystoreError, Wallet, WalletBuilder,
};
use std::fs::OpenOptions;
use tempfile::tempdir;
const NAME: &str = "Wallet McWalletface";
const SEED: &[u8] = &[42; 42];
const WALLET_PASSWORD: &[u8] = &[43; 43];
const VOTING_KEYSTORE_PASSWORD: &[u8] = &[44; 44];
const WITHDRAWAL_KEYSTORE_PASSWORD: &[u8] = &[45; 45];
const MNEMONIC: &str =
"enemy fog enlist laundry nurse hungry discover turkey holiday resemble glad discover";
fn wallet_from_seed() -> Wallet {
WalletBuilder::from_seed_bytes(SEED, WALLET_PASSWORD, NAME.into())
.expect("should init builder")
.build()
.expect("should build wallet")
}
fn recovered_voting_key(wallet: &Wallet, index: u32) -> Vec<u8> {
let (secret, path) = recover_validator_secret(wallet, WALLET_PASSWORD, index, KeyType::Voting)
.expect("should recover voting secret");
assert_eq!(
format!("{}", path),
format!("m/12381/3600/{}/0/0", index),
"path should be as expected"
);
secret.as_bytes().to_vec()
}
fn recovered_withdrawal_key(wallet: &Wallet, index: u32) -> Vec<u8> {
let (secret, path) =
recover_validator_secret(wallet, WALLET_PASSWORD, index, KeyType::Withdrawal)
.expect("should recover withdrawal secret");
assert_eq!(
format!("{}", path),
format!("m/12381/3600/{}/0", index),
"path should be as expected"
);
secret.as_bytes().to_vec()
}
fn manually_derived_voting_key(index: u32) -> Vec<u8> {
DerivedKey::from_seed(SEED)
.expect("should derive master key")
.child(12381)
.child(3600)
.child(index)
.child(0)
.child(0)
.secret()
.to_vec()
}
fn manually_derived_withdrawal_key(index: u32) -> Vec<u8> {
DerivedKey::from_seed(SEED)
.expect("should derive master key")
.child(12381)
.child(3600)
.child(index)
.child(0)
.secret()
.to_vec()
}
#[test]
fn mnemonic_equality() {
let m = Mnemonic::from_phrase(MNEMONIC, Language::English).unwrap();
let from_mnemonic = WalletBuilder::from_mnemonic(&m, WALLET_PASSWORD, NAME.into())
.expect("should init builder")
.build()
.expect("should build wallet");
let seed = Seed::new(&m, "");
let from_seed = WalletBuilder::from_seed_bytes(seed.as_bytes(), WALLET_PASSWORD, NAME.into())
.expect("should init builder")
.build()
.expect("should build wallet");
assert_eq!(
from_mnemonic
.decrypt_seed(WALLET_PASSWORD)
.unwrap()
.as_bytes(),
from_seed.decrypt_seed(WALLET_PASSWORD).unwrap().as_bytes(),
"wallet from mnemonic should match wallet from seed"
);
}
#[test]
fn metadata() {
let wallet = wallet_from_seed();
assert_eq!(wallet.name(), NAME, "name");
assert_eq!(&wallet.type_field(), "hierarchical deterministic", "name");
assert_eq!(wallet.nextaccount(), 0, "name");
}
#[test]
fn string_round_trip() {
let wallet = wallet_from_seed();
let json = wallet.to_json_string().unwrap();
let decoded = Wallet::from_json_str(&json).unwrap();
assert_eq!(
decoded.decrypt_seed(&[1, 2, 3]).err().unwrap(),
Error::KeystoreError(KeystoreError::InvalidPassword),
"should not decrypt with bad password"
);
assert_eq!(
wallet.decrypt_seed(WALLET_PASSWORD).unwrap().as_bytes(),
decoded.decrypt_seed(WALLET_PASSWORD).unwrap().as_bytes(),
"should decrypt with good password"
);
}
#[test]
fn file_round_trip() {
let wallet = wallet_from_seed();
let dir = tempdir().unwrap();
let path = dir.path().join("keystore.json");
let get_file = || {
OpenOptions::new()
.write(true)
.read(true)
.create(true)
.open(path.clone())
.expect("should create file")
};
wallet
.to_json_writer(&mut get_file())
.expect("should write to file");
let decoded = Wallet::from_json_reader(&mut get_file()).unwrap();
assert_eq!(
decoded.decrypt_seed(&[1, 2, 3]).err().unwrap(),
Error::KeystoreError(KeystoreError::InvalidPassword),
"should not decrypt with bad password"
);
assert_eq!(
wallet.decrypt_seed(WALLET_PASSWORD).unwrap().as_bytes(),
decoded.decrypt_seed(WALLET_PASSWORD).unwrap().as_bytes(),
"should decrypt with good password"
);
}
#[test]
fn empty_wallet_password() {
assert_eq!(
WalletBuilder::from_seed_bytes(SEED, &[], NAME.into())
.err()
.expect("should error"),
Error::EmptyPassword
)
}
#[test]
fn empty_wallet_seed() {
assert_eq!(
WalletBuilder::from_seed_bytes(&[], WALLET_PASSWORD, NAME.into())
.err()
.expect("should error"),
Error::EmptySeed
)
}
#[test]
fn empty_keystore_password() {
let mut wallet = wallet_from_seed();
assert_eq!(wallet.nextaccount(), 0, "initial nextaccount");
assert_eq!(
wallet
.next_validator(WALLET_PASSWORD, &[], WITHDRAWAL_KEYSTORE_PASSWORD,)
.err()
.expect("should error"),
Error::KeystoreError(KeystoreError::EmptyPassword),
"should fail with empty voting password"
);
assert_eq!(wallet.nextaccount(), 0, "next account should not update");
assert_eq!(
wallet
.next_validator(WALLET_PASSWORD, VOTING_KEYSTORE_PASSWORD, &[],)
.err()
.expect("should error"),
Error::KeystoreError(KeystoreError::EmptyPassword),
"should fail with empty withdrawal password"
);
assert_eq!(wallet.nextaccount(), 0, "next account should not update");
}
#[test]
fn key_derivation_from_seed() {
let mut wallet = wallet_from_seed();
for i in 0..4 {
assert_eq!(wallet.nextaccount(), i, "initial nextaccount");
let keystores = wallet
.next_validator(
WALLET_PASSWORD,
VOTING_KEYSTORE_PASSWORD,
WITHDRAWAL_KEYSTORE_PASSWORD,
)
.expect("should generate keystores");
assert_eq!(
keystores.voting.path(),
format!("m/12381/3600/{}/0/0", i),
"voting path should match"
);
assert_eq!(
keystores.withdrawal.path(),
format!("m/12381/3600/{}/0", i),
"withdrawal path should match"
);
let voting_keypair = keystores
.voting
.decrypt_keypair(VOTING_KEYSTORE_PASSWORD)
.expect("should decrypt voting keypair");
assert_eq!(
voting_keypair.sk.as_bytes().as_ref(),
&manually_derived_voting_key(i)[..],
EIP-2386 (draft): Eth2 wallet (#1117) * Add test to understand flow of key storage * First commit * Committing to save trait stuff * Working naive design * Add keystore struct * Move keystore files into their own module * Add serde (de)serialize_with magic * Add keystore test * Fix tests * Add comments and minor fixes * Pass optional params to `to_keystore` function * Add `path` field to keystore * Add function to read Keystore from file * Add test vectors and fix Version serialization * Checksum params is empty object * Add public key to Keystore * Add function for saving keystore into file * Deleted account_manager main.rs * Move keystore module to validator_client * Add save_keystore method to validator_directory * Add load_keystore function. Minor refactorings * Fixed dependencies * Address some review comments * Add Password newtype; derive Zeroize * Fix test * Move keystore into own crate * Remove padding * Add error enum, zeroize more things * Fix comment * Add keystore builder * Remove keystore stuff from val client * Add more tests, comments * Add more comments, test vectors * Progress on improving JSON validation * More JSON verification * Start moving JSON into own mod * Remove old code * Add more tests, reader/writers * Tidy * Move keystore into own file * Move more logic into keystore file * Tidy * Tidy * Allow for odd-character hex * Add more json missing field checks * Use scrypt by default * Tidy, address comments * Test path and uuid in vectors * Fix comment * Add checks for kdf params * Enforce empty kdf message * Expose json_keystore mod * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * First commits on path derivation * Progress with implementation * Move key derivation into own crate * Start defining JSON wallet * Add progress * Split out encrypt/decrypt * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Replace some password usage with slice * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Expose PlainText struct * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add builder * Expose consts, remove Password * Minor progress * Expose SALT_SIZE * First compiling version * Add test vectors * Move dbg assert statement * Add mnemonic, tidy * Tidy * Add testing * Fix broken test * Address review comments Co-authored-by: pawan <pawandhananjay@gmail.com>
2020-05-11 22:54:59 +00:00
"voting secret should match manually derived"
);
assert_eq!(
voting_keypair.sk.as_bytes().as_ref(),
&recovered_voting_key(&wallet, i)[..],
EIP-2386 (draft): Eth2 wallet (#1117) * Add test to understand flow of key storage * First commit * Committing to save trait stuff * Working naive design * Add keystore struct * Move keystore files into their own module * Add serde (de)serialize_with magic * Add keystore test * Fix tests * Add comments and minor fixes * Pass optional params to `to_keystore` function * Add `path` field to keystore * Add function to read Keystore from file * Add test vectors and fix Version serialization * Checksum params is empty object * Add public key to Keystore * Add function for saving keystore into file * Deleted account_manager main.rs * Move keystore module to validator_client * Add save_keystore method to validator_directory * Add load_keystore function. Minor refactorings * Fixed dependencies * Address some review comments * Add Password newtype; derive Zeroize * Fix test * Move keystore into own crate * Remove padding * Add error enum, zeroize more things * Fix comment * Add keystore builder * Remove keystore stuff from val client * Add more tests, comments * Add more comments, test vectors * Progress on improving JSON validation * More JSON verification * Start moving JSON into own mod * Remove old code * Add more tests, reader/writers * Tidy * Move keystore into own file * Move more logic into keystore file * Tidy * Tidy * Allow for odd-character hex * Add more json missing field checks * Use scrypt by default * Tidy, address comments * Test path and uuid in vectors * Fix comment * Add checks for kdf params * Enforce empty kdf message * Expose json_keystore mod * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * First commits on path derivation * Progress with implementation * Move key derivation into own crate * Start defining JSON wallet * Add progress * Split out encrypt/decrypt * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Replace some password usage with slice * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Expose PlainText struct * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add builder * Expose consts, remove Password * Minor progress * Expose SALT_SIZE * First compiling version * Add test vectors * Move dbg assert statement * Add mnemonic, tidy * Tidy * Add testing * Fix broken test * Address review comments Co-authored-by: pawan <pawandhananjay@gmail.com>
2020-05-11 22:54:59 +00:00
"voting secret should match recovered"
);
let withdrawal_keypair = keystores
.withdrawal
.decrypt_keypair(WITHDRAWAL_KEYSTORE_PASSWORD)
.expect("should decrypt withdrawal keypair");
assert_eq!(
withdrawal_keypair.sk.as_bytes().as_ref(),
&manually_derived_withdrawal_key(i)[..],
EIP-2386 (draft): Eth2 wallet (#1117) * Add test to understand flow of key storage * First commit * Committing to save trait stuff * Working naive design * Add keystore struct * Move keystore files into their own module * Add serde (de)serialize_with magic * Add keystore test * Fix tests * Add comments and minor fixes * Pass optional params to `to_keystore` function * Add `path` field to keystore * Add function to read Keystore from file * Add test vectors and fix Version serialization * Checksum params is empty object * Add public key to Keystore * Add function for saving keystore into file * Deleted account_manager main.rs * Move keystore module to validator_client * Add save_keystore method to validator_directory * Add load_keystore function. Minor refactorings * Fixed dependencies * Address some review comments * Add Password newtype; derive Zeroize * Fix test * Move keystore into own crate * Remove padding * Add error enum, zeroize more things * Fix comment * Add keystore builder * Remove keystore stuff from val client * Add more tests, comments * Add more comments, test vectors * Progress on improving JSON validation * More JSON verification * Start moving JSON into own mod * Remove old code * Add more tests, reader/writers * Tidy * Move keystore into own file * Move more logic into keystore file * Tidy * Tidy * Allow for odd-character hex * Add more json missing field checks * Use scrypt by default * Tidy, address comments * Test path and uuid in vectors * Fix comment * Add checks for kdf params * Enforce empty kdf message * Expose json_keystore mod * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * First commits on path derivation * Progress with implementation * Move key derivation into own crate * Start defining JSON wallet * Add progress * Split out encrypt/decrypt * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Replace some password usage with slice * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Expose PlainText struct * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add builder * Expose consts, remove Password * Minor progress * Expose SALT_SIZE * First compiling version * Add test vectors * Move dbg assert statement * Add mnemonic, tidy * Tidy * Add testing * Fix broken test * Address review comments Co-authored-by: pawan <pawandhananjay@gmail.com>
2020-05-11 22:54:59 +00:00
"withdrawal secret should match manually derived"
);
assert_eq!(
withdrawal_keypair.sk.as_bytes().as_ref(),
&recovered_withdrawal_key(&wallet, i)[..],
EIP-2386 (draft): Eth2 wallet (#1117) * Add test to understand flow of key storage * First commit * Committing to save trait stuff * Working naive design * Add keystore struct * Move keystore files into their own module * Add serde (de)serialize_with magic * Add keystore test * Fix tests * Add comments and minor fixes * Pass optional params to `to_keystore` function * Add `path` field to keystore * Add function to read Keystore from file * Add test vectors and fix Version serialization * Checksum params is empty object * Add public key to Keystore * Add function for saving keystore into file * Deleted account_manager main.rs * Move keystore module to validator_client * Add save_keystore method to validator_directory * Add load_keystore function. Minor refactorings * Fixed dependencies * Address some review comments * Add Password newtype; derive Zeroize * Fix test * Move keystore into own crate * Remove padding * Add error enum, zeroize more things * Fix comment * Add keystore builder * Remove keystore stuff from val client * Add more tests, comments * Add more comments, test vectors * Progress on improving JSON validation * More JSON verification * Start moving JSON into own mod * Remove old code * Add more tests, reader/writers * Tidy * Move keystore into own file * Move more logic into keystore file * Tidy * Tidy * Allow for odd-character hex * Add more json missing field checks * Use scrypt by default * Tidy, address comments * Test path and uuid in vectors * Fix comment * Add checks for kdf params * Enforce empty kdf message * Expose json_keystore mod * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * First commits on path derivation * Progress with implementation * Move key derivation into own crate * Start defining JSON wallet * Add progress * Split out encrypt/decrypt * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Replace some password usage with slice * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Expose PlainText struct * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add builder * Expose consts, remove Password * Minor progress * Expose SALT_SIZE * First compiling version * Add test vectors * Move dbg assert statement * Add mnemonic, tidy * Tidy * Add testing * Fix broken test * Address review comments Co-authored-by: pawan <pawandhananjay@gmail.com>
2020-05-11 22:54:59 +00:00
"withdrawal secret should match recovered"
);
assert_ne!(
withdrawal_keypair.sk.as_bytes().as_ref(),
voting_keypair.sk.as_bytes().as_bytes(),
EIP-2386 (draft): Eth2 wallet (#1117) * Add test to understand flow of key storage * First commit * Committing to save trait stuff * Working naive design * Add keystore struct * Move keystore files into their own module * Add serde (de)serialize_with magic * Add keystore test * Fix tests * Add comments and minor fixes * Pass optional params to `to_keystore` function * Add `path` field to keystore * Add function to read Keystore from file * Add test vectors and fix Version serialization * Checksum params is empty object * Add public key to Keystore * Add function for saving keystore into file * Deleted account_manager main.rs * Move keystore module to validator_client * Add save_keystore method to validator_directory * Add load_keystore function. Minor refactorings * Fixed dependencies * Address some review comments * Add Password newtype; derive Zeroize * Fix test * Move keystore into own crate * Remove padding * Add error enum, zeroize more things * Fix comment * Add keystore builder * Remove keystore stuff from val client * Add more tests, comments * Add more comments, test vectors * Progress on improving JSON validation * More JSON verification * Start moving JSON into own mod * Remove old code * Add more tests, reader/writers * Tidy * Move keystore into own file * Move more logic into keystore file * Tidy * Tidy * Allow for odd-character hex * Add more json missing field checks * Use scrypt by default * Tidy, address comments * Test path and uuid in vectors * Fix comment * Add checks for kdf params * Enforce empty kdf message * Expose json_keystore mod * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * First commits on path derivation * Progress with implementation * Move key derivation into own crate * Start defining JSON wallet * Add progress * Split out encrypt/decrypt * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Replace some password usage with slice * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add progress * Expose PlainText struct * First commits on path derivation * Progress with implementation * More progress * Passing intermediate test vectors * Tidy, add comments * Add DerivedKey structs * Move key derivation into own crate * Add zeroize structs * Return error for empty seed * Add tests * Tidy * Add builder * Expose consts, remove Password * Minor progress * Expose SALT_SIZE * First compiling version * Add test vectors * Move dbg assert statement * Add mnemonic, tidy * Tidy * Add testing * Fix broken test * Address review comments Co-authored-by: pawan <pawandhananjay@gmail.com>
2020-05-11 22:54:59 +00:00
"voting and withdrawal keypairs should be distinct"
);
assert_eq!(wallet.nextaccount(), i + 1, "updated nextaccount");
}
}