From 177a3514627a90284833544258686d0e8d6a461f Mon Sep 17 00:00:00 2001 From: Luke Anderson Date: Mon, 8 Apr 2019 15:02:11 +1000 Subject: [PATCH] Added deterministic keypair generation. - The Account Manager has a new subcommand, allowing generation of deterministic keys given a particular validator index. - Split functionality in generate_deterministic_keypair function - Fixed up READMEs to reflect new functionality & correct naming. --- account_manager/Cargo.toml | 1 + account_manager/README.md | 16 +++++++-- account_manager/src/main.rs | 35 ++++++++++++++++++- .../generate_deterministic_keypairs.rs | 18 ++++++---- eth2/types/src/test_utils/mod.rs | 1 + validator_client/README.md | 3 +- validator_client/src/main.rs | 2 +- validator_client/src/service.rs | 8 +++-- 8 files changed, 69 insertions(+), 15 deletions(-) diff --git a/account_manager/Cargo.toml b/account_manager/Cargo.toml index c26d4b70a..7b561869a 100644 --- a/account_manager/Cargo.toml +++ b/account_manager/Cargo.toml @@ -11,3 +11,4 @@ slog = "^2.2.3" slog-term = "^2.4.0" slog-async = "^2.3.0" validator_client = { path = "../validator_client" } +types = { path = "../eth2/types" } diff --git a/account_manager/README.md b/account_manager/README.md index bf8891f40..6762b937f 100644 --- a/account_manager/README.md +++ b/account_manager/README.md @@ -1,6 +1,6 @@ -# Lighthouse Accounts Manager +# Lighthouse Account Manager -The accounts manager (AM) is a stand-alone binary which allows +The account manager (AM) is a stand-alone binary which allows users to generate and manage the cryptographic keys necessary to interact with Ethereum Serenity. @@ -21,4 +21,14 @@ staking on Ethereum 1.x (TPD) The AM is not a service, and does not run continuously, nor does it interact with any running services. It is intended to be executed separately from other Lighthouse binaries -and produce files which can be consumed by them. \ No newline at end of file +and produce files which can be consumed by them.& + +## Usage + +Simply run `./account_manager generate` to generate a new random private key, +which will be automatically saved to the correct directory. + +If you prefer to use our "deterministic" keys for testing purposes, simply +run `./accounts_manager generate_deterministic -i `, where `index` is +the validator index for the key. This will reliably produce the same key each time +and save it to the directory. \ No newline at end of file diff --git a/account_manager/src/main.rs b/account_manager/src/main.rs index 42c78aaea..869a2411e 100644 --- a/account_manager/src/main.rs +++ b/account_manager/src/main.rs @@ -3,6 +3,7 @@ use clap::{App, Arg, SubCommand}; use slog::{debug, info, o, Drain}; use std::path::PathBuf; use validator_client::Config as ValidatorClientConfig; +use types::test_utils::generate_deterministic_keypair; fn main() { // Logging @@ -29,6 +30,21 @@ fn main() { .version("0.0.1") .author("Sigma Prime "), ) + .subcommand( + SubCommand::with_name("generate_deterministic") + .about("Generates a deterministic validator private key FOR TESTING") + .version("0.0.1") + .author("Sigma Prime ") + .arg( + Arg::with_name("validator index") + .long("index") + .short("i") + .value_name("index") + .help("The index of the validator, for which the test key is generated") + .takes_value(true) + .required(true) + ) + ) .get_matches(); let config = ValidatorClientConfig::parse_args(&matches, &log) @@ -50,7 +66,24 @@ fn main() { keypair.identifier(), key_path.to_string_lossy() ); - } + }, + ("generate_deterministic", Some(gen_d_matches)) => { + let validator_index = gen_d_matches + .value_of("validator index") + .expect("Validator index required.") + .parse::() + .expect("Invalid validator index.") as usize; + let keypair = generate_deterministic_keypair(validator_index); + let key_path: PathBuf = config + .save_key(&keypair) + .expect("Unable to save newly generated deterministic private key."); + debug!( + log, + "Deterministic Keypair generated {:?}, saved to: {:?}", + keypair.identifier(), + key_path.to_string_lossy() + ); + }, _ => panic!( "The account manager must be run with a subcommand. See help for more information." ), diff --git a/eth2/types/src/test_utils/generate_deterministic_keypairs.rs b/eth2/types/src/test_utils/generate_deterministic_keypairs.rs index 37880a988..ec1c15f29 100644 --- a/eth2/types/src/test_utils/generate_deterministic_keypairs.rs +++ b/eth2/types/src/test_utils/generate_deterministic_keypairs.rs @@ -18,13 +18,17 @@ pub fn generate_deterministic_keypairs(validator_count: usize) -> Vec { let keypairs: Vec = (0..validator_count) .collect::>() .par_iter() - .map(|&i| { - let secret = int_to_bytes48(i as u64 + 1000); - let sk = SecretKey::from_bytes(&secret).unwrap(); - let pk = PublicKey::from_secret_key(&sk); - Keypair { sk, pk } - }) + .map(|&i| generate_deterministic_keypair(i)) .collect(); - keypairs } + +/// Generates a single deterministic keypair, where the secret key is `validator_index`. +/// +/// This is used for testing only, and not to be used in production! +pub fn generate_deterministic_keypair(validator_index: usize) -> Keypair { + let secret = int_to_bytes48(validator_index as u64 + 1000); + let sk = SecretKey::from_bytes(&secret).unwrap(); + let pk = PublicKey::from_secret_key(&sk); + Keypair { sk, pk } +} diff --git a/eth2/types/src/test_utils/mod.rs b/eth2/types/src/test_utils/mod.rs index 018b70d15..98ef14c66 100644 --- a/eth2/types/src/test_utils/mod.rs +++ b/eth2/types/src/test_utils/mod.rs @@ -16,6 +16,7 @@ mod testing_transfer_builder; mod testing_voluntary_exit_builder; pub use generate_deterministic_keypairs::generate_deterministic_keypairs; +pub use generate_deterministic_keypairs::generate_deterministic_keypair; pub use keypairs_file::KeypairsFile; pub use rand::{prng::XorShiftRng, SeedableRng}; pub use serde_utils::{fork_from_hex_str, u8_from_hex_str}; diff --git a/validator_client/README.md b/validator_client/README.md index 03979fbb8..e6a01007a 100644 --- a/validator_client/README.md +++ b/validator_client/README.md @@ -75,8 +75,9 @@ The configuration directory structure looks like: Where the hex value of the directory is a portion of the validator public key. -Validator keys must be generated using the separate `accounts_manager` binary, which will +Validator keys must be generated using the separate `account_manager` binary, which will place the keys into this directory structure in a format compatible with the validator client. +Be sure to check the readme for `account_manager`. The chain specification (slot length, BLS domain, etc.) defaults to foundation parameters, however is temporary and an upgrade will allow these parameters to be diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index 7a353e0dc..6ddcfa819 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -9,7 +9,7 @@ mod signer; use crate::config::Config as ValidatorClientConfig; use clap::{App, Arg}; use protos::services_grpc::ValidatorServiceClient; -use service::Service as ValidatorService; +use crate::service::Service as ValidatorService; use slog::{error, info, o, Drain}; use types::Keypair; diff --git a/validator_client/src/service.rs b/validator_client/src/service.rs index a8a8325dd..fb5f32778 100644 --- a/validator_client/src/service.rs +++ b/validator_client/src/service.rs @@ -166,11 +166,15 @@ impl Service { /* Generate the duties manager */ - // generate keypairs + // Load generated keypairs + let keypairs = match config.fetch_keys(&log) { + Some(kps) => Arc::new(kps), + None => panic!("No key pairs found, cannot start validator client without at least one. Try running `./account_manager generate` first.") + }; // TODO: keypairs are randomly generated; they should be loaded from a file or generated. // https://github.com/sigp/lighthouse/issues/160 - let keypairs = Arc::new(generate_deterministic_keypairs(8)); + //let keypairs = Arc::new(generate_deterministic_keypairs(8)); // Builds a mapping of Epoch -> Map(PublicKey, EpochDuty) // where EpochDuty contains slot numbers and attestation data that each validator needs to