Moved configuration around for validator client.

- Custom datadir/server argument logic moved into configuration, out of main.
 - Updated the validator config directory structure, as per issue #253 suggestions
 - Removed the 'generate 3 random keys' function
 - Updated the README to reflect new structure
 - Just exit if there are no keys, don't generate any (this is for accounts_manager, in a separate commit).
 - Created a lib.rs file, so that the validator client configuration can be included by external crates.
This commit is contained in:
Luke Anderson 2019-03-20 16:23:33 +11:00
parent b000a0972e
commit 49f6e7ac65
No known key found for this signature in database
GPG Key ID: 44408169EC61E228
5 changed files with 136 additions and 84 deletions

View File

@ -4,6 +4,15 @@ version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[[bin]]
name = "validator_client"
path = "src/main.rs"
[lib]
name = "validator_client"
path = "src/lib.rs"
[dependencies]
block_proposer = { path = "../eth2/block_proposer" }
bls = { path = "../eth2/utils/bls" }

View File

@ -57,10 +57,31 @@ complete and return a block from the BN.
### Configuration
Presently the validator specifics (pubkey, etc.) are randomly generated and the
chain specification (slot length, BLS domain, etc.) are fixed to foundation
parameters. This is temporary and will be upgrade so these parameters can be
read from file (or initialized on first-boot).
Validator configurations are stored in a separate data directory from the main Beacon Node
binary. The validator data directory defaults to:
`$HOME/.lighthouse-validator`, however an alternative can be specified on the command line
with `--datadir`.
The configuration directory structure looks like:
```
~/.lighthouse-validator
└── validators
├── 3cf4210d58ec
│   └── private.key
├── 9b5d8b5be4e7
│   └── private.key
└── cf6e07188f48
└── private.key
```
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
place the keys into this directory structure in a format compatible with the validator client.
The chain specification (slot length, BLS domain, etc.) defaults to foundation
parameters, however is temporary and an upgrade will allow these parameters to be
read from a file (or initialized on first-boot).
## BN Communication

View File

@ -1,72 +1,131 @@
use bls::Keypair;
use clap::ArgMatches;
use std::fs;
use std::fs::File;
use std::io::{Error, ErrorKind};
use std::path::PathBuf;
use types::ChainSpec;
use bincode;
/// Stores the core configuration for this validator instance.
#[derive(Clone)]
pub struct ClientConfig {
pub struct ValidatorClientConfig {
/// The data directory, which stores all validator databases
pub data_dir: PathBuf,
pub key_dir: PathBuf,
/// The directory where the individual validator configuration directories are stored.
pub validator_dir: PathBuf,
/// The server at which the Beacon Node can be contacted
pub server: String,
/// The chain specification that we are connecting to
pub spec: ChainSpec,
}
const DEFAULT_LIGHTHOUSE_DIR: &str = ".lighthouse-validators";
const DEFAULT_KEYSTORE_SUBDIR: &str = "keystore";
const DEFAULT_VALIDATOR_DATADIR: &str = ".lighthouse-validator";
const DEFAULT_VALIDATORS_SUBDIR: &str = "validators";
const DEFAULT_PRIVATE_KEY_FILENAME: &str = "private.key";
impl ClientConfig {
/// Build a new configuration from defaults.
pub fn default() -> Result<Self, Error> {
let data_dir = {
let home = dirs::home_dir()
.ok_or(Error::new(ErrorKind::NotFound, "Unable to determine home directory."))?;
home.join(DEFAULT_LIGHTHOUSE_DIR)
impl ValidatorClientConfig {
/// Build a new configuration from defaults, which are overrided by arguments provided.
pub fn build_config(arguments: &ArgMatches) -> Result<Self, Error> {
// Use the specified datadir, or default in the home directory
let data_dir: PathBuf = match arguments.value_of("datadir") {
Some(path) => PathBuf::from(path.to_string()),
None => {
let home = dirs::home_dir().ok_or_else(|| Error::new(
ErrorKind::NotFound,
"Unable to determine home directory.",
))?;
home.join(DEFAULT_VALIDATOR_DATADIR)
}
};
fs::create_dir_all(&data_dir)?;
let key_dir = data_dir.join(DEFAULT_KEYSTORE_SUBDIR);
fs::create_dir_all(&key_dir)?;
let validator_dir = data_dir.join(DEFAULT_VALIDATORS_SUBDIR);
fs::create_dir_all(&validator_dir)?;
let server: String = match arguments.value_of("server") {
Some(srv) => {
//TODO: I don't think this parses correctly a server & port combo
srv.parse::<u16>()
.map_err(|e| Error::new(ErrorKind::InvalidInput, e))?
.to_string()
}
None => "localhost:50051".to_string(),
};
// TODO: Permit loading a custom spec from file.
let spec: ChainSpec = match arguments.value_of("spec") {
Some(spec_str) => {
match spec_str {
"foundation" => ChainSpec::foundation(),
"few_validators" => ChainSpec::few_validators(),
// Should be impossible due to clap's `possible_values(..)` function.
_ => unreachable!(),
}
}
None => ChainSpec::foundation(),
};
let server = "localhost:50051".to_string();
let spec = ChainSpec::foundation();
Ok(Self {
data_dir,
key_dir,
validator_dir,
server,
spec,
})
}
/// Try to load keys from key_dir, returning None if none are found or an error.
/// Try to load keys from validator_dir, returning None if none are found or an error.
pub fn fetch_keys(&self) -> Result<Option<Vec<Keypair>>, Error> {
let mut key_files = fs::read_dir(&self.key_dir)?.peekable();
let mut validator_dirs = fs::read_dir(&self.validator_dir)?.peekable();
if key_files.peek().is_none() {
// There are no validator directories.
if validator_dirs.peek().is_none() {
return Ok(None);
}
let mut key_pairs: Vec<Keypair> = Vec::new();
for key_filename in key_files {
let mut key_file = File::open(key_filename?.path())?;
for validator_dir_result in validator_dirs {
let validator_dir = validator_dir_result?;
// Try to open the key file directly
// TODO skip keyfiles that are not found, and log the error instead of returning it.
let mut key_file = File::open(validator_dir.path().join(DEFAULT_PRIVATE_KEY_FILENAME))?;
let key: Keypair = bincode::deserialize_from(&mut key_file)
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
// TODO skip keyfile if it's not matched, and log the error instead of returning it.
let validator_directory_name = validator_dir.file_name().into_string().map_err(|_| {
Error::new(
ErrorKind::InvalidData,
"The filename cannot be parsed to a string.",
)
})?;
if key.identifier() != validator_directory_name {
return Err(Error::new(
ErrorKind::InvalidData,
"The validator directory ID did not match the key found inside.",
));
}
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");
/// Saves a keypair to a file inside the appropriate validator directory. Returns the saved path filename.
pub fn save_key(&self, key: &Keypair) -> Result<PathBuf, Error> {
let validator_config_path = self.validator_dir.join(key.identifier());
let key_path = validator_config_path.join(DEFAULT_PRIVATE_KEY_FILENAME);
fs::create_dir_all(&validator_config_path)?;
let mut key_file = File::create(&key_path)?;
bincode::serialize_into(&mut key_file, &key)
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
Ok(())
Ok(key_path)
}
}

View File

@ -0,0 +1,3 @@
pub mod config;
pub use crate::config::ValidatorClientConfig;

View File

@ -1,24 +1,19 @@
use self::block_producer_service::{BeaconBlockGrpcClient, BlockProducerService};
use self::duties::{DutiesManager, DutiesManagerService, EpochDutiesMap};
use crate::config::ClientConfig;
use crate::config::ValidatorClientConfig;
use block_proposer::{test_utils::LocalSigner, BlockProducer};
use bls::Keypair;
use clap::{App, Arg};
use grpcio::{ChannelBuilder, EnvBuilder};
use protos::services_grpc::{BeaconBlockServiceClient, ValidatorServiceClient};
use slog::{debug, error, info, o, Drain};
use slog::{error, info, o, Drain};
use slot_clock::SystemTimeSlotClock;
use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use types::ChainSpec;
use std::{thread, process, time};
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();
@ -57,36 +52,11 @@ fn main() {
)
.get_matches();
let mut config = ClientConfig::default().expect("Unable to create a default configuration.");
// Custom datadir
if let Some(dir) = matches.value_of("datadir") {
config.data_dir = PathBuf::from(dir.to_string());
}
// Custom server port
if let Some(server_str) = matches.value_of("server") {
if let Ok(addr) = server_str.parse::<u16>() {
config.server = addr.to_string();
} else {
error!(log, "Invalid address"; "server" => server_str);
return;
}
}
// TODO: Permit loading a custom spec from file.
// Custom spec
if let Some(spec_str) = matches.value_of("spec") {
match spec_str {
"foundation" => config.spec = ChainSpec::foundation(),
"few_validators" => config.spec = ChainSpec::few_validators(),
// Should be impossible due to clap's `possible_values(..)` function.
_ => unreachable!(),
};
}
let config = ValidatorClientConfig::build_config(&matches)
.expect("Unable to build a configuration for the validator client.");
// Log configuration
info!(log, "";
info!(log, "Configuration parameters:";
"data_dir" => &config.data_dir.to_str(),
"server" => &config.server);
@ -121,31 +91,21 @@ fn main() {
let poll_interval_millis = spec.seconds_per_slot * 1000 / 10; // 10% epoch time precision.
info!(log, "Starting block producer service"; "polls_per_epoch" => spec.seconds_per_slot * 1000 / poll_interval_millis);
let keypairs = config
.fetch_keys()
.expect("Encountered an error while fetching saved keys.")
.unwrap_or_else(|| {
error!(log, "No key pairs found in configuration, they must first be generated with: account_manager generate.");
// give the logger a chance to flush the error before exiting.
thread::sleep(time::Duration::from_millis(500));
process::exit(1)
});
/*
* Start threads.
*/
let mut threads = vec![];
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());