Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Interactive account passwords #1623

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions account_manager/src/common.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use account_utils::PlainText;
use account_utils::{read_mnemonic_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 std::fs;
Expand All @@ -10,6 +10,7 @@ use std::thread::sleep;
use std::time::Duration;

pub const MNEMONIC_PROMPT: &str = "Enter the mnemonic phrase:";
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();
Expand All @@ -29,9 +30,11 @@ pub fn base_wallet_dir(matches: &ArgMatches, arg: &'static str) -> Result<PathBu
)
}

/// 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(
mnemonic_path: Option<PathBuf>,
stdin_password: bool,
stdin_inputs: bool,
) -> Result<Mnemonic, String> {
let mnemonic = match mnemonic_path {
Some(path) => fs::read(&path)
Expand All @@ -51,7 +54,7 @@ pub fn read_mnemonic_from_cli(
eprintln!("");
eprintln!("{}", MNEMONIC_PROMPT);

let mnemonic = read_mnemonic_from_user(stdin_password)?;
let mnemonic = read_input_from_user(stdin_inputs)?;

match Mnemonic::from_phrase(mnemonic.as_str(), Language::English) {
Ok(mnemonic_m) => {
Expand All @@ -68,3 +71,19 @@ pub fn read_mnemonic_from_cli(
};
Ok(mnemonic)
}

/// Reads in a wallet name from the user. If the `--wallet-name` flag is provided, use it. Otherwise
/// read from an interactive prompt using tty unless the `--stdin-inputs` flag is provided.
pub fn read_wallet_name_from_cli(
wallet_name: Option<String>,
stdin_inputs: bool,
) -> Result<String, String> {
match wallet_name {
Some(name) => Ok(name),
None => {
eprintln!("{}", WALLET_NAME_PROMPT);

read_input_from_user(stdin_inputs)
}
}
}
55 changes: 43 additions & 12 deletions account_manager/src/validator/create.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::common::read_wallet_name_from_cli;
use crate::wallet::create::STDIN_INPUTS_FLAG;
use crate::{common::ensure_dir_exists, SECRETS_DIR_FLAG, VALIDATOR_DIR_FLAG};
use account_utils::{random_password, strip_off_newlines, validator_definitions};
use account_utils::{
random_password, read_password_from_user, strip_off_newlines, validator_definitions, PlainText,
};
use clap::{App, Arg, ArgMatches};
use environment::Environment;
use eth2_wallet::PlainText;
use eth2_wallet_manager::WalletManager;
use std::ffi::OsStr;
use std::fs;
Expand All @@ -18,6 +21,7 @@ pub const DEPOSIT_GWEI_FLAG: &str = "deposit-gwei";
pub const STORE_WITHDRAW_FLAG: &str = "store-withdrawal-keystore";
pub const COUNT_FLAG: &str = "count";
pub const AT_MOST_FLAG: &str = "at-most";
pub const WALLET_PASSWORD_PROMPT: &str = "Enter your wallet's password:";

pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
App::new(CMD)
Expand All @@ -30,16 +34,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long(WALLET_NAME_FLAG)
.value_name("WALLET_NAME")
.help("Use the wallet identified by this name")
.takes_value(true)
.required(true),
.takes_value(true),
)
.arg(
Arg::with_name(WALLET_PASSWORD_FLAG)
.long(WALLET_PASSWORD_FLAG)
.value_name("WALLET_PASSWORD_PATH")
.help("A path to a file containing the password which will unlock the wallet.")
.takes_value(true)
.required(true),
.takes_value(true),
)
.arg(
Arg::with_name(VALIDATOR_DIR_FLAG)
Expand Down Expand Up @@ -99,6 +101,11 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.conflicts_with("count")
.takes_value(true),
)
.arg(
Arg::with_name(STDIN_INPUTS_FLAG)
.long(STDIN_INPUTS_FLAG)
.help("If present, read all user inputs from stdin instead of tty."),
)
}

pub fn cli_run<T: EthSpec>(
Expand All @@ -108,8 +115,9 @@ pub fn cli_run<T: EthSpec>(
) -> Result<(), String> {
let spec = env.core_context().eth2_config.spec;

let name: String = clap_utils::parse_required(matches, WALLET_NAME_FLAG)?;
let wallet_password_path: PathBuf = clap_utils::parse_required(matches, WALLET_PASSWORD_FLAG)?;
let name: Option<String> = clap_utils::parse_optional(matches, WALLET_NAME_FLAG)?;
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);

let validator_dir = clap_utils::parse_path_with_default_in_home_dir(
matches,
VALIDATOR_DIR_FLAG,
Expand Down Expand Up @@ -151,15 +159,17 @@ pub fn cli_run<T: EthSpec>(
return Ok(());
}

let wallet_password = fs::read(&wallet_password_path)
.map_err(|e| format!("Unable to read {:?}: {:?}", wallet_password_path, e))
.map(|bytes| PlainText::from(strip_off_newlines(bytes)))?;
let wallet_password_path: Option<PathBuf> =
clap_utils::parse_optional(matches, WALLET_PASSWORD_FLAG)?;

let wallet_name = read_wallet_name_from_cli(name, stdin_inputs)?;
let wallet_password = read_wallet_password_from_cli(wallet_password_path, stdin_inputs)?;

let mgr = WalletManager::open(&wallet_base_dir)
.map_err(|e| format!("Unable to open --{}: {:?}", BASE_DIR_FLAG, e))?;

let mut wallet = mgr
.wallet_by_name(&name)
.wallet_by_name(&wallet_name)
.map_err(|e| format!("Unable to open wallet: {:?}", e))?;

for i in 0..n {
Expand Down Expand Up @@ -204,3 +214,24 @@ fn existing_validator_count<P: AsRef<Path>>(validator_dir: P) -> Result<usize, S
})
.map_err(|e| format!("Unable to read {:?}: {}", validator_dir.as_ref(), e))
}

/// Used when a user is accessing an existing wallet. Read in a wallet password from a file if the password file
/// path is provided. Otherwise, read from an interactive prompt using tty unless the `--stdin-inputs`
/// flag is provided.
pub fn read_wallet_password_from_cli(
password_file_path: Option<PathBuf>,
stdin_inputs: bool,
) -> Result<PlainText, String> {
match password_file_path {
Some(path) => fs::read(&path)
.map_err(|e| format!("Unable to read {:?}: {:?}", path, e))
.map(|bytes| strip_off_newlines(bytes).into()),
None => {
eprintln!("");
eprintln!("{}", WALLET_PASSWORD_PROMPT);
let password =
PlainText::from(read_password_from_user(stdin_inputs)?.as_ref().to_vec());
Ok(password)
}
}
}
12 changes: 6 additions & 6 deletions account_manager/src/validator/import.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::wallet::create::STDIN_INPUTS_FLAG;
use crate::{common::ensure_dir_exists, VALIDATOR_DIR_FLAG};
use account_utils::{
eth2_keystore::Keystore,
Expand All @@ -17,7 +18,6 @@ use std::time::Duration;
pub const CMD: &str = "import";
pub const KEYSTORE_FLAG: &str = "keystore";
pub const DIR_FLAG: &str = "directory";
pub const STDIN_PASSWORD_FLAG: &str = "stdin-passwords";
pub const REUSE_PASSWORD_FLAG: &str = "reuse-password";

pub const PASSWORD_PROMPT: &str = "Enter the keystore password, or press enter to omit it:";
Expand Down Expand Up @@ -66,9 +66,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.takes_value(true),
)
.arg(
Arg::with_name(STDIN_PASSWORD_FLAG)
.long(STDIN_PASSWORD_FLAG)
.help("If present, read passwords from stdin instead of tty."),
Arg::with_name(STDIN_INPUTS_FLAG)
.long(STDIN_INPUTS_FLAG)
.help("If present, read all user inputs from stdin instead of tty."),
)
.arg(
Arg::with_name(REUSE_PASSWORD_FLAG)
Expand All @@ -85,7 +85,7 @@ pub fn cli_run(matches: &ArgMatches) -> Result<(), String> {
VALIDATOR_DIR_FLAG,
PathBuf::new().join(".lighthouse").join("validators"),
)?;
let stdin_password = matches.is_present(STDIN_PASSWORD_FLAG);
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);
let reuse_password = matches.is_present(REUSE_PASSWORD_FLAG);

ensure_dir_exists(&validator_dir)?;
Expand Down Expand Up @@ -153,7 +153,7 @@ pub fn cli_run(matches: &ArgMatches) -> Result<(), String> {
eprintln!("");
eprintln!("{}", PASSWORD_PROMPT);

let password = read_password_from_user(stdin_password)?;
let password = read_password_from_user(stdin_inputs)?;

if password.as_ref().is_empty() {
eprintln!("Continuing without password.");
Expand Down
12 changes: 6 additions & 6 deletions account_manager/src/validator/recover.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::create::STORE_WITHDRAW_FLAG;
use super::import::STDIN_PASSWORD_FLAG;
use crate::common::{ensure_dir_exists, read_mnemonic_from_cli};
use crate::validator::create::COUNT_FLAG;
use crate::wallet::create::STDIN_INPUTS_FLAG;
use crate::{SECRETS_DIR_FLAG, VALIDATOR_DIR_FLAG};
use account_utils::eth2_keystore::{keypair_from_secret, Keystore, KeystoreBuilder};
use account_utils::random_password;
Expand Down Expand Up @@ -78,9 +78,9 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
),
)
.arg(
Arg::with_name(STDIN_PASSWORD_FLAG)
.long(STDIN_PASSWORD_FLAG)
.help("If present, read passwords from stdin instead of tty."),
Arg::with_name(STDIN_INPUTS_FLAG)
.long(STDIN_INPUTS_FLAG)
.help("If present, read all user inputs from stdin instead of tty."),
)
}

Expand All @@ -98,7 +98,7 @@ pub fn cli_run(matches: &ArgMatches) -> Result<(), String> {
let first_index: u32 = clap_utils::parse_required(matches, FIRST_INDEX_FLAG)?;
let count: u32 = clap_utils::parse_required(matches, COUNT_FLAG)?;
let mnemonic_path: Option<PathBuf> = clap_utils::parse_optional(matches, MNEMONIC_FLAG)?;
let stdin_password = matches.is_present(STDIN_PASSWORD_FLAG);
let stdin_inputs = matches.is_present(STDIN_INPUTS_FLAG);

ensure_dir_exists(&validator_dir)?;
ensure_dir_exists(&secrets_dir)?;
Expand All @@ -107,7 +107,7 @@ pub fn cli_run(matches: &ArgMatches) -> Result<(), String> {
eprintln!("WARNING: KEY RECOVERY CAN LEAD TO DUPLICATING VALIDATORS KEYS, WHICH CAN LEAD TO SLASHING.");
eprintln!("");

let mnemonic = read_mnemonic_from_cli(mnemonic_path, stdin_password)?;
let mnemonic = read_mnemonic_from_cli(mnemonic_path, stdin_inputs)?;

let seed = Seed::new(&mnemonic, "");

Expand Down
Loading