2019-03-30 07:14:04 +00:00
|
|
|
mod attestation_producer;
|
2019-03-29 06:28:07 +00:00
|
|
|
mod block_producer;
|
2019-02-14 01:09:18 +00:00
|
|
|
mod config;
|
|
|
|
mod duties;
|
2019-03-25 05:50:15 +00:00
|
|
|
pub mod error;
|
2019-03-22 06:27:07 +00:00
|
|
|
mod service;
|
2019-03-29 05:33:27 +00:00
|
|
|
mod signer;
|
2019-03-22 06:27:07 +00:00
|
|
|
|
2019-03-25 06:03:17 +00:00
|
|
|
use crate::config::Config as ValidatorClientConfig;
|
2019-04-08 05:39:26 +00:00
|
|
|
use crate::service::Service as ValidatorService;
|
2019-06-09 08:34:56 +00:00
|
|
|
use clap::{App, Arg};
|
2019-07-01 06:38:42 +00:00
|
|
|
use eth2_config::{read_from_file, write_to_file, Eth2Config};
|
2019-03-29 12:45:53 +00:00
|
|
|
use protos::services_grpc::ValidatorServiceClient;
|
2019-06-09 00:21:50 +00:00
|
|
|
use slog::{crit, error, info, o, Drain};
|
2019-07-01 06:38:42 +00:00
|
|
|
use std::fs;
|
2019-06-09 00:21:50 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
use types::{Keypair, MainnetEthSpec, MinimalEthSpec};
|
|
|
|
|
|
|
|
pub const DEFAULT_SPEC: &str = "minimal";
|
|
|
|
pub const DEFAULT_DATA_DIR: &str = ".lighthouse-validator";
|
2019-06-09 10:35:36 +00:00
|
|
|
pub const CLIENT_CONFIG_FILENAME: &str = "validator-client.toml";
|
|
|
|
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
|
2019-02-14 01:09:18 +00:00
|
|
|
|
|
|
|
fn main() {
|
|
|
|
// Logging
|
|
|
|
let decorator = slog_term::TermDecorator::new().build();
|
|
|
|
let drain = slog_term::CompactFormat::new(decorator).build().fuse();
|
|
|
|
let drain = slog_async::Async::new(drain).build().fuse();
|
|
|
|
let log = slog::Logger::root(drain, o!());
|
|
|
|
|
|
|
|
// CLI
|
|
|
|
let matches = App::new("Lighthouse Validator Client")
|
|
|
|
.version("0.0.1")
|
|
|
|
.author("Sigma Prime <contact@sigmaprime.io>")
|
|
|
|
.about("Eth 2.0 Validator Client")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("datadir")
|
|
|
|
.long("datadir")
|
|
|
|
.value_name("DIR")
|
|
|
|
.help("Data directory for keys and databases.")
|
2019-06-09 08:34:56 +00:00
|
|
|
.takes_value(true),
|
2019-06-09 00:21:50 +00:00
|
|
|
)
|
|
|
|
.arg(
|
2019-06-11 01:37:59 +00:00
|
|
|
Arg::with_name("eth2-spec")
|
|
|
|
.long("eth2-spec")
|
2019-06-09 00:21:50 +00:00
|
|
|
.short("e")
|
2019-06-11 01:37:59 +00:00
|
|
|
.value_name("TOML_FILE")
|
|
|
|
.help("Path to Ethereum 2.0 specifications file.")
|
|
|
|
.takes_value(true),
|
2019-02-14 01:09:18 +00:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("server")
|
|
|
|
.long("server")
|
|
|
|
.value_name("server")
|
|
|
|
.help("Address to connect to BeaconNode.")
|
|
|
|
.takes_value(true),
|
|
|
|
)
|
2019-03-01 17:19:08 +00:00
|
|
|
.arg(
|
2019-06-09 00:21:50 +00:00
|
|
|
Arg::with_name("spec-constants")
|
|
|
|
.long("spec-constants")
|
|
|
|
.value_name("TITLE")
|
2019-03-01 17:19:08 +00:00
|
|
|
.short("s")
|
2019-06-09 00:21:50 +00:00
|
|
|
.help("The title of the spec constants for chain config.")
|
2019-03-01 17:19:08 +00:00
|
|
|
.takes_value(true)
|
2019-06-08 12:49:04 +00:00
|
|
|
.possible_values(&["mainnet", "minimal"])
|
|
|
|
.default_value("minimal"),
|
2019-03-01 17:19:08 +00:00
|
|
|
)
|
2019-02-14 01:09:18 +00:00
|
|
|
.get_matches();
|
|
|
|
|
2019-07-02 07:32:14 +00:00
|
|
|
let data_dir = match matches
|
2019-07-01 06:38:42 +00:00
|
|
|
.value_of("datadir")
|
|
|
|
.and_then(|v| Some(PathBuf::from(v)))
|
2019-07-02 07:32:14 +00:00
|
|
|
{
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
// use the default
|
|
|
|
let mut default_dir = match dirs::home_dir() {
|
|
|
|
Some(v) => v,
|
|
|
|
None => {
|
|
|
|
crit!(log, "Failed to find a home directory");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
default_dir.push(DEFAULT_DATA_DIR);
|
|
|
|
PathBuf::from(default_dir)
|
|
|
|
}
|
|
|
|
};
|
2019-07-01 06:38:42 +00:00
|
|
|
|
|
|
|
// create the directory if needed
|
|
|
|
match fs::create_dir_all(&data_dir) {
|
|
|
|
Ok(_) => {}
|
2019-06-09 00:21:50 +00:00
|
|
|
Err(e) => {
|
2019-07-01 06:38:42 +00:00
|
|
|
crit!(log, "Failed to initialize data dir"; "error" => format!("{}", e));
|
2019-06-09 08:34:56 +00:00
|
|
|
return;
|
2019-06-09 00:21:50 +00:00
|
|
|
}
|
2019-07-01 06:38:42 +00:00
|
|
|
}
|
2019-06-09 00:21:50 +00:00
|
|
|
|
|
|
|
let client_config_path = data_dir.join(CLIENT_CONFIG_FILENAME);
|
|
|
|
|
|
|
|
// Attempt to lead the `ClientConfig` from disk.
|
|
|
|
//
|
|
|
|
// If file doesn't exist, create a new, default one.
|
|
|
|
let mut client_config = match read_from_file::<ValidatorClientConfig>(
|
|
|
|
client_config_path.clone(),
|
|
|
|
) {
|
|
|
|
Ok(Some(c)) => c,
|
|
|
|
Ok(None) => {
|
|
|
|
let default = ValidatorClientConfig::default();
|
|
|
|
if let Err(e) = write_to_file(client_config_path.clone(), &default) {
|
|
|
|
crit!(log, "Failed to write default ClientConfig to file"; "error" => format!("{:?}", e));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
crit!(log, "Failed to load a ChainConfig file"; "error" => format!("{:?}", e));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Ensure the `data_dir` in the config matches that supplied to the CLI.
|
|
|
|
client_config.data_dir = data_dir.clone();
|
|
|
|
|
|
|
|
// Update the client config with any CLI args.
|
|
|
|
match client_config.apply_cli_args(&matches) {
|
|
|
|
Ok(()) => (),
|
|
|
|
Err(s) => {
|
|
|
|
crit!(log, "Failed to parse ClientConfig CLI arguments"; "error" => s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let eth2_config_path: PathBuf = matches
|
2019-06-11 01:37:59 +00:00
|
|
|
.value_of("eth2-spec")
|
2019-06-09 00:21:50 +00:00
|
|
|
.and_then(|s| Some(PathBuf::from(s)))
|
|
|
|
.unwrap_or_else(|| data_dir.join(ETH2_CONFIG_FILENAME));
|
|
|
|
|
|
|
|
// Attempt to load the `Eth2Config` from file.
|
|
|
|
//
|
|
|
|
// If the file doesn't exist, create a default one depending on the CLI flags.
|
2019-06-09 08:34:56 +00:00
|
|
|
let mut eth2_config = match read_from_file::<Eth2Config>(eth2_config_path.clone()) {
|
2019-06-09 00:21:50 +00:00
|
|
|
Ok(Some(c)) => c,
|
|
|
|
Ok(None) => {
|
|
|
|
let default = match matches.value_of("spec-constants") {
|
|
|
|
Some("mainnet") => Eth2Config::mainnet(),
|
|
|
|
Some("minimal") => Eth2Config::minimal(),
|
|
|
|
_ => unreachable!(), // Guarded by slog.
|
|
|
|
};
|
|
|
|
if let Err(e) = write_to_file(eth2_config_path, &default) {
|
|
|
|
crit!(log, "Failed to write default Eth2Config to file"; "error" => format!("{:?}", e));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
crit!(log, "Failed to instantiate an Eth2Config"; "error" => format!("{:?}", e));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Update the eth2 config with any CLI flags.
|
|
|
|
match eth2_config.apply_cli_args(&matches) {
|
|
|
|
Ok(()) => (),
|
|
|
|
Err(s) => {
|
|
|
|
crit!(log, "Failed to parse Eth2Config CLI arguments"; "error" => s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
info!(
|
|
|
|
log,
|
|
|
|
"Starting validator client";
|
|
|
|
"datadir" => client_config.data_dir.to_str(),
|
|
|
|
"spec_constants" => ð2_config.spec_constants,
|
|
|
|
);
|
|
|
|
|
|
|
|
let result = match eth2_config.spec_constants.as_str() {
|
|
|
|
"mainnet" => ValidatorService::<ValidatorServiceClient, Keypair>::start::<MainnetEthSpec>(
|
|
|
|
client_config,
|
|
|
|
eth2_config,
|
|
|
|
log.clone(),
|
|
|
|
),
|
|
|
|
"minimal" => ValidatorService::<ValidatorServiceClient, Keypair>::start::<MinimalEthSpec>(
|
|
|
|
client_config,
|
|
|
|
eth2_config,
|
|
|
|
log.clone(),
|
|
|
|
),
|
|
|
|
other => {
|
|
|
|
crit!(log, "Unknown spec constants"; "title" => other);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
2019-02-14 01:09:18 +00:00
|
|
|
|
2019-03-22 06:27:07 +00:00
|
|
|
// start the validator service.
|
2019-03-30 03:27:37 +00:00
|
|
|
// this specifies the GRPC and signer type to use as the duty manager beacon node.
|
2019-06-09 00:21:50 +00:00
|
|
|
match result {
|
2019-03-25 05:50:15 +00:00
|
|
|
Ok(_) => info!(log, "Validator client shutdown successfully."),
|
2019-06-09 00:21:50 +00:00
|
|
|
Err(e) => crit!(log, "Validator client exited with error"; "error" => e.to_string()),
|
|
|
|
}
|
|
|
|
}
|