mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-16 12:28:24 +00:00
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>
This commit is contained in:
60
eth2/utils/eth2_wallet/tests/eip2386_vectors.rs
Normal file
60
eth2/utils/eth2_wallet/tests/eip2386_vectors.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use eth2_keystore::Uuid;
|
||||
use eth2_wallet::Wallet;
|
||||
|
||||
const EXPECTED_SECRET: &str = "147addc7ec981eb2715a22603813271cce540e0b7f577126011eb06249d9227c";
|
||||
const PASSWORD: &str = "testpassword";
|
||||
|
||||
pub fn decode_and_check_seed(json: &str) -> Wallet {
|
||||
let wallet = Wallet::from_json_str(json).expect("should decode keystore json");
|
||||
let expected_sk = hex::decode(EXPECTED_SECRET).unwrap();
|
||||
let seed = wallet.decrypt_seed(PASSWORD.as_bytes()).unwrap();
|
||||
assert_eq!(seed.as_bytes(), &expected_sk[..]);
|
||||
wallet
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eip2386_test_vector_scrypt() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 0,
|
||||
"type": "hierarchical deterministic",
|
||||
"uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5",
|
||||
"version": 1
|
||||
}
|
||||
"#;
|
||||
|
||||
let wallet = decode_and_check_seed(&vector);
|
||||
assert_eq!(
|
||||
*wallet.uuid(),
|
||||
Uuid::parse_str("b74559b8-ed56-4841-b25c-dba1b7c9d9d5").unwrap(),
|
||||
"uuid"
|
||||
);
|
||||
assert_eq!(wallet.name(), "Test wallet 2", "name");
|
||||
assert_eq!(wallet.nextaccount(), 0, "nextaccount");
|
||||
assert_eq!(wallet.type_field(), "hierarchical deterministic", "type");
|
||||
}
|
||||
246
eth2/utils/eth2_wallet/tests/json.rs
Normal file
246
eth2/utils/eth2_wallet/tests/json.rs
Normal file
@@ -0,0 +1,246 @@
|
||||
use eth2_wallet::{Error, KeystoreError, Wallet};
|
||||
|
||||
fn assert_bad_json(json: &str) {
|
||||
match Wallet::from_json_str(&json) {
|
||||
Err(Error::KeystoreError(KeystoreError::InvalidJson(_))) => {}
|
||||
_ => panic!("expected invalid json error"),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: the `crypto` object is inherited from the `eth2_keystore` crate so we don't test it here.
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn additional_top_level_param() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 0,
|
||||
"type": "hierarchical deterministic",
|
||||
"uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5",
|
||||
"version": 1,
|
||||
"cats": 42
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_bad_json(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_top_level_param() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 0,
|
||||
"type": "hierarchical deterministic",
|
||||
"uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5"
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_bad_json(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_version() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 0,
|
||||
"type": "hierarchical deterministic",
|
||||
"uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5",
|
||||
"version": 2
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_bad_json(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_uuid() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 0,
|
||||
"type": "hierarchical deterministic",
|
||||
"uuid": "!b74559b8-ed56-4841-b25c-dba1b7c9d9d5",
|
||||
"version": 1
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_bad_json(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_type() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 0,
|
||||
"type": "something else",
|
||||
"uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5",
|
||||
"version": 1
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_bad_json(&vector);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn more_that_u32_nextaccount() {
|
||||
let vector = r#"
|
||||
{
|
||||
"crypto": {
|
||||
"checksum": {
|
||||
"function": "sha256",
|
||||
"message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021",
|
||||
"params": {}
|
||||
},
|
||||
"cipher": {
|
||||
"function": "aes-128-ctr",
|
||||
"message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a",
|
||||
"params": {
|
||||
"iv": "9476702ab99beff3e8012eff49ffb60d"
|
||||
}
|
||||
},
|
||||
"kdf": {
|
||||
"function": "pbkdf2",
|
||||
"message": "",
|
||||
"params": {
|
||||
"c": 16,
|
||||
"dklen": 32,
|
||||
"prf": "hmac-sha256",
|
||||
"salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Test wallet 2",
|
||||
"nextaccount": 4294967297,
|
||||
"type": "hierarchical deterministic",
|
||||
"uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5",
|
||||
"version": 1
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_bad_json(&vector);
|
||||
}
|
||||
280
eth2/utils/eth2_wallet/tests/tests.rs
Normal file
280
eth2/utils/eth2_wallet/tests/tests.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
use eth2_wallet::{
|
||||
bip39::{Language, Mnemonic, Seed},
|
||||
recover_validator_secret, DerivedKey, Error, KeyType, KeystoreError, Wallet, WalletBuilder,
|
||||
};
|
||||
use ssz::Encode;
|
||||
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_ssz_bytes(),
|
||||
manually_derived_voting_key(i),
|
||||
"voting secret should match manually derived"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
voting_keypair.sk.as_ssz_bytes(),
|
||||
recovered_voting_key(&wallet, i),
|
||||
"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_ssz_bytes(),
|
||||
manually_derived_withdrawal_key(i),
|
||||
"withdrawal secret should match manually derived"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
withdrawal_keypair.sk.as_ssz_bytes(),
|
||||
recovered_withdrawal_key(&wallet, i),
|
||||
"withdrawal secret should match recovered"
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
withdrawal_keypair.sk.as_ssz_bytes(),
|
||||
voting_keypair.sk.as_ssz_bytes(),
|
||||
"voting and withdrawal keypairs should be distinct"
|
||||
);
|
||||
|
||||
assert_eq!(wallet.nextaccount(), i + 1, "updated nextaccount");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user