Directory restructure (#1532)

Closes #1487
Closes #1427

Directory restructure in accordance with #1487. Also has temporary migration code to move the old directories into new structure.
Also extracts all default directory names and utility functions into a `directory` crate to avoid repetitio.

~Since `validator_definition.yaml` stores absolute paths, users will have to manually change the keystore paths or delete the file to get the validators picked up by the vc.~. `validator_definition.yaml` is migrated as well from the default directories.

Co-authored-by: realbigsean <seananderson33@gmail.com>
Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
Pawan Dhananjay 2020-09-29 00:02:44 +00:00 committed by Paul Hauner
parent dffc56ef1d
commit 8e20176337
No known key found for this signature in database
GPG Key ID: 5E2CFF9B75FA63DF
40 changed files with 367 additions and 265 deletions

18
Cargo.lock generated
View File

@ -9,6 +9,7 @@ dependencies = [
"clap", "clap",
"clap_utils", "clap_utils",
"deposit_contract", "deposit_contract",
"directory",
"dirs", "dirs",
"environment", "environment",
"eth2_keystore", "eth2_keystore",
@ -377,6 +378,7 @@ dependencies = [
"clap_utils", "clap_utils",
"client", "client",
"ctrlc", "ctrlc",
"directory",
"dirs", "dirs",
"environment", "environment",
"eth2_config", "eth2_config",
@ -758,6 +760,7 @@ version = "0.2.0"
dependencies = [ dependencies = [
"beacon_chain", "beacon_chain",
"bus", "bus",
"directory",
"dirs", "dirs",
"environment", "environment",
"error-chain", "error-chain",
@ -1216,6 +1219,16 @@ dependencies = [
"generic-array 0.14.4", "generic-array 0.14.4",
] ]
[[package]]
name = "directory"
version = "0.1.0"
dependencies = [
"clap",
"clap_utils",
"dirs",
"eth2_testnet_config",
]
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "2.0.2" version = "2.0.2"
@ -1522,6 +1535,7 @@ name = "eth2_libp2p"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"base64 0.12.3", "base64 0.12.3",
"directory",
"dirs", "dirs",
"discv5", "discv5",
"environment", "environment",
@ -2567,6 +2581,7 @@ dependencies = [
"clap", "clap",
"clap_utils", "clap_utils",
"deposit_contract", "deposit_contract",
"directory",
"dirs", "dirs",
"environment", "environment",
"eth2_keystore", "eth2_keystore",
@ -2929,6 +2944,7 @@ dependencies = [
"boot_node", "boot_node",
"clap", "clap",
"clap_utils", "clap_utils",
"directory",
"env_logger", "env_logger",
"environment", "environment",
"eth2_testnet_config", "eth2_testnet_config",
@ -5829,7 +5845,6 @@ dependencies = [
"compare_fields_derive", "compare_fields_derive",
"criterion", "criterion",
"derivative", "derivative",
"dirs",
"eth2_hashing", "eth2_hashing",
"eth2_interop_keypairs", "eth2_interop_keypairs",
"eth2_ssz", "eth2_ssz",
@ -6022,6 +6037,7 @@ dependencies = [
"clap", "clap",
"clap_utils", "clap_utils",
"deposit_contract", "deposit_contract",
"directory",
"dirs", "dirs",
"environment", "environment",
"eth2_config", "eth2_config",

View File

@ -20,6 +20,7 @@ members = [
"common/compare_fields", "common/compare_fields",
"common/compare_fields_derive", "common/compare_fields_derive",
"common/deposit_contract", "common/deposit_contract",
"common/directory",
"common/eth2_config", "common/eth2_config",
"common/eth2_interop_keypairs", "common/eth2_interop_keypairs",
"common/eth2_testnet_config", "common/eth2_testnet_config",

View File

@ -24,6 +24,7 @@ eth2_testnet_config = { path = "../common/eth2_testnet_config" }
web3 = "0.11.0" web3 = "0.11.0"
futures = { version = "0.3.5", features = ["compat"] } futures = { version = "0.3.5", features = ["compat"] }
clap_utils = { path = "../common/clap_utils" } clap_utils = { path = "../common/clap_utils" }
directory = { path = "../common/directory" }
eth2_wallet = { path = "../crypto/eth2_wallet" } eth2_wallet = { path = "../crypto/eth2_wallet" }
eth2_wallet_manager = { path = "../common/eth2_wallet_manager" } eth2_wallet_manager = { path = "../common/eth2_wallet_manager" }
rand = "0.7.2" rand = "0.7.2"

View File

@ -1,10 +1,8 @@
use account_utils::PlainText; use account_utils::PlainText;
use account_utils::{read_input_from_user, strip_off_newlines}; use account_utils::{read_input_from_user, strip_off_newlines};
use clap::ArgMatches;
use eth2_wallet::bip39::{Language, Mnemonic}; use eth2_wallet::bip39::{Language, Mnemonic};
use std::fs; use std::fs;
use std::fs::create_dir_all; use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::from_utf8; use std::str::from_utf8;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
@ -12,26 +10,6 @@ use std::time::Duration;
pub const MNEMONIC_PROMPT: &str = "Enter the mnemonic phrase:"; pub const MNEMONIC_PROMPT: &str = "Enter the mnemonic phrase:";
pub const WALLET_NAME_PROMPT: &str = "Enter wallet name:"; pub const WALLET_NAME_PROMPT: &str = "Enter wallet name:";
pub fn ensure_dir_exists<P: AsRef<Path>>(path: P) -> Result<(), String> {
let path = path.as_ref();
if !path.exists() {
create_dir_all(path).map_err(|e| format!("Unable to create {:?}: {:?}", path, e))?;
}
Ok(())
}
pub fn base_wallet_dir(matches: &ArgMatches, arg: &'static str) -> Result<PathBuf, String> {
clap_utils::parse_path_with_default_in_home_dir(
matches,
arg,
PathBuf::new().join(".lighthouse").join("wallets"),
)
}
/// Reads in a mnemonic from the user. If the file path is provided, read from it. Otherwise, read
/// from an interactive prompt using tty, unless the `--stdin-inputs` flag is provided.
pub fn read_mnemonic_from_cli( pub fn read_mnemonic_from_cli(
mnemonic_path: Option<PathBuf>, mnemonic_path: Option<PathBuf>,
stdin_inputs: bool, stdin_inputs: bool,

View File

@ -10,7 +10,7 @@ use types::EthSpec;
pub const CMD: &str = "account_manager"; pub const CMD: &str = "account_manager";
pub const SECRETS_DIR_FLAG: &str = "secrets-dir"; pub const SECRETS_DIR_FLAG: &str = "secrets-dir";
pub const VALIDATOR_DIR_FLAG: &str = "validator-dir"; pub const VALIDATOR_DIR_FLAG: &str = "validator-dir";
pub const BASE_DIR_FLAG: &str = "base-dir"; pub const WALLETS_DIR_FLAG: &str = "wallets-dir";
pub fn cli_app<'a, 'b>() -> App<'a, 'b> { pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new(CMD) App::new(CMD)

View File

@ -1,10 +1,13 @@
use crate::common::read_wallet_name_from_cli; use crate::common::read_wallet_name_from_cli;
use crate::wallet::create::STDIN_INPUTS_FLAG; use crate::wallet::create::STDIN_INPUTS_FLAG;
use crate::{common::ensure_dir_exists, SECRETS_DIR_FLAG, VALIDATOR_DIR_FLAG}; use crate::{SECRETS_DIR_FLAG, WALLETS_DIR_FLAG};
use account_utils::{ use account_utils::{
random_password, read_password_from_user, strip_off_newlines, validator_definitions, PlainText, random_password, read_password_from_user, strip_off_newlines, validator_definitions, PlainText,
}; };
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use directory::{
ensure_dir_exists, parse_path_or_default_with_flag, DEFAULT_SECRET_DIR, DEFAULT_WALLET_DIR,
};
use environment::Environment; use environment::Environment;
use eth2_wallet_manager::WalletManager; use eth2_wallet_manager::WalletManager;
use std::ffi::OsStr; use std::ffi::OsStr;
@ -14,7 +17,6 @@ use types::EthSpec;
use validator_dir::Builder as ValidatorDirBuilder; use validator_dir::Builder as ValidatorDirBuilder;
pub const CMD: &str = "create"; pub const CMD: &str = "create";
pub const BASE_DIR_FLAG: &str = "base-dir";
pub const WALLET_NAME_FLAG: &str = "wallet-name"; pub const WALLET_NAME_FLAG: &str = "wallet-name";
pub const WALLET_PASSWORD_FLAG: &str = "wallet-password"; pub const WALLET_PASSWORD_FLAG: &str = "wallet-password";
pub const DEPOSIT_GWEI_FLAG: &str = "deposit-gwei"; pub const DEPOSIT_GWEI_FLAG: &str = "deposit-gwei";
@ -44,14 +46,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name(VALIDATOR_DIR_FLAG) Arg::with_name(WALLETS_DIR_FLAG)
.long(VALIDATOR_DIR_FLAG) .long(WALLETS_DIR_FLAG)
.value_name("VALIDATOR_DIRECTORY") .value_name(WALLETS_DIR_FLAG)
.help( .help("A path containing Eth2 EIP-2386 wallets. Defaults to ~/.lighthouse/{testnet}/wallets")
"The path where the validator directories will be created. \ .takes_value(true)
Defaults to ~/.lighthouse/validators", .conflicts_with("datadir"),
)
.takes_value(true),
) )
.arg( .arg(
Arg::with_name(SECRETS_DIR_FLAG) Arg::with_name(SECRETS_DIR_FLAG)
@ -59,8 +59,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.value_name("SECRETS_DIR") .value_name("SECRETS_DIR")
.help( .help(
"The path where the validator keystore passwords will be stored. \ "The path where the validator keystore passwords will be stored. \
Defaults to ~/.lighthouse/secrets", Defaults to ~/.lighthouse/{testnet}/secrets",
) )
.conflicts_with("datadir")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@ -111,23 +112,25 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
pub fn cli_run<T: EthSpec>( pub fn cli_run<T: EthSpec>(
matches: &ArgMatches, matches: &ArgMatches,
mut env: Environment<T>, mut env: Environment<T>,
wallet_base_dir: PathBuf, validator_dir: PathBuf,
) -> Result<(), String> { ) -> Result<(), String> {
let spec = env.core_context().eth2_config.spec; let spec = env.core_context().eth2_config.spec;
let name: Option<String> = clap_utils::parse_optional(matches, WALLET_NAME_FLAG)?; let name: Option<String> = clap_utils::parse_optional(matches, WALLET_NAME_FLAG)?;
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG); let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
let wallet_base_dir = if matches.value_of("datadir").is_some() {
let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
path.join(DEFAULT_WALLET_DIR)
} else {
parse_path_or_default_with_flag(matches, WALLETS_DIR_FLAG, DEFAULT_WALLET_DIR)?
};
let secrets_dir = if matches.value_of("datadir").is_some() {
let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
path.join(DEFAULT_SECRET_DIR)
} else {
parse_path_or_default_with_flag(matches, SECRETS_DIR_FLAG, DEFAULT_SECRET_DIR)?
};
let validator_dir = clap_utils::parse_path_with_default_in_home_dir(
matches,
VALIDATOR_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("validators"),
)?;
let secrets_dir = clap_utils::parse_path_with_default_in_home_dir(
matches,
SECRETS_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("secrets"),
)?;
let deposit_gwei = clap_utils::parse_optional(matches, DEPOSIT_GWEI_FLAG)? let deposit_gwei = clap_utils::parse_optional(matches, DEPOSIT_GWEI_FLAG)?
.unwrap_or_else(|| spec.max_effective_balance); .unwrap_or_else(|| spec.max_effective_balance);
let count: Option<usize> = clap_utils::parse_optional(matches, COUNT_FLAG)?; let count: Option<usize> = clap_utils::parse_optional(matches, COUNT_FLAG)?;
@ -136,6 +139,9 @@ pub fn cli_run<T: EthSpec>(
ensure_dir_exists(&validator_dir)?; ensure_dir_exists(&validator_dir)?;
ensure_dir_exists(&secrets_dir)?; ensure_dir_exists(&secrets_dir)?;
eprintln!("secrets-dir path {:?}", secrets_dir);
eprintln!("wallets-dir path {:?}", wallet_base_dir);
let starting_validator_count = existing_validator_count(&validator_dir)?; let starting_validator_count = existing_validator_count(&validator_dir)?;
let n = match (count, at_most) { let n = match (count, at_most) {
@ -166,7 +172,7 @@ pub fn cli_run<T: EthSpec>(
let wallet_password = read_wallet_password_from_cli(wallet_password_path, stdin_inputs)?; let wallet_password = read_wallet_password_from_cli(wallet_password_path, stdin_inputs)?;
let mgr = WalletManager::open(&wallet_base_dir) let mgr = WalletManager::open(&wallet_base_dir)
.map_err(|e| format!("Unable to open --{}: {:?}", BASE_DIR_FLAG, e))?; .map_err(|e| format!("Unable to open --{}: {:?}", WALLETS_DIR_FLAG, e))?;
let mut wallet = mgr let mut wallet = mgr
.wallet_by_name(&wallet_name) .wallet_by_name(&wallet_name)

View File

@ -46,16 +46,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
The deposit contract address will be determined by the --testnet-dir flag on the \ The deposit contract address will be determined by the --testnet-dir flag on the \
primary Lighthouse binary.", primary Lighthouse binary.",
) )
.arg(
Arg::with_name(VALIDATOR_DIR_FLAG)
.long(VALIDATOR_DIR_FLAG)
.value_name("VALIDATOR_DIRECTORY")
.help(
"The path to the validator client data directory. \
Defaults to ~/.lighthouse/validators",
)
.takes_value(true),
)
.arg( .arg(
Arg::with_name(VALIDATOR_FLAG) Arg::with_name(VALIDATOR_FLAG)
.long(VALIDATOR_FLAG) .long(VALIDATOR_FLAG)
@ -209,14 +199,10 @@ where
pub fn cli_run<T: EthSpec>( pub fn cli_run<T: EthSpec>(
matches: &ArgMatches<'_>, matches: &ArgMatches<'_>,
mut env: Environment<T>, mut env: Environment<T>,
validator_dir: PathBuf,
) -> Result<(), String> { ) -> Result<(), String> {
let log = env.core_context().log().clone(); let log = env.core_context().log().clone();
let data_dir = clap_utils::parse_path_with_default_in_home_dir(
matches,
VALIDATOR_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("validators"),
)?;
let validator: String = clap_utils::parse_required(matches, VALIDATOR_FLAG)?; let validator: String = clap_utils::parse_required(matches, VALIDATOR_FLAG)?;
let eth1_ipc_path: Option<PathBuf> = clap_utils::parse_optional(matches, ETH1_IPC_FLAG)?; let eth1_ipc_path: Option<PathBuf> = clap_utils::parse_optional(matches, ETH1_IPC_FLAG)?;
let eth1_http_url: Option<String> = clap_utils::parse_optional(matches, ETH1_HTTP_FLAG)?; let eth1_http_url: Option<String> = clap_utils::parse_optional(matches, ETH1_HTTP_FLAG)?;
@ -225,7 +211,7 @@ pub fn cli_run<T: EthSpec>(
let confirmation_batch_size: usize = let confirmation_batch_size: usize =
clap_utils::parse_required(matches, CONFIRMATION_BATCH_SIZE_FLAG)?; clap_utils::parse_required(matches, CONFIRMATION_BATCH_SIZE_FLAG)?;
let manager = ValidatorManager::open(&data_dir) let manager = ValidatorManager::open(&validator_dir)
.map_err(|e| format!("Unable to read --{}: {:?}", VALIDATOR_DIR_FLAG, e))?; .map_err(|e| format!("Unable to read --{}: {:?}", VALIDATOR_DIR_FLAG, e))?;
let validators = match validator.as_ref() { let validators = match validator.as_ref() {

View File

@ -1,5 +1,4 @@
use crate::wallet::create::STDIN_INPUTS_FLAG; use crate::wallet::create::STDIN_INPUTS_FLAG;
use crate::{common::ensure_dir_exists, VALIDATOR_DIR_FLAG};
use account_utils::{ use account_utils::{
eth2_keystore::Keystore, eth2_keystore::Keystore,
read_password_from_user, read_password_from_user,
@ -55,16 +54,6 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.required_unless(KEYSTORE_FLAG) .required_unless(KEYSTORE_FLAG)
.takes_value(true), .takes_value(true),
) )
.arg(
Arg::with_name(VALIDATOR_DIR_FLAG)
.long(VALIDATOR_DIR_FLAG)
.value_name("VALIDATOR_DIRECTORY")
.help(
"The path where the validator directories will be created. \
Defaults to ~/.lighthouse/validators",
)
.takes_value(true),
)
.arg( .arg(
Arg::with_name(STDIN_INPUTS_FLAG) Arg::with_name(STDIN_INPUTS_FLAG)
.long(STDIN_INPUTS_FLAG) .long(STDIN_INPUTS_FLAG)
@ -77,19 +66,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
) )
} }
pub fn cli_run(matches: &ArgMatches) -> Result<(), String> { pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), String> {
let keystore: Option<PathBuf> = clap_utils::parse_optional(matches, KEYSTORE_FLAG)?; let keystore: Option<PathBuf> = clap_utils::parse_optional(matches, KEYSTORE_FLAG)?;
let keystores_dir: Option<PathBuf> = clap_utils::parse_optional(matches, DIR_FLAG)?; let keystores_dir: Option<PathBuf> = clap_utils::parse_optional(matches, DIR_FLAG)?;
let validator_dir = clap_utils::parse_path_with_default_in_home_dir(
matches,
VALIDATOR_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("validators"),
)?;
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG); let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
let reuse_password = matches.is_present(REUSE_PASSWORD_FLAG); let reuse_password = matches.is_present(REUSE_PASSWORD_FLAG);
ensure_dir_exists(&validator_dir)?;
let mut defs = ValidatorDefinitions::open_or_create(&validator_dir) let mut defs = ValidatorDefinitions::open_or_create(&validator_dir)
.map_err(|e| format!("Unable to open {}: {:?}", CONFIG_FILENAME, e))?; .map_err(|e| format!("Unable to open {}: {:?}", CONFIG_FILENAME, e))?;

View File

@ -1,38 +1,21 @@
use crate::VALIDATOR_DIR_FLAG; use crate::VALIDATOR_DIR_FLAG;
use clap::{App, Arg, ArgMatches}; use clap::App;
use std::path::PathBuf; use std::path::PathBuf;
use validator_dir::Manager as ValidatorManager; use validator_dir::Manager as ValidatorManager;
pub const CMD: &str = "list"; pub const CMD: &str = "list";
pub fn cli_app<'a, 'b>() -> App<'a, 'b> { pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new(CMD) App::new(CMD).about("Lists the names of all validators.")
.arg(
Arg::with_name(VALIDATOR_DIR_FLAG)
.long(VALIDATOR_DIR_FLAG)
.value_name("VALIDATOR_DIRECTORY")
.help(
"The path to search for validator directories. \
Defaults to ~/.lighthouse/validators",
)
.takes_value(true),
)
.about("Lists the names of all validators.")
} }
pub fn cli_run(matches: &ArgMatches<'_>) -> Result<(), String> { pub fn cli_run(validator_dir: PathBuf) -> Result<(), String> {
let data_dir = clap_utils::parse_path_with_default_in_home_dir( let mgr = ValidatorManager::open(&validator_dir)
matches,
VALIDATOR_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("validators"),
)?;
let mgr = ValidatorManager::open(&data_dir)
.map_err(|e| format!("Unable to read --{}: {:?}", VALIDATOR_DIR_FLAG, e))?; .map_err(|e| format!("Unable to read --{}: {:?}", VALIDATOR_DIR_FLAG, e))?;
for (name, _path) in mgr for (name, _path) in mgr
.directory_names() .directory_names()
.map_err(|e| format!("Unable to list wallets: {:?}", e))? .map_err(|e| format!("Unable to list validators: {:?}", e))?
{ {
println!("{}", name) println!("{}", name)
} }

View File

@ -4,9 +4,11 @@ pub mod import;
pub mod list; pub mod list;
pub mod recover; pub mod recover;
use crate::common::base_wallet_dir; use crate::VALIDATOR_DIR_FLAG;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use directory::{parse_path_or_default_with_flag, DEFAULT_VALIDATOR_DIR};
use environment::Environment; use environment::Environment;
use std::path::PathBuf;
use types::EthSpec; use types::EthSpec;
pub const CMD: &str = "validator"; pub const CMD: &str = "validator";
@ -15,11 +17,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new(CMD) App::new(CMD)
.about("Provides commands for managing Eth2 validators.") .about("Provides commands for managing Eth2 validators.")
.arg( .arg(
Arg::with_name("base-dir") Arg::with_name(VALIDATOR_DIR_FLAG)
.long("base-dir") .long(VALIDATOR_DIR_FLAG)
.value_name("BASE_DIRECTORY") .value_name("VALIDATOR_DIRECTORY")
.help("A path containing Eth2 EIP-2386 wallets. Defaults to ~/.lighthouse/wallets") .help(
.takes_value(true), "The path to search for validator directories. \
Defaults to ~/.lighthouse/{testnet}/validators",
)
.takes_value(true)
.global(true)
.conflicts_with("datadir"),
) )
.subcommand(create::cli_app()) .subcommand(create::cli_app())
.subcommand(deposit::cli_app()) .subcommand(deposit::cli_app())
@ -29,14 +36,20 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
} }
pub fn cli_run<T: EthSpec>(matches: &ArgMatches, env: Environment<T>) -> Result<(), String> { pub fn cli_run<T: EthSpec>(matches: &ArgMatches, env: Environment<T>) -> Result<(), String> {
let base_wallet_dir = base_wallet_dir(matches, "base-dir")?; let validator_base_dir = if matches.value_of("datadir").is_some() {
let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
path.join(DEFAULT_VALIDATOR_DIR)
} else {
parse_path_or_default_with_flag(matches, VALIDATOR_DIR_FLAG, DEFAULT_VALIDATOR_DIR)?
};
eprintln!("validator-dir path: {:?}", validator_base_dir);
match matches.subcommand() { match matches.subcommand() {
(create::CMD, Some(matches)) => create::cli_run::<T>(matches, env, base_wallet_dir), (create::CMD, Some(matches)) => create::cli_run::<T>(matches, env, validator_base_dir),
(deposit::CMD, Some(matches)) => deposit::cli_run::<T>(matches, env), (deposit::CMD, Some(matches)) => deposit::cli_run::<T>(matches, env, validator_base_dir),
(import::CMD, Some(matches)) => import::cli_run(matches), (import::CMD, Some(matches)) => import::cli_run(matches, validator_base_dir),
(list::CMD, Some(matches)) => list::cli_run(matches), (list::CMD, Some(_)) => list::cli_run(validator_base_dir),
(recover::CMD, Some(matches)) => recover::cli_run(matches), (recover::CMD, Some(matches)) => recover::cli_run(matches, validator_base_dir),
(unknown, _) => Err(format!( (unknown, _) => Err(format!(
"{} does not have a {} command. See --help", "{} does not have a {} command. See --help",
CMD, unknown CMD, unknown

View File

@ -1,11 +1,13 @@
use super::create::STORE_WITHDRAW_FLAG; use super::create::STORE_WITHDRAW_FLAG;
use crate::common::{ensure_dir_exists, read_mnemonic_from_cli}; use crate::common::read_mnemonic_from_cli;
use crate::validator::create::COUNT_FLAG; use crate::validator::create::COUNT_FLAG;
use crate::wallet::create::STDIN_INPUTS_FLAG; use crate::wallet::create::STDIN_INPUTS_FLAG;
use crate::{SECRETS_DIR_FLAG, VALIDATOR_DIR_FLAG}; use crate::SECRETS_DIR_FLAG;
use account_utils::eth2_keystore::{keypair_from_secret, Keystore, KeystoreBuilder}; use account_utils::eth2_keystore::{keypair_from_secret, Keystore, KeystoreBuilder};
use account_utils::random_password; use account_utils::random_password;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use directory::ensure_dir_exists;
use directory::{parse_path_or_default_with_flag, DEFAULT_SECRET_DIR};
use eth2_wallet::bip39::Seed; use eth2_wallet::bip39::Seed;
use eth2_wallet::{recover_validator_secret_from_mnemonic, KeyType, ValidatorKeystores}; use eth2_wallet::{recover_validator_secret_from_mnemonic, KeyType, ValidatorKeystores};
use std::path::PathBuf; use std::path::PathBuf;
@ -48,23 +50,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
) )
.takes_value(true) .takes_value(true)
) )
.arg(
Arg::with_name(VALIDATOR_DIR_FLAG)
.long(VALIDATOR_DIR_FLAG)
.value_name("VALIDATOR_DIRECTORY")
.help(
"The path where the validator directories will be created. \
Defaults to ~/.lighthouse/validators",
)
.takes_value(true),
)
.arg( .arg(
Arg::with_name(SECRETS_DIR_FLAG) Arg::with_name(SECRETS_DIR_FLAG)
.long(SECRETS_DIR_FLAG) .long(SECRETS_DIR_FLAG)
.value_name("SECRETS_DIR") .value_name("SECRETS_DIR")
.help( .help(
"The path where the validator keystore passwords will be stored. \ "The path where the validator keystore passwords will be stored. \
Defaults to ~/.lighthouse/secrets", Defaults to ~/.lighthouse/{testnet}/secrets",
) )
.takes_value(true), .takes_value(true),
) )
@ -84,17 +76,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
) )
} }
pub fn cli_run(matches: &ArgMatches) -> Result<(), String> { pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), String> {
let validator_dir = clap_utils::parse_path_with_default_in_home_dir( let secrets_dir = if matches.value_of("datadir").is_some() {
matches, let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
VALIDATOR_DIR_FLAG, path.join(DEFAULT_SECRET_DIR)
PathBuf::new().join(".lighthouse").join("validators"), } else {
)?; parse_path_or_default_with_flag(matches, SECRETS_DIR_FLAG, DEFAULT_SECRET_DIR)?
let secrets_dir = clap_utils::parse_path_with_default_in_home_dir( };
matches,
SECRETS_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("secrets"),
)?;
let first_index: u32 = clap_utils::parse_required(matches, FIRST_INDEX_FLAG)?; let first_index: u32 = clap_utils::parse_required(matches, FIRST_INDEX_FLAG)?;
let count: u32 = clap_utils::parse_required(matches, COUNT_FLAG)?; let count: u32 = clap_utils::parse_required(matches, COUNT_FLAG)?;
let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?; let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;

View File

@ -1,5 +1,5 @@
use crate::common::read_wallet_name_from_cli; use crate::common::read_wallet_name_from_cli;
use crate::BASE_DIR_FLAG; use crate::WALLETS_DIR_FLAG;
use account_utils::{ use account_utils::{
is_password_sufficiently_complex, random_password, read_password_from_user, strip_off_newlines, is_password_sufficiently_complex, random_password, read_password_from_user, strip_off_newlines,
}; };
@ -80,7 +80,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
) )
} }
pub fn cli_run(matches: &ArgMatches, base_dir: PathBuf) -> Result<(), String> { pub fn cli_run(matches: &ArgMatches, wallet_base_dir: PathBuf) -> Result<(), String> {
let mnemonic_output_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?; let mnemonic_output_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;
// Create a new random mnemonic. // Create a new random mnemonic.
@ -88,7 +88,7 @@ pub fn cli_run(matches: &ArgMatches, base_dir: PathBuf) -> Result<(), String> {
// The `tiny-bip39` crate uses `thread_rng()` for this entropy. // The `tiny-bip39` crate uses `thread_rng()` for this entropy.
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
let wallet = create_wallet_from_mnemonic(matches, &base_dir.as_path(), &mnemonic)?; let wallet = create_wallet_from_mnemonic(matches, &wallet_base_dir.as_path(), &mnemonic)?;
if let Some(path) = mnemonic_output_path { if let Some(path) = mnemonic_output_path {
create_with_600_perms(&path, mnemonic.phrase().as_bytes()) create_with_600_perms(&path, mnemonic.phrase().as_bytes())
@ -121,7 +121,7 @@ pub fn cli_run(matches: &ArgMatches, base_dir: PathBuf) -> Result<(), String> {
pub fn create_wallet_from_mnemonic( pub fn create_wallet_from_mnemonic(
matches: &ArgMatches, matches: &ArgMatches,
base_dir: &Path, wallet_base_dir: &Path,
mnemonic: &Mnemonic, mnemonic: &Mnemonic,
) -> Result<LockedWallet, String> { ) -> Result<LockedWallet, String> {
let name: Option<String> = clap_utils::parse_optional(matches, NAME_FLAG)?; let name: Option<String> = clap_utils::parse_optional(matches, NAME_FLAG)?;
@ -134,8 +134,8 @@ pub fn create_wallet_from_mnemonic(
unknown => return Err(format!("--{} {} is not supported", TYPE_FLAG, unknown)), unknown => return Err(format!("--{} {} is not supported", TYPE_FLAG, unknown)),
}; };
let mgr = WalletManager::open(&base_dir) let mgr = WalletManager::open(&wallet_base_dir)
.map_err(|e| format!("Unable to open --{}: {:?}", BASE_DIR_FLAG, e))?; .map_err(|e| format!("Unable to open --{}: {:?}", WALLETS_DIR_FLAG, e))?;
let wallet_password: PlainText = match wallet_password_path { let wallet_password: PlainText = match wallet_password_path {
Some(path) => { Some(path) => {

View File

@ -1,4 +1,4 @@
use crate::BASE_DIR_FLAG; use crate::WALLETS_DIR_FLAG;
use clap::App; use clap::App;
use eth2_wallet_manager::WalletManager; use eth2_wallet_manager::WalletManager;
use std::path::PathBuf; use std::path::PathBuf;
@ -9,9 +9,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new(CMD).about("Lists the names of all wallets.") App::new(CMD).about("Lists the names of all wallets.")
} }
pub fn cli_run(base_dir: PathBuf) -> Result<(), String> { pub fn cli_run(wallet_base_dir: PathBuf) -> Result<(), String> {
let mgr = WalletManager::open(&base_dir) let mgr = WalletManager::open(&wallet_base_dir)
.map_err(|e| format!("Unable to open --{}: {:?}", BASE_DIR_FLAG, e))?; .map_err(|e| format!("Unable to open --{}: {:?}", WALLETS_DIR_FLAG, e))?;
for (name, _uuid) in mgr for (name, _uuid) in mgr
.wallets() .wallets()

View File

@ -2,11 +2,10 @@ pub mod create;
pub mod list; pub mod list;
pub mod recover; pub mod recover;
use crate::{ use crate::WALLETS_DIR_FLAG;
common::{base_wallet_dir, ensure_dir_exists},
BASE_DIR_FLAG,
};
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use directory::{ensure_dir_exists, parse_path_or_default_with_flag, DEFAULT_WALLET_DIR};
use std::path::PathBuf;
pub const CMD: &str = "wallet"; pub const CMD: &str = "wallet";
@ -14,11 +13,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new(CMD) App::new(CMD)
.about("Manage wallets, from which validator keys can be derived.") .about("Manage wallets, from which validator keys can be derived.")
.arg( .arg(
Arg::with_name(BASE_DIR_FLAG) Arg::with_name(WALLETS_DIR_FLAG)
.long(BASE_DIR_FLAG) .long(WALLETS_DIR_FLAG)
.value_name("BASE_DIRECTORY") .value_name("WALLETS_DIRECTORY")
.help("A path containing Eth2 EIP-2386 wallets. Defaults to ~/.lighthouse/wallets") .help("A path containing Eth2 EIP-2386 wallets. Defaults to ~/.lighthouse/{testnet}/wallets")
.takes_value(true), .takes_value(true)
.global(true)
.conflicts_with("datadir"),
) )
.subcommand(create::cli_app()) .subcommand(create::cli_app())
.subcommand(list::cli_app()) .subcommand(list::cli_app())
@ -26,13 +27,20 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
} }
pub fn cli_run(matches: &ArgMatches) -> Result<(), String> { pub fn cli_run(matches: &ArgMatches) -> Result<(), String> {
let base_dir = base_wallet_dir(matches, BASE_DIR_FLAG)?; let wallet_base_dir = if matches.value_of("datadir").is_some() {
ensure_dir_exists(&base_dir)?; let path: PathBuf = clap_utils::parse_required(matches, "datadir")?;
path.join(DEFAULT_WALLET_DIR)
} else {
parse_path_or_default_with_flag(matches, WALLETS_DIR_FLAG, DEFAULT_WALLET_DIR)?
};
ensure_dir_exists(&wallet_base_dir)?;
eprintln!("wallet-dir path: {:?}", wallet_base_dir);
match matches.subcommand() { match matches.subcommand() {
(create::CMD, Some(matches)) => create::cli_run(matches, base_dir), (create::CMD, Some(matches)) => create::cli_run(matches, wallet_base_dir),
(list::CMD, Some(_)) => list::cli_run(base_dir), (list::CMD, Some(_)) => list::cli_run(wallet_base_dir),
(recover::CMD, Some(matches)) => recover::cli_run(matches, base_dir), (recover::CMD, Some(matches)) => recover::cli_run(matches, wallet_base_dir),
(unknown, _) => Err(format!( (unknown, _) => Err(format!(
"{} does not have a {} command. See --help", "{} does not have a {} command. See --help",
CMD, unknown CMD, unknown

View File

@ -30,6 +30,7 @@ tokio = { version = "0.2.21", features = ["time"] }
exit-future = "0.2.0" exit-future = "0.2.0"
dirs = "2.0.2" dirs = "2.0.2"
logging = { path = "../common/logging" } logging = { path = "../common/logging" }
directory = {path = "../common/directory"}
futures = "0.3.5" futures = "0.3.5"
environment = { path = "../lighthouse/environment" } environment = { path = "../lighthouse/environment" }
genesis = { path = "genesis" } genesis = { path = "genesis" }

View File

@ -41,3 +41,4 @@ lazy_static = "1.4.0"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
time = "0.2.16" time = "0.2.16"
bus = "2.2.3" bus = "2.2.3"
directory = {path = "../../common/directory"}

View File

@ -1,11 +1,10 @@
use directory::DEFAULT_ROOT_DIR;
use network::NetworkConfig; use network::NetworkConfig;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use types::Graffiti; use types::Graffiti;
pub const DEFAULT_DATADIR: &str = ".lighthouse";
/// The number initial validators when starting the `Minimal`. /// The number initial validators when starting the `Minimal`.
const TESTNET_SPEC_CONSTANTS: &str = "minimal"; const TESTNET_SPEC_CONSTANTS: &str = "minimal";
@ -72,7 +71,7 @@ pub struct Config {
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
data_dir: PathBuf::from(DEFAULT_DATADIR), data_dir: PathBuf::from(DEFAULT_ROOT_DIR),
db_name: "chain_db".to_string(), db_name: "chain_db".to_string(),
freezer_db_path: None, freezer_db_path: None,
log_file: PathBuf::from(""), log_file: PathBuf::from(""),

View File

@ -36,6 +36,7 @@ discv5 = { version = "0.1.0-alpha.12", features = ["libp2p"] }
tiny-keccak = "2.0.2" tiny-keccak = "2.0.2"
environment = { path = "../../lighthouse/environment" } environment = { path = "../../lighthouse/environment" }
rand = "0.7.3" rand = "0.7.3"
directory = { path = "../../common/directory" }
regex = "1.3.9" regex = "1.3.9"
[dependencies.libp2p] [dependencies.libp2p]

View File

@ -1,5 +1,8 @@
use crate::types::GossipKind; use crate::types::GossipKind;
use crate::{Enr, PeerIdSerialized}; use crate::{Enr, PeerIdSerialized};
use directory::{
DEFAULT_BEACON_NODE_DIR, DEFAULT_HARDCODED_TESTNET, DEFAULT_NETWORK_DIR, DEFAULT_ROOT_DIR,
};
use discv5::{Discv5Config, Discv5ConfigBuilder}; use discv5::{Discv5Config, Discv5ConfigBuilder};
use libp2p::gossipsub::{ use libp2p::gossipsub::{
GossipsubConfig, GossipsubConfigBuilder, GossipsubMessage, MessageId, ValidationMode, GossipsubConfig, GossipsubConfigBuilder, GossipsubMessage, MessageId, ValidationMode,
@ -74,9 +77,14 @@ pub struct Config {
impl Default for Config { impl Default for Config {
/// Generate a default network configuration. /// Generate a default network configuration.
fn default() -> Self { fn default() -> Self {
let mut network_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); // WARNING: this directory default should be always overrided with parameters
network_dir.push(".lighthouse"); // from cli for specific networks.
network_dir.push("network"); let network_dir = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(DEFAULT_ROOT_DIR)
.join(DEFAULT_HARDCODED_TESTNET)
.join(DEFAULT_BEACON_NODE_DIR)
.join(DEFAULT_NETWORK_DIR);
// The function used to generate a gossipsub message id // The function used to generate a gossipsub message id
// We use the first 8 bytes of SHA256(data) for content addressing // We use the first 8 bytes of SHA256(data) for content addressing

View File

@ -1,7 +1,8 @@
use beacon_chain::builder::PUBKEY_CACHE_FILENAME; use beacon_chain::builder::PUBKEY_CACHE_FILENAME;
use clap::ArgMatches; use clap::ArgMatches;
use clap_utils::BAD_TESTNET_DIR_MESSAGE; use clap_utils::BAD_TESTNET_DIR_MESSAGE;
use client::{config::DEFAULT_DATADIR, ClientConfig, ClientGenesis}; use client::{ClientConfig, ClientGenesis};
use directory::{DEFAULT_BEACON_NODE_DIR, DEFAULT_NETWORK_DIR, DEFAULT_ROOT_DIR};
use eth2_libp2p::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, PeerIdSerialized}; use eth2_libp2p::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, PeerIdSerialized};
use eth2_testnet_config::Eth2TestnetConfig; use eth2_testnet_config::Eth2TestnetConfig;
use slog::{crit, info, warn, Logger}; use slog::{crit, info, warn, Logger};
@ -13,9 +14,6 @@ use std::net::{TcpListener, UdpSocket};
use std::path::PathBuf; use std::path::PathBuf;
use types::{ChainSpec, EthSpec, GRAFFITI_BYTES_LEN}; use types::{ChainSpec, EthSpec, GRAFFITI_BYTES_LEN};
pub const BEACON_NODE_DIR: &str = "beacon";
pub const NETWORK_DIR: &str = "network";
/// Gets the fully-initialized global client. /// Gets the fully-initialized global client.
/// ///
/// The top-level `clap` arguments should be provided as `cli_args`. /// The top-level `clap` arguments should be provided as `cli_args`.
@ -295,7 +293,7 @@ pub fn set_network_config(
if let Some(dir) = cli_args.value_of("network-dir") { if let Some(dir) = cli_args.value_of("network-dir") {
config.network_dir = PathBuf::from(dir); config.network_dir = PathBuf::from(dir);
} else { } else {
config.network_dir = data_dir.join(NETWORK_DIR); config.network_dir = data_dir.join(DEFAULT_NETWORK_DIR);
}; };
if let Some(listen_address_str) = cli_args.value_of("listen-address") { if let Some(listen_address_str) = cli_args.value_of("listen-address") {
@ -456,11 +454,18 @@ pub fn get_data_dir(cli_args: &ArgMatches) -> PathBuf {
// Read the `--datadir` flag. // Read the `--datadir` flag.
// //
// If it's not present, try and find the home directory (`~`) and push the default data // If it's not present, try and find the home directory (`~`) and push the default data
// directory onto it. // directory and the testnet name onto it.
cli_args cli_args
.value_of("datadir") .value_of("datadir")
.map(|path| PathBuf::from(path).join(BEACON_NODE_DIR)) .map(|path| PathBuf::from(path).join(DEFAULT_BEACON_NODE_DIR))
.or_else(|| dirs::home_dir().map(|home| home.join(DEFAULT_DATADIR).join(BEACON_NODE_DIR))) .or_else(|| {
dirs::home_dir().map(|home| {
home.join(DEFAULT_ROOT_DIR)
.join(directory::get_testnet_name(cli_args))
.join(DEFAULT_BEACON_NODE_DIR)
})
})
.unwrap_or_else(|| PathBuf::from(".")) .unwrap_or_else(|| PathBuf::from("."))
} }

View File

@ -40,12 +40,12 @@ keypairs. Creating a single validator looks like this:
- `lighthouse account validator create --wallet-name wally --wallet-password wally.pass --count 1` - `lighthouse account validator create --wallet-name wally --wallet-password wally.pass --count 1`
In step (1), we created a wallet in `~/.lighthouse/wallets` with the name In step (1), we created a wallet in `~/.lighthouse/{testnet}/wallets` with the name
`wally`. We encrypted this using a pre-defined password in the `wally`. We encrypted this using a pre-defined password in the
`wally.pass` file. Then, in step (2), we created one new validator in the `wally.pass` file. Then, in step (2), we created one new validator in the
`~/.lighthouse/validators` directory using `wally` (unlocking it with `~/.lighthouse/{testnet}/validators` directory using `wally` (unlocking it with
`wally.pass`) and storing the passwords to the validators voting key in `wally.pass`) and storing the passwords to the validators voting key in
`~/.lighthouse/secrets`. `~/.lighthouse/{testnet}/secrets`.
Thanks to the hierarchical key derivation scheme, we can delete all of the Thanks to the hierarchical key derivation scheme, we can delete all of the
aforementioned directories and then regenerate them as long as we remembered aforementioned directories and then regenerate them as long as we remembered
@ -63,14 +63,16 @@ There are three important directories in Lighthouse validator key management:
- `wallets/`: contains encrypted wallets which are used for hierarchical - `wallets/`: contains encrypted wallets which are used for hierarchical
key derivation. key derivation.
- Defaults to `~/.lighthouse/wallets` - Defaults to `~/.lighthouse/{testnet}/wallets`
- `validators/`: contains a directory for each validator containing - `validators/`: contains a directory for each validator containing
encrypted keystores and other validator-specific data. encrypted keystores and other validator-specific data.
- Defaults to `~/.lighthouse/validators` - Defaults to `~/.lighthouse/{testnet}/validators`
- `secrets/`: since the validator signing keys are "hot", the validator process - `secrets/`: since the validator signing keys are "hot", the validator process
needs access to the passwords to decrypt the keystores in the validators needs access to the passwords to decrypt the keystores in the validators
dir. These passwords are stored here. dir. These passwords are stored here.
- Defaults to `~/.lighthouse/secrets` - Defaults to `~/.lighthouse/{testnet}/secrets`
where `testnet` is the name of the testnet passed in the `--testnet` parameter (default is `medalla`).
When the validator client boots, it searches the `validators/` for directories When the validator client boots, it searches the `validators/` for directories
containing voting keystores. When it discovers a keystore, it searches the containing voting keystores. When it discovers a keystore, it searches the

View File

@ -41,7 +41,7 @@ OPTIONS:
The GWEI value of the deposit amount. Defaults to the minimum amount required for an active validator The GWEI value of the deposit amount. Defaults to the minimum amount required for an active validator
(MAX_EFFECTIVE_BALANCE) (MAX_EFFECTIVE_BALANCE)
--secrets-dir <SECRETS_DIR> --secrets-dir <SECRETS_DIR>
The path where the validator keystore passwords will be stored. Defaults to ~/.lighthouse/secrets The path where the validator keystore passwords will be stored. Defaults to ~/.lighthouse/{testnet}/secrets
-s, --spec <TITLE> -s, --spec <TITLE>
Specifies the default eth2 spec type. [default: mainnet] [possible values: mainnet, minimal, interop] Specifies the default eth2 spec type. [default: mainnet] [possible values: mainnet, minimal, interop]
@ -53,7 +53,7 @@ OPTIONS:
Path to directory containing eth2_testnet specs. Defaults to a hard-coded Lighthouse testnet. Only effective Path to directory containing eth2_testnet specs. Defaults to a hard-coded Lighthouse testnet. Only effective
if there is no existing database. if there is no existing database.
--validator-dir <VALIDATOR_DIRECTORY> --validator-dir <VALIDATOR_DIRECTORY>
The path where the validator directories will be created. Defaults to ~/.lighthouse/validators The path where the validator directories will be created. Defaults to ~/.lighthouse/{testnet}/validators
--wallet-name <WALLET_NAME> Use the wallet identified by this name --wallet-name <WALLET_NAME> Use the wallet identified by this name
--wallet-password <WALLET_PASSWORD_PATH> --wallet-password <WALLET_PASSWORD_PATH>
@ -73,10 +73,12 @@ This command will:
- Derive a single new BLS keypair from `wally`, updating it so that it generates a - Derive a single new BLS keypair from `wally`, updating it so that it generates a
new key next time. new key next time.
- Create a new directory in `~/.lighthouse/validators` containing: - Create a new directory in `~/.lighthouse/{testnet}/validators` containing:
- An encrypted keystore containing the validators voting keypair. - An encrypted keystore containing the validators voting keypair.
- An `eth1_deposit_data.rlp` assuming the default deposit amount (`32 ETH` - An `eth1_deposit_data.rlp` assuming the default deposit amount (`32 ETH`
for most testnets and mainnet) which can be submitted to the deposit for most testnets and mainnet) which can be submitted to the deposit
contract for the medalla testnet. Other testnets can be set via the contract for the medalla testnet. Other testnets can be set via the
`--testnet` CLI param. `--testnet` CLI param.
- Store a password to the validators voting keypair in `~/.lighthouse/secrets`. - Store a password to the validators voting keypair in `~/.lighthouse/{testnet}/secrets`.
where `testnet` is the name of the testnet passed in the `--testnet` parameter (default is `medalla`).

View File

@ -16,7 +16,7 @@ useful.
## Introducing the `validator_definitions.yml` file ## Introducing the `validator_definitions.yml` file
The `validator_definitions.yml` file is located in the `validator-dir`, which The `validator_definitions.yml` file is located in the `validator-dir`, which
defaults to `~/.lighthouse/validators`. It is a defaults to `~/.lighthouse/{testnet}/validators`. It is a
[YAML](https://en.wikipedia.org/wiki/YAML) encoded file defining exactly which [YAML](https://en.wikipedia.org/wiki/YAML) encoded file defining exactly which
validators the validator client will (and won't) act for. validators the validator client will (and won't) act for.
@ -92,7 +92,7 @@ name identical to the `voting_public_key` value.
Lets assume the following directory structure: Lets assume the following directory structure:
``` ```
~/.lighthouse/validators ~/.lighthouse/{testnet}/validators
├── john ├── john
│   └── voting-keystore.json │   └── voting-keystore.json
├── sally ├── sally
@ -135,7 +135,7 @@ In order for the validator client to decrypt the validators, they will need to
ensure their `secrets-dir` is organised as below: ensure their `secrets-dir` is organised as below:
``` ```
~/.lighthouse/secrets ~/.lighthouse/{testnet}/secrets
├── 0xa5566f9ec3c6e1fdf362634ebec9ef7aceb0e460e5079714808388e5d48f4ae1e12897fed1bea951c17fa389d511e477 ├── 0xa5566f9ec3c6e1fdf362634ebec9ef7aceb0e460e5079714808388e5d48f4ae1e12897fed1bea951c17fa389d511e477
├── 0xaa440c566fcf34dedf233baf56cf5fb05bb420d9663b4208272545608c27c13d5b08174518c758ecd814f158f2b4a337 ├── 0xaa440c566fcf34dedf233baf56cf5fb05bb420d9663b4208272545608c27c13d5b08174518c758ecd814f158f2b4a337
└── 0x87a580d31d7bc69069b55f5a01995a610dd391a26dc9e36e81057a17211983a79266800ab8531f21f1083d7d84085007 └── 0x87a580d31d7bc69069b55f5a01995a610dd391a26dc9e36e81057a17211983a79266800ab8531f21f1083d7d84085007

View File

@ -0,0 +1,13 @@
[package]
name = "directory"
version = "0.1.0"
authors = ["pawan <pawandhananjay@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = "2.33.0"
clap_utils = {path = "../clap_utils"}
dirs = "2.0.2"
eth2_testnet_config = { path = "../eth2_testnet_config" }

View File

@ -0,0 +1,60 @@
use clap::ArgMatches;
pub use eth2_testnet_config::DEFAULT_HARDCODED_TESTNET;
use std::fs::create_dir_all;
use std::path::{Path, PathBuf};
/// Names for the default directories.
pub const DEFAULT_ROOT_DIR: &str = ".lighthouse";
pub const DEFAULT_BEACON_NODE_DIR: &str = "beacon";
pub const DEFAULT_NETWORK_DIR: &str = "network";
pub const DEFAULT_VALIDATOR_DIR: &str = "validators";
pub const DEFAULT_SECRET_DIR: &str = "secrets";
pub const DEFAULT_WALLET_DIR: &str = "wallets";
/// Base directory name for unnamed testnets passed through the --testnet-dir flag
pub const CUSTOM_TESTNET_DIR: &str = "custom";
/// Gets the testnet directory name
///
/// Tries to get the name first from the "testnet" flag,
/// if not present, then checks the "testnet-dir" flag and returns a custom name
/// If neither flags are present, returns the default hardcoded network name.
pub fn get_testnet_name(matches: &ArgMatches) -> String {
if let Some(testnet_name) = matches.value_of("testnet") {
testnet_name.to_string()
} else if matches.value_of("testnet-dir").is_some() {
CUSTOM_TESTNET_DIR.to_string()
} else {
eth2_testnet_config::DEFAULT_HARDCODED_TESTNET.to_string()
}
}
/// Checks if a directory exists in the given path and creates a directory if it does not exist.
pub fn ensure_dir_exists<P: AsRef<Path>>(path: P) -> Result<(), String> {
let path = path.as_ref();
if !path.exists() {
create_dir_all(path).map_err(|e| format!("Unable to create {:?}: {:?}", path, e))?;
}
Ok(())
}
/// If `arg` is in `matches`, parses the value as a path.
///
/// Otherwise, attempts to find the default directory for the `testnet` from the `matches`
/// and appends `flag` to it.
pub fn parse_path_or_default_with_flag(
matches: &ArgMatches,
arg: &'static str,
flag: &str,
) -> Result<PathBuf, String> {
clap_utils::parse_path_with_default_in_home_dir(
matches,
arg,
PathBuf::new()
.join(DEFAULT_ROOT_DIR)
.join(get_testnet_name(matches))
.join(flag),
)
}

View File

@ -12,7 +12,6 @@ harness = false
bls = { path = "../../crypto/bls" } bls = { path = "../../crypto/bls" }
compare_fields = { path = "../../common/compare_fields" } compare_fields = { path = "../../common/compare_fields" }
compare_fields_derive = { path = "../../common/compare_fields_derive" } compare_fields_derive = { path = "../../common/compare_fields_derive" }
dirs = "2.0.2"
eth2_interop_keypairs = { path = "../../common/eth2_interop_keypairs" } eth2_interop_keypairs = { path = "../../common/eth2_interop_keypairs" }
ethereum-types = "0.9.1" ethereum-types = "0.9.1"
eth2_hashing = "0.1.0" eth2_hashing = "0.1.0"

View File

@ -4,21 +4,9 @@ use crate::*;
use bls::get_withdrawal_credentials; use bls::get_withdrawal_credentials;
use log::debug; use log::debug;
use rayon::prelude::*; use rayon::prelude::*;
use std::path::PathBuf;
pub const KEYPAIRS_FILE: &str = "keypairs.raw_keypairs"; pub const KEYPAIRS_FILE: &str = "keypairs.raw_keypairs";
/// Returns the directory where the generated keypairs should be stored.
///
/// It is either `$HOME/.lighthouse/keypairs.raw_keypairs` or, if `$HOME` is not available,
/// `./keypairs.raw_keypairs`.
pub fn keypairs_path() -> PathBuf {
let dir = dirs::home_dir()
.map(|home| (home.join(".lighthouse")))
.unwrap_or_else(|| PathBuf::from(""));
dir.join(KEYPAIRS_FILE)
}
/// Builds a beacon state to be used for testing purposes. /// Builds a beacon state to be used for testing purposes.
/// ///
/// This struct should **never be used for production purposes.** /// This struct should **never be used for production purposes.**

View File

@ -35,3 +35,4 @@ validator_dir = { path = "../common/validator_dir", features = ["insecure_keys"]
rand = "0.7.2" rand = "0.7.2"
eth2_keystore = { path = "../crypto/eth2_keystore" } eth2_keystore = { path = "../crypto/eth2_keystore" }
lighthouse_version = { path = "../common/lighthouse_version" } lighthouse_version = { path = "../common/lighthouse_version" }
directory = { path = "../common/directory" }

View File

@ -20,7 +20,7 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches<'_>) -> Res
.and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ())) .and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ()))
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
dirs::home_dir() dirs::home_dir()
.map(|home| home.join(".lighthouse").join("testnet")) .map(|home| home.join(directory::DEFAULT_ROOT_DIR).join("testnet"))
.expect("should locate home directory") .expect("should locate home directory")
}); });

View File

@ -31,7 +31,7 @@ pub fn run<T: EthSpec>(mut env: Environment<T>, matches: &ArgMatches) -> Result<
.and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ())) .and_then(|dir| dir.parse::<PathBuf>().map_err(|_| ()))
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
dirs::home_dir() dirs::home_dir()
.map(|home| home.join(".lighthouse").join("testnet")) .map(|home| home.join(directory::DEFAULT_ROOT_DIR).join("testnet"))
.expect("should locate home directory") .expect("should locate home directory")
}); });

View File

@ -10,7 +10,7 @@ pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
let testnet_dir_path = parse_path_with_default_in_home_dir( let testnet_dir_path = parse_path_with_default_in_home_dir(
matches, matches,
"testnet-dir", "testnet-dir",
PathBuf::from(".lighthouse/testnet"), PathBuf::from(directory::DEFAULT_ROOT_DIR).join("testnet"),
)?; )?;
let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?; let deposit_contract_address: Address = parse_required(matches, "deposit-contract-address")?;
let deposit_contract_deploy_block = parse_required(matches, "deposit-contract-deploy-block")?; let deposit_contract_deploy_block = parse_required(matches, "deposit-contract-deploy-block")?;

View File

@ -31,9 +31,10 @@ validator_client = { "path" = "../validator_client" }
account_manager = { "path" = "../account_manager" } account_manager = { "path" = "../account_manager" }
clap_utils = { path = "../common/clap_utils" } clap_utils = { path = "../common/clap_utils" }
eth2_testnet_config = { path = "../common/eth2_testnet_config" } eth2_testnet_config = { path = "../common/eth2_testnet_config" }
directory = { path = "../common/directory" }
lighthouse_version = { path = "../common/lighthouse_version" } lighthouse_version = { path = "../common/lighthouse_version" }
account_utils = { path = "../common/account_utils" }
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = "3.1.0"
validator_dir = { path = "../common/validator_dir" } validator_dir = { path = "../common/validator_dir" }
account_utils = { path = "../common/account_utils" }

View File

@ -10,7 +10,6 @@ use std::process::exit;
use types::EthSpec; use types::EthSpec;
use validator_client::ProductionValidatorClient; use validator_client::ProductionValidatorClient;
pub const DEFAULT_DATA_DIR: &str = ".lighthouse";
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
fn bls_library_name() -> &'static str { fn bls_library_name() -> &'static str {
@ -91,7 +90,10 @@ fn main() {
.short("d") .short("d")
.value_name("DIR") .value_name("DIR")
.global(true) .global(true)
.help("Data directory for lighthouse keys and databases.") .help(
"Root data directory for lighthouse keys and databases. \
Defaults to $HOME/.lighthouse/{default-testnet}, \
currently, $HOME/.lighthouse/medalla")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(

View File

@ -11,7 +11,7 @@ use account_manager::{
list::CMD as LIST_CMD, list::CMD as LIST_CMD,
CMD as WALLET_CMD, CMD as WALLET_CMD,
}, },
BASE_DIR_FLAG, CMD as ACCOUNT_CMD, *, CMD as ACCOUNT_CMD, WALLETS_DIR_FLAG, *,
}; };
use account_utils::{ use account_utils::{
eth2_keystore::KeystoreBuilder, eth2_keystore::KeystoreBuilder,
@ -73,7 +73,7 @@ fn dir_child_count<P: AsRef<Path>>(dir: P) -> usize {
fn list_wallets<P: AsRef<Path>>(base_dir: P) -> Vec<String> { fn list_wallets<P: AsRef<Path>>(base_dir: P) -> Vec<String> {
let output = output_result( let output = output_result(
wallet_cmd() wallet_cmd()
.arg(format!("--{}", BASE_DIR_FLAG)) .arg(format!("--{}", WALLETS_DIR_FLAG))
.arg(base_dir.as_ref().as_os_str()) .arg(base_dir.as_ref().as_os_str())
.arg(LIST_CMD), .arg(LIST_CMD),
) )
@ -97,7 +97,7 @@ fn create_wallet<P: AsRef<Path>>(
) -> Result<Output, String> { ) -> Result<Output, String> {
output_result( output_result(
wallet_cmd() wallet_cmd()
.arg(format!("--{}", BASE_DIR_FLAG)) .arg(format!("--{}", WALLETS_DIR_FLAG))
.arg(base_dir.as_ref().as_os_str()) .arg(base_dir.as_ref().as_os_str())
.arg(CREATE_CMD) .arg(CREATE_CMD)
.arg(format!("--{}", NAME_FLAG)) .arg(format!("--{}", NAME_FLAG))
@ -233,15 +233,15 @@ impl TestValidator {
store_withdrawal_key: bool, store_withdrawal_key: bool,
) -> Result<Vec<String>, String> { ) -> Result<Vec<String>, String> {
let mut cmd = validator_cmd(); let mut cmd = validator_cmd();
cmd.arg(format!("--{}", BASE_DIR_FLAG)) cmd.arg(format!("--{}", VALIDATOR_DIR_FLAG))
.arg(self.wallet.base_dir().into_os_string()) .arg(self.validator_dir.clone().into_os_string())
.arg(CREATE_CMD) .arg(CREATE_CMD)
.arg(format!("--{}", WALLETS_DIR_FLAG))
.arg(self.wallet.base_dir().into_os_string())
.arg(format!("--{}", WALLET_NAME_FLAG)) .arg(format!("--{}", WALLET_NAME_FLAG))
.arg(&self.wallet.name) .arg(&self.wallet.name)
.arg(format!("--{}", WALLET_PASSWORD_FLAG)) .arg(format!("--{}", WALLET_PASSWORD_FLAG))
.arg(self.wallet.password_path().into_os_string()) .arg(self.wallet.password_path().into_os_string())
.arg(format!("--{}", VALIDATOR_DIR_FLAG))
.arg(self.validator_dir.clone().into_os_string())
.arg(format!("--{}", SECRETS_DIR_FLAG)) .arg(format!("--{}", SECRETS_DIR_FLAG))
.arg(self.secrets_dir.clone().into_os_string()) .arg(self.secrets_dir.clone().into_os_string())
.arg(format!("--{}", DEPOSIT_GWEI_FLAG)) .arg(format!("--{}", DEPOSIT_GWEI_FLAG))
@ -375,13 +375,6 @@ fn validator_create() {
assert_eq!(dir_child_count(validator_dir.path()), 6); assert_eq!(dir_child_count(validator_dir.path()), 6);
} }
/// Returns the `lighthouse account validator import` command.
fn validator_import_cmd() -> Command {
let mut cmd = validator_cmd();
cmd.arg(IMPORT_CMD);
cmd
}
#[test] #[test]
fn validator_import_launchpad() { fn validator_import_launchpad() {
const PASSWORD: &str = "cats"; const PASSWORD: &str = "cats";
@ -407,12 +400,13 @@ fn validator_import_launchpad() {
// Create a not-keystore file in the src dir. // Create a not-keystore file in the src dir.
File::create(src_dir.path().join(NOT_KEYSTORE_NAME)).unwrap(); File::create(src_dir.path().join(NOT_KEYSTORE_NAME)).unwrap();
let mut child = validator_import_cmd() let mut child = validator_cmd()
.arg(format!("--{}", VALIDATOR_DIR_FLAG))
.arg(dst_dir.path().as_os_str())
.arg(IMPORT_CMD)
.arg(format!("--{}", STDIN_INPUTS_FLAG)) // Using tty does not work well with tests. .arg(format!("--{}", STDIN_INPUTS_FLAG)) // Using tty does not work well with tests.
.arg(format!("--{}", import::DIR_FLAG)) .arg(format!("--{}", import::DIR_FLAG))
.arg(src_dir.path().as_os_str()) .arg(src_dir.path().as_os_str())
.arg(format!("--{}", VALIDATOR_DIR_FLAG))
.arg(dst_dir.path().as_os_str())
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.spawn() .spawn()

View File

@ -96,7 +96,7 @@ pub fn testing_client_config() -> ClientConfig {
/// This struct is separate to `LocalValidatorClient` to allow for pre-computation of validator /// This struct is separate to `LocalValidatorClient` to allow for pre-computation of validator
/// keypairs since the task is quite resource intensive. /// keypairs since the task is quite resource intensive.
pub struct ValidatorFiles { pub struct ValidatorFiles {
pub datadir: TempDir, pub validator_dir: TempDir,
pub secrets_dir: TempDir, pub secrets_dir: TempDir,
} }
@ -110,7 +110,7 @@ impl ValidatorFiles {
.map_err(|e| format!("Unable to create VC secrets dir: {:?}", e))?; .map_err(|e| format!("Unable to create VC secrets dir: {:?}", e))?;
Ok(Self { Ok(Self {
datadir, validator_dir: datadir,
secrets_dir, secrets_dir,
}) })
} }
@ -120,7 +120,7 @@ impl ValidatorFiles {
let this = Self::new()?; let this = Self::new()?;
build_deterministic_validator_dirs( build_deterministic_validator_dirs(
this.datadir.path().into(), this.validator_dir.path().into(),
this.secrets_dir.path().into(), this.secrets_dir.path().into(),
keypair_indices, keypair_indices,
) )
@ -170,7 +170,7 @@ impl<E: EthSpec> LocalValidatorClient<E> {
mut config: ValidatorConfig, mut config: ValidatorConfig,
files: ValidatorFiles, files: ValidatorFiles,
) -> Result<Self, String> { ) -> Result<Self, String> {
config.data_dir = files.datadir.path().into(); config.validator_dir = files.validator_dir.path().into();
config.secrets_dir = files.secrets_dir.path().into(); config.secrets_dir = files.secrets_dir.path().into();
ProductionValidatorClient::new(context, config) ProductionValidatorClient::new(context, config)

View File

@ -31,6 +31,7 @@ slog-term = "2.5.0"
tokio = { version = "0.2.21", features = ["time"] } tokio = { version = "0.2.21", features = ["time"] }
futures = { version = "0.3.5", features = ["compat"] } futures = { version = "0.3.5", features = ["compat"] }
dirs = "2.0.2" dirs = "2.0.2"
directory = {path = "../common/directory"}
logging = { path = "../common/logging" } logging = { path = "../common/logging" }
environment = { path = "../lighthouse/environment" } environment = { path = "../lighthouse/environment" }
parking_lot = "0.11.0" parking_lot = "0.11.0"

View File

@ -16,6 +16,19 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.default_value(&DEFAULT_HTTP_SERVER) .default_value(&DEFAULT_HTTP_SERVER)
.takes_value(true), .takes_value(true),
) )
.arg(
Arg::with_name("validators-dir")
.long("validators-dir")
.value_name("VALIDATORS_DIR")
.help(
"The directory which contains the validator keystores, deposit data for \
each validator along with the common slashing protection database \
and the validator_definitions.yml"
)
.takes_value(true)
.conflicts_with("datadir")
.requires("secrets-dir")
)
.arg( .arg(
Arg::with_name("secrets-dir") Arg::with_name("secrets-dir")
.long("secrets-dir") .long("secrets-dir")
@ -24,9 +37,11 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
"The directory which contains the password to unlock the validator \ "The directory which contains the password to unlock the validator \
voting keypairs. Each password should be contained in a file where the \ voting keypairs. Each password should be contained in a file where the \
name is the 0x-prefixed hex representation of the validators voting public \ name is the 0x-prefixed hex representation of the validators voting public \
key. Defaults to ~/.lighthouse/secrets.", key. Defaults to ~/.lighthouse/{testnet}/secrets.",
) )
.takes_value(true), .takes_value(true)
.conflicts_with("datadir")
.requires("validators-dir"),
) )
.arg(Arg::with_name("auto-register").long("auto-register").help( .arg(Arg::with_name("auto-register").long("auto-register").help(
"If present, the validator client will register any new signing keys with \ "If present, the validator client will register any new signing keys with \
@ -48,6 +63,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
that might also be using the same keystores." that might also be using the same keystores."
) )
) )
.arg(
Arg::with_name("strict-slashing-protection")
.long("strict-slashing-protection")
.help(
"If present, do not create a new slashing database. This is to ensure that users \
do not accidentally get slashed in case their slashing protection db ends up in the \
wrong directory during directory restructure and vc creates a new empty db and \
re-registers all validators."
)
)
.arg( .arg(
Arg::with_name("disable-auto-discover") Arg::with_name("disable-auto-discover")
.long("disable-auto-discover") .long("disable-auto-discover")

View File

@ -1,12 +1,14 @@
use clap::ArgMatches; use clap::ArgMatches;
use clap_utils::{parse_optional, parse_path_with_default_in_home_dir}; use clap_utils::{parse_optional, parse_required};
use directory::{
get_testnet_name, DEFAULT_HARDCODED_TESTNET, DEFAULT_ROOT_DIR, DEFAULT_SECRET_DIR,
DEFAULT_VALIDATOR_DIR,
};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
use types::{Graffiti, GRAFFITI_BYTES_LEN}; use types::{Graffiti, GRAFFITI_BYTES_LEN};
pub const DEFAULT_HTTP_SERVER: &str = "http://localhost:5052/"; pub const DEFAULT_HTTP_SERVER: &str = "http://localhost:5052/";
pub const DEFAULT_DATA_DIR: &str = ".lighthouse/validators";
pub const DEFAULT_SECRETS_DIR: &str = ".lighthouse/secrets";
/// Path to the slashing protection database within the datadir. /// Path to the slashing protection database within the datadir.
pub const SLASHING_PROTECTION_FILENAME: &str = "slashing_protection.sqlite"; pub const SLASHING_PROTECTION_FILENAME: &str = "slashing_protection.sqlite";
@ -14,7 +16,7 @@ pub const SLASHING_PROTECTION_FILENAME: &str = "slashing_protection.sqlite";
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct Config { pub struct Config {
/// The data directory, which stores all validator databases /// The data directory, which stores all validator databases
pub data_dir: PathBuf, pub validator_dir: PathBuf,
/// The directory containing the passwords to unlock validator keystores. /// The directory containing the passwords to unlock validator keystores.
pub secrets_dir: PathBuf, pub secrets_dir: PathBuf,
/// The http endpoint of the beacon node API. /// The http endpoint of the beacon node API.
@ -28,6 +30,8 @@ pub struct Config {
pub delete_lockfiles: bool, pub delete_lockfiles: bool,
/// If true, don't scan the validators dir for new keystores. /// If true, don't scan the validators dir for new keystores.
pub disable_auto_discover: bool, pub disable_auto_discover: bool,
/// If true, don't re-register existing validators in definitions.yml for slashing protection.
pub strict_slashing_protection: bool,
/// Graffiti to be inserted everytime we create a block. /// Graffiti to be inserted everytime we create a block.
pub graffiti: Option<Graffiti>, pub graffiti: Option<Graffiti>,
} }
@ -35,19 +39,22 @@ pub struct Config {
impl Default for Config { impl Default for Config {
/// Build a new configuration from defaults. /// Build a new configuration from defaults.
fn default() -> Self { fn default() -> Self {
let data_dir = dirs::home_dir() // WARNING: these directory defaults should be always overrided with parameters
.map(|home| home.join(DEFAULT_DATA_DIR)) // from cli for specific networks.
.unwrap_or_else(|| PathBuf::from(".")); let base_dir = dirs::home_dir()
let secrets_dir = dirs::home_dir() .unwrap_or_else(|| PathBuf::from("."))
.map(|home| home.join(DEFAULT_SECRETS_DIR)) .join(DEFAULT_ROOT_DIR)
.unwrap_or_else(|| PathBuf::from(".")); .join(DEFAULT_HARDCODED_TESTNET);
let validator_dir = base_dir.join(DEFAULT_VALIDATOR_DIR);
let secrets_dir = base_dir.join(DEFAULT_SECRET_DIR);
Self { Self {
data_dir, validator_dir,
secrets_dir, secrets_dir,
http_server: DEFAULT_HTTP_SERVER.to_string(), http_server: DEFAULT_HTTP_SERVER.to_string(),
allow_unsynced_beacon_node: false, allow_unsynced_beacon_node: false,
delete_lockfiles: false, delete_lockfiles: false,
disable_auto_discover: false, disable_auto_discover: false,
strict_slashing_protection: false,
graffiti: None, graffiti: None,
} }
} }
@ -59,16 +66,39 @@ impl Config {
pub fn from_cli(cli_args: &ArgMatches) -> Result<Config, String> { pub fn from_cli(cli_args: &ArgMatches) -> Result<Config, String> {
let mut config = Config::default(); let mut config = Config::default();
config.data_dir = parse_path_with_default_in_home_dir( let default_root_dir = dirs::home_dir()
cli_args, .map(|home| home.join(DEFAULT_ROOT_DIR))
"datadir", .unwrap_or_else(|| PathBuf::from("."));
PathBuf::from(".lighthouse").join("validators"),
)?;
if !config.data_dir.exists() { let (mut validator_dir, mut secrets_dir) = (None, None);
if cli_args.value_of("datadir").is_some() {
let base_dir: PathBuf = parse_required(cli_args, "datadir")?;
validator_dir = Some(base_dir.join(DEFAULT_VALIDATOR_DIR));
secrets_dir = Some(base_dir.join(DEFAULT_SECRET_DIR));
}
if cli_args.value_of("validators-dir").is_some()
&& cli_args.value_of("secrets-dir").is_some()
{
validator_dir = Some(parse_required(cli_args, "validators-dir")?);
secrets_dir = Some(parse_required(cli_args, "secrets-dir")?);
}
config.validator_dir = validator_dir.unwrap_or_else(|| {
default_root_dir
.join(get_testnet_name(cli_args))
.join(DEFAULT_VALIDATOR_DIR)
});
config.secrets_dir = secrets_dir.unwrap_or_else(|| {
default_root_dir
.join(get_testnet_name(cli_args))
.join(DEFAULT_SECRET_DIR)
});
if !config.validator_dir.exists() {
return Err(format!( return Err(format!(
"The directory for validator data (--datadir) does not exist: {:?}", "The directory for validator data does not exist: {:?}",
config.data_dir config.validator_dir
)); ));
} }
@ -79,10 +109,7 @@ impl Config {
config.allow_unsynced_beacon_node = cli_args.is_present("allow-unsynced"); config.allow_unsynced_beacon_node = cli_args.is_present("allow-unsynced");
config.delete_lockfiles = cli_args.is_present("delete-lockfiles"); config.delete_lockfiles = cli_args.is_present("delete-lockfiles");
config.disable_auto_discover = cli_args.is_present("disable-auto-discover"); config.disable_auto_discover = cli_args.is_present("disable-auto-discover");
config.strict_slashing_protection = cli_args.is_present("strict-slashing-protection");
if let Some(secrets_dir) = parse_optional(cli_args, "secrets-dir")? {
config.secrets_dir = secrets_dir;
}
if let Some(input_graffiti) = cli_args.value_of("graffiti") { if let Some(input_graffiti) = cli_args.value_of("graffiti") {
let graffiti_bytes = input_graffiti.as_bytes(); let graffiti_bytes = input_graffiti.as_bytes();

View File

@ -68,18 +68,18 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
log, log,
"Starting validator client"; "Starting validator client";
"beacon_node" => &config.http_server, "beacon_node" => &config.http_server,
"datadir" => format!("{:?}", config.data_dir), "validator_dir" => format!("{:?}", config.validator_dir),
); );
let mut validator_defs = ValidatorDefinitions::open_or_create(&config.data_dir) let mut validator_defs = ValidatorDefinitions::open_or_create(&config.validator_dir)
.map_err(|e| format!("Unable to open or create validator definitions: {:?}", e))?; .map_err(|e| format!("Unable to open or create validator definitions: {:?}", e))?;
if !config.disable_auto_discover { if !config.disable_auto_discover {
let new_validators = validator_defs let new_validators = validator_defs
.discover_local_keystores(&config.data_dir, &config.secrets_dir, &log) .discover_local_keystores(&config.validator_dir, &config.secrets_dir, &log)
.map_err(|e| format!("Unable to discover local validator keystores: {:?}", e))?; .map_err(|e| format!("Unable to discover local validator keystores: {:?}", e))?;
validator_defs validator_defs
.save(&config.data_dir) .save(&config.validator_dir)
.map_err(|e| format!("Unable to update validator definitions: {:?}", e))?; .map_err(|e| format!("Unable to update validator definitions: {:?}", e))?;
info!( info!(
log, log,
@ -90,7 +90,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
let validators = InitializedValidators::from_definitions( let validators = InitializedValidators::from_definitions(
validator_defs, validator_defs,
config.data_dir.clone(), config.validator_dir.clone(),
config.delete_lockfiles, config.delete_lockfiles,
log.clone(), log.clone(),
) )

View File

@ -62,14 +62,24 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
fork_service: ForkService<T, E>, fork_service: ForkService<T, E>,
log: Logger, log: Logger,
) -> Result<Self, String> { ) -> Result<Self, String> {
let slashing_db_path = config.data_dir.join(SLASHING_PROTECTION_FILENAME); let slashing_db_path = config.validator_dir.join(SLASHING_PROTECTION_FILENAME);
let slashing_protection = let slashing_protection = if config.strict_slashing_protection {
// Don't create a new slashing database if `strict_slashing_protection` is turned on.
SlashingDatabase::open(&slashing_db_path).map_err(|e| {
format!(
"Failed to open slashing protection database: {:?}.
Ensure that `slashing_protection.sqlite` is in {:?} folder",
e, config.validator_dir
)
})?
} else {
SlashingDatabase::open_or_create(&slashing_db_path).map_err(|e| { SlashingDatabase::open_or_create(&slashing_db_path).map_err(|e| {
format!( format!(
"Failed to open or create slashing protection database: {:?}", "Failed to open or create slashing protection database: {:?}",
e e
) )
})?; })?
};
Ok(Self { Ok(Self {
validators: Arc::new(RwLock::new(validators)), validators: Arc::new(RwLock::new(validators)),