From e942d7533be92f82f9ba4f76a30fa372e58db0d9 Mon Sep 17 00:00:00 2001 From: Luke Anderson Date: Tue, 12 Mar 2019 21:56:45 +1100 Subject: [PATCH] A first go at persisting validator keys and handling configuration. Addresses issue #253. - Creates a keystore directory in the config - Fetches serialized keys from the keystore directory - If no keys, generates keys randomly, saves serialized keys to keystore dir. --- eth2/utils/bls/src/keypair.rs | 4 +++ validator_client/Cargo.toml | 1 + validator_client/src/config.rs | 47 +++++++++++++++++++++++++++++++--- validator_client/src/main.rs | 30 ++++++++++++++++++---- 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/eth2/utils/bls/src/keypair.rs b/eth2/utils/bls/src/keypair.rs index d60a2fc253..6feb2a5856 100644 --- a/eth2/utils/bls/src/keypair.rs +++ b/eth2/utils/bls/src/keypair.rs @@ -14,4 +14,8 @@ impl Keypair { let pk = PublicKey::from_secret_key(&sk); Keypair { sk, pk } } + + pub fn identifier(&self) -> String { + self.pk.concatenated_hex_id() + } } diff --git a/validator_client/Cargo.toml b/validator_client/Cargo.toml index f76772f28f..e9d66f6d34 100644 --- a/validator_client/Cargo.toml +++ b/validator_client/Cargo.toml @@ -18,3 +18,4 @@ slog = "^2.2.3" slog-term = "^2.4.0" slog-async = "^2.3.0" ssz = { path = "../eth2/utils/ssz" } +bincode = "^1.1.2" diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index 68405ed2f5..e400f228c2 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -1,4 +1,7 @@ +use bls::Keypair; use std::fs; +use std::fs::File; +use std::io::{Error, ErrorKind}; use std::path::PathBuf; use types::ChainSpec; @@ -6,27 +9,63 @@ use types::ChainSpec; #[derive(Clone)] pub struct ClientConfig { pub data_dir: PathBuf, + pub key_dir: PathBuf, pub server: String, pub spec: ChainSpec, } const DEFAULT_LIGHTHOUSE_DIR: &str = ".lighthouse-validators"; +const DEFAULT_KEYSTORE_SUBDIR: &str = "keystore"; impl ClientConfig { /// Build a new configuration from defaults. - pub fn default() -> Self { + pub fn default() -> Result { let data_dir = { let home = dirs::home_dir().expect("Unable to determine home dir."); home.join(DEFAULT_LIGHTHOUSE_DIR) }; - fs::create_dir_all(&data_dir) - .unwrap_or_else(|_| panic!("Unable to create {:?}", &data_dir)); + fs::create_dir_all(&data_dir)?; + + let key_dir = data_dir.join(DEFAULT_KEYSTORE_SUBDIR); + fs::create_dir_all(&key_dir)?; + let server = "localhost:50051".to_string(); let spec = ChainSpec::foundation(); - Self { + Ok(Self { data_dir, + key_dir, server, spec, + }) + } + + // Try to load keys from datadir, or fail + pub fn fetch_keys(&self) -> Result>, Error> { + let mut key_files = fs::read_dir(&self.key_dir)?.peekable(); + + if key_files.peek().is_none() { + return Ok(None); } + + let mut key_pairs: Vec = Vec::new(); + + for key_filename in key_files { + let mut key_file = File::open(key_filename?.path())?; + + let key: Keypair = bincode::deserialize_from(&mut key_file) + .map_err(|e| Error::new(ErrorKind::InvalidData, e))?; + + key_pairs.push(key); + } + + Ok(Some(key_pairs)) + } + + pub fn save_key(&self, key: &Keypair) -> Result<(), Error> { + let key_path = self.key_dir.join(key.identifier() + ".key"); + let mut key_file = File::create(&key_path)?; + bincode::serialize_into(&mut key_file, &key) + .map_err(|e| Error::new(ErrorKind::InvalidData, e))?; + Ok(()) } } diff --git a/validator_client/src/main.rs b/validator_client/src/main.rs index ebab8538c5..2f2903707d 100644 --- a/validator_client/src/main.rs +++ b/validator_client/src/main.rs @@ -6,7 +6,7 @@ use bls::Keypair; use clap::{App, Arg}; use grpcio::{ChannelBuilder, EnvBuilder}; use protos::services_grpc::{BeaconBlockServiceClient, ValidatorServiceClient}; -use slog::{error, info, o, Drain}; +use slog::{debug, error, info, o, Drain}; use slot_clock::SystemTimeSlotClock; use std::path::PathBuf; use std::sync::Arc; @@ -17,6 +17,8 @@ mod block_producer_service; mod config; mod duties; +const NUMBER_OF_VALIDATOR_TEST_KEYS: u16 = 3; + fn main() { // Logging let decorator = slog_term::TermDecorator::new().build(); @@ -55,7 +57,7 @@ fn main() { ) .get_matches(); - let mut config = ClientConfig::default(); + let mut config = ClientConfig::default().expect("Unable to create a default configuration."); // Custom datadir if let Some(dir) = matches.value_of("datadir") { @@ -123,9 +125,27 @@ fn main() { * Start threads. */ let mut threads = vec![]; - // TODO: keypairs are randomly generated; they should be loaded from a file or generated. - // https://github.com/sigp/lighthouse/issues/160 - let keypairs = vec![Keypair::random()]; + + let keypairs = config + .fetch_keys() + .expect("Encountered an error while fetching saved keys.") + .unwrap_or_else(|| { + // TODO: Key generation should occur in a separate binary + let mut k = Vec::new(); + info!( + log, + "No key pairs found, generating and saving 3 random key pairs." + ); + for _n in 0..NUMBER_OF_VALIDATOR_TEST_KEYS { + let keypair = Keypair::random(); + config + .save_key(&keypair) + .expect("Unable to save newly generated private key."); + debug!(log, "Keypair generated {:?}", keypair.identifier()); + k.push(keypair); + } + k + }); for keypair in keypairs { info!(log, "Starting validator services"; "validator" => keypair.pk.concatenated_hex_id());