diff --git a/account_manager/Cargo.toml b/account_manager/Cargo.toml index fc3df1e8d..2838b242d 100644 --- a/account_manager/Cargo.toml +++ b/account_manager/Cargo.toml @@ -13,3 +13,4 @@ slog-async = "2.3.0" validator_client = { path = "../validator_client" } types = { path = "../eth2/types" } dirs = "2.0.2" +environment = { path = "../lighthouse/environment" } diff --git a/account_manager/src/cli.rs b/account_manager/src/cli.rs new file mode 100644 index 000000000..9eded83cc --- /dev/null +++ b/account_manager/src/cli.rs @@ -0,0 +1,54 @@ +use clap::{App, Arg, SubCommand}; + +pub fn cli_app<'a, 'b>() -> App<'a, 'b> { + App::new("Account Manager") + .visible_aliases(&["am", "accounts", "accounts_manager"]) + .version("0.0.1") + .author("Sigma Prime ") + .about("Eth 2.0 Accounts Manager") + .arg( + Arg::with_name("logfile") + .long("logfile") + .value_name("logfile") + .help("File path where output will be written.") + .takes_value(true), + ) + .arg( + Arg::with_name("datadir") + .long("datadir") + .short("d") + .value_name("DIR") + .help("Data directory for keys and databases.") + .takes_value(true), + ) + .subcommand( + SubCommand::with_name("generate") + .about("Generates a new validator private key") + .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), + ) + .arg( + Arg::with_name("validator count") + .long("validator_count") + .short("n") + .value_name("validator_count") + .help("If supplied along with `index`, generates keys `i..i + n`.") + .takes_value(true) + .default_value("1"), + ), + ) +} diff --git a/account_manager/src/main.rs b/account_manager/src/lib.rs similarity index 58% rename from account_manager/src/main.rs rename to account_manager/src/lib.rs index 095f65cbc..90a80e6dd 100644 --- a/account_manager/src/main.rs +++ b/account_manager/src/lib.rs @@ -1,72 +1,21 @@ +mod cli; + use bls::Keypair; -use clap::{App, Arg, SubCommand}; -use slog::{crit, debug, info, o, Drain}; +use clap::ArgMatches; +use environment::RuntimeContext; +use slog::{crit, debug, info}; use std::fs; use std::path::PathBuf; -use types::test_utils::generate_deterministic_keypair; +use types::{test_utils::generate_deterministic_keypair, EthSpec}; use validator_client::Config as ValidatorClientConfig; +pub use cli::cli_app; + pub const DEFAULT_DATA_DIR: &str = ".lighthouse-validator"; pub const CLIENT_CONFIG_FILENAME: &str = "account-manager.toml"; -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 mut log = slog::Logger::root(drain, o!()); - - // CLI - let matches = App::new("Lighthouse Accounts Manager") - .version("0.0.1") - .author("Sigma Prime ") - .about("Eth 2.0 Accounts Manager") - .arg( - Arg::with_name("logfile") - .long("logfile") - .value_name("logfile") - .help("File path where output will be written.") - .takes_value(true), - ) - .arg( - Arg::with_name("datadir") - .long("datadir") - .short("d") - .value_name("DIR") - .help("Data directory for keys and databases.") - .takes_value(true), - ) - .subcommand( - SubCommand::with_name("generate") - .about("Generates a new validator private key") - .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), - ) - .arg( - Arg::with_name("validator count") - .long("validator_count") - .short("n") - .value_name("validator_count") - .help("If supplied along with `index`, generates keys `i..i + n`.") - .takes_value(true) - .default_value("1"), - ), - ) - .get_matches(); +pub fn run(matches: &ArgMatches, context: RuntimeContext) { + let mut log = context.log; let data_dir = match matches .value_of("datadir") diff --git a/beacon_node/client/Cargo.toml b/beacon_node/client/Cargo.toml index 1a82cd22b..d806f2007 100644 --- a/beacon_node/client/Cargo.toml +++ b/beacon_node/client/Cargo.toml @@ -27,7 +27,6 @@ error-chain = "0.12.1" serde_yaml = "0.8.11" slog = { version = "2.5.2", features = ["max_level_trace"] } slog-async = "2.3.0" -slog-json = "2.3.0" tokio = "0.1.22" clap = "2.33.0" dirs = "2.0.2" diff --git a/beacon_node/client/src/config.rs b/beacon_node/client/src/config.rs index 331c905cc..b88b2ba4c 100644 --- a/beacon_node/client/src/config.rs +++ b/beacon_node/client/src/config.rs @@ -1,10 +1,8 @@ use clap::ArgMatches; use network::NetworkConfig; use serde_derive::{Deserialize, Serialize}; -use slog::{info, o, Drain}; -use std::fs::{self, OpenOptions}; +use std::fs; use std::path::PathBuf; -use std::sync::Mutex; /// The number initial validators when starting the `Minimal`. const TESTNET_SPEC_CONSTANTS: &str = "minimal"; @@ -95,47 +93,11 @@ impl Config { Some(path) } - // Update the logger to output in JSON to specified file - fn update_logger(&mut self, log: &mut slog::Logger) -> Result<(), &'static str> { - let file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&self.log_file); - - if file.is_err() { - return Err("Cannot open log file"); - } - let file = file.unwrap(); - - if let Some(file) = self.log_file.to_str() { - info!( - *log, - "Log file specified, output will now be written to {} in json.", file - ); - } else { - info!( - *log, - "Log file specified output will now be written in json" - ); - } - - let drain = Mutex::new(slog_json::Json::default(file)).fuse(); - let drain = slog_async::Async::new(drain).build().fuse(); - *log = slog::Logger::root(drain, o!()); - - Ok(()) - } - /// Apply the following arguments to `self`, replacing values if they are specified in `args`. /// /// Returns an error if arguments are obviously invalid. May succeed even if some values are /// invalid. - pub fn apply_cli_args( - &mut self, - args: &ArgMatches, - log: &mut slog::Logger, - ) -> Result<(), String> { + pub fn apply_cli_args(&mut self, args: &ArgMatches, _log: &slog::Logger) -> Result<(), String> { if let Some(dir) = args.value_of("datadir") { self.data_dir = PathBuf::from(dir); }; @@ -149,11 +111,6 @@ impl Config { self.rest_api.apply_cli_args(args)?; self.websocket_server.apply_cli_args(args)?; - if let Some(log_file) = args.value_of("logfile") { - self.log_file = PathBuf::from(log_file); - self.update_logger(log)?; - }; - Ok(()) } } diff --git a/lighthouse/Cargo.toml b/lighthouse/Cargo.toml index 25a41ea6a..ff6cc054b 100644 --- a/lighthouse/Cargo.toml +++ b/lighthouse/Cargo.toml @@ -18,3 +18,4 @@ slog-async = "^2.3.0" environment = { path = "./environment" } futures = "0.1.25" validator_client = { "path" = "../validator_client" } +account_manager = { "path" = "../account_manager" } diff --git a/lighthouse/environment/Cargo.toml b/lighthouse/environment/Cargo.toml index b5e21a4e8..01053ae9a 100644 --- a/lighthouse/environment/Cargo.toml +++ b/lighthouse/environment/Cargo.toml @@ -17,3 +17,4 @@ slog-async = "^2.3.0" ctrlc = { version = "3.1.1", features = ["termination"] } futures = "0.1.25" parking_lot = "0.7" +slog-json = "2.3.0" diff --git a/lighthouse/environment/src/lib.rs b/lighthouse/environment/src/lib.rs index bde6c2dbb..69002682a 100644 --- a/lighthouse/environment/src/lib.rs +++ b/lighthouse/environment/src/lib.rs @@ -9,9 +9,12 @@ use eth2_config::Eth2Config; use futures::{sync::oneshot, Future}; -use slog::{o, Drain, Level, Logger}; +use slog::{info, o, Drain, Level, Logger}; use sloggers::{null::NullLoggerBuilder, Build}; use std::cell::RefCell; +use std::fs::OpenOptions; +use std::path::PathBuf; +use std::sync::Mutex; use tokio::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor}; use types::{EthSpec, InteropEthSpec, MainnetEthSpec, MinimalEthSpec}; @@ -224,6 +227,28 @@ impl Environment { .map_err(|e| format!("Tokio runtime shutdown returned an error: {:?}", e)) } + /// Sets the logger (and all child loggers) to log to a file. + pub fn log_to_json_file(&mut self, path: PathBuf) -> Result<(), String> { + let file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&path) + .map_err(|e| format!("Unable to open logfile: {:?}", e))?; + + let drain = Mutex::new(slog_json::Json::default(file)).fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); + self.log = slog::Logger::root(drain, o!()); + + info!( + self.log, + "Logging to JSON file"; + "path" => format!("{:?}", path) + ); + + Ok(()) + } + pub fn eth_spec_instance(&self) -> &E { &self.eth_spec_instance } diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index 5ad7a2bf8..badc464e1 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -6,6 +6,7 @@ use clap::{App, Arg, ArgMatches}; use env_logger::{Builder, Env}; use environment::EnvironmentBuilder; use slog::{crit, info, warn}; +use std::path::PathBuf; use std::process::exit; use types::EthSpec; use validator_client::ProductionValidatorClient; @@ -52,6 +53,7 @@ fn main() { ) .subcommand(beacon_node::cli_app()) .subcommand(validator_client::cli_app()) + .subcommand(account_manager::cli_app()) .get_matches(); macro_rules! run_with_spec { @@ -92,6 +94,13 @@ fn run( let log = environment.core_context().log; + if let Some(log_path) = matches.value_of("logfile") { + let path = log_path + .parse::() + .map_err(|e| format!("Failed to parse log path: {:?}", e))?; + environment.log_to_json_file(path)?; + } + if std::mem::size_of::() != 8 { crit!( log, @@ -114,6 +123,16 @@ fn run( // // Creating a command which can run both might be useful future works. + if let Some(sub_matches) = matches.subcommand_matches("Account Manager") { + let runtime_context = environment.core_context(); + + account_manager::run(sub_matches, runtime_context); + + // Exit early if the account manager was run. It does not used the tokio executor, so no + // need to wait for it to shutdown. + return Ok(()); + } + let beacon_node = if let Some(sub_matches) = matches.subcommand_matches("Beacon Node") { let runtime_context = environment.core_context(); diff --git a/validator_client/Cargo.toml b/validator_client/Cargo.toml index 038bbd3c3..c3e9cb856 100644 --- a/validator_client/Cargo.toml +++ b/validator_client/Cargo.toml @@ -25,7 +25,6 @@ serde_derive = "1.0.102" serde_json = "1.0.41" slog = { version = "2.5.2", features = ["max_level_trace", "release_max_level_trace"] } slog-async = "2.3.0" -slog-json = "2.3.0" slog-term = "2.4.2" tokio = "0.1.22" tokio-timer = "0.2.11" diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index d56487616..749a5813c 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -2,12 +2,11 @@ use bincode; use bls::Keypair; use clap::ArgMatches; use serde_derive::{Deserialize, Serialize}; -use slog::{error, info, o, warn, Drain}; -use std::fs::{self, File, OpenOptions}; +use slog::{error, warn}; +use std::fs::{self, File}; use std::io::{Error, ErrorKind}; use std::ops::Range; use std::path::PathBuf; -use std::sync::Mutex; use types::{ test_utils::{generate_deterministic_keypair, load_keypairs_from_yaml}, EthSpec, MainnetEthSpec, @@ -94,17 +93,12 @@ impl Config { pub fn apply_cli_args( &mut self, args: &ArgMatches, - log: &mut slog::Logger, + _log: &slog::Logger, ) -> Result<(), &'static str> { if let Some(datadir) = args.value_of("datadir") { self.data_dir = PathBuf::from(datadir); }; - if let Some(log_file) = args.value_of("logfile") { - self.log_file = PathBuf::from(log_file); - self.update_logger(log)?; - }; - if let Some(srv) = args.value_of("server") { self.server = srv.to_string(); }; @@ -112,38 +106,6 @@ impl Config { Ok(()) } - // Update the logger to output in JSON to specified file - fn update_logger(&mut self, log: &mut slog::Logger) -> Result<(), &'static str> { - let file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&self.log_file); - - if file.is_err() { - return Err("Cannot open log file"); - } - let file = file.unwrap(); - - if let Some(file) = self.log_file.to_str() { - info!( - *log, - "Log file specified, output will now be written to {} in json.", file - ); - } else { - info!( - *log, - "Log file specified output will now be written in json" - ); - } - - let drain = Mutex::new(slog_json::Json::default(file)).fuse(); - let drain = slog_async::Async::new(drain).build().fuse(); - *log = slog::Logger::root(drain, o!()); - - Ok(()) - } - /// Reads a single keypair from the given `path`. /// /// `path` should be the path to a directory containing a private key. The file name of `path`