-
Notifications
You must be signed in to change notification settings - Fork 779
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into directory-restructure
- Loading branch information
Showing
78 changed files
with
1,915 additions
and
1,066 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "account_manager" | ||
version = "0.2.8" | ||
version = "0.2.11" | ||
authors = ["Paul Hauner <[email protected]>", "Luke Anderson <[email protected]>"] | ||
edition = "2018" | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use account_utils::PlainText; | ||
use account_utils::{read_mnemonic_from_user, strip_off_newlines}; | ||
use eth2_wallet::bip39::{Language, Mnemonic}; | ||
use std::fs; | ||
use std::path::PathBuf; | ||
use std::str::from_utf8; | ||
use std::thread::sleep; | ||
use std::time::Duration; | ||
|
||
pub const MNEMONIC_PROMPT: &str = "Enter the mnemonic phrase:"; | ||
|
||
pub fn read_mnemonic_from_cli( | ||
mnemonic_path: Option<PathBuf>, | ||
stdin_password: bool, | ||
) -> Result<Mnemonic, String> { | ||
let mnemonic = match mnemonic_path { | ||
Some(path) => fs::read(&path) | ||
.map_err(|e| format!("Unable to read {:?}: {:?}", path, e)) | ||
.and_then(|bytes| { | ||
let bytes_no_newlines: PlainText = strip_off_newlines(bytes).into(); | ||
let phrase = from_utf8(&bytes_no_newlines.as_ref()) | ||
.map_err(|e| format!("Unable to derive mnemonic: {:?}", e))?; | ||
Mnemonic::from_phrase(phrase, Language::English).map_err(|e| { | ||
format!( | ||
"Unable to derive mnemonic from string {:?}: {:?}", | ||
phrase, e | ||
) | ||
}) | ||
})?, | ||
None => loop { | ||
eprintln!(""); | ||
eprintln!("{}", MNEMONIC_PROMPT); | ||
|
||
let mnemonic = read_mnemonic_from_user(stdin_password)?; | ||
|
||
match Mnemonic::from_phrase(mnemonic.as_str(), Language::English) { | ||
Ok(mnemonic_m) => { | ||
eprintln!("Valid mnemonic provided."); | ||
eprintln!(""); | ||
sleep(Duration::from_secs(1)); | ||
break mnemonic_m; | ||
} | ||
Err(_) => { | ||
eprintln!("Invalid mnemonic"); | ||
} | ||
} | ||
}, | ||
}; | ||
Ok(mnemonic) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
mod common; | ||
pub mod validator; | ||
pub mod wallet; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
use super::create::STORE_WITHDRAW_FLAG; | ||
use super::import::STDIN_PASSWORD_FLAG; | ||
use crate::common::read_mnemonic_from_cli; | ||
use crate::validator::create::COUNT_FLAG; | ||
use crate::{SECRETS_DIR_FLAG, VALIDATOR_DIR_FLAG}; | ||
use account_utils::eth2_keystore::{keypair_from_secret, Keystore, KeystoreBuilder}; | ||
use account_utils::random_password; | ||
use clap::{App, Arg, ArgMatches}; | ||
use directory::ensure_dir_exists; | ||
use eth2_wallet::bip39::Seed; | ||
use eth2_wallet::{recover_validator_secret_from_mnemonic, KeyType, ValidatorKeystores}; | ||
use std::path::PathBuf; | ||
use validator_dir::Builder as ValidatorDirBuilder; | ||
pub const CMD: &str = "recover"; | ||
pub const FIRST_INDEX_FLAG: &str = "first-index"; | ||
pub const MNEMONIC_FLAG: &str = "mnemonic-path"; | ||
|
||
pub fn cli_app<'a, 'b>() -> App<'a, 'b> { | ||
App::new(CMD) | ||
.about( | ||
"Recovers validator private keys given a BIP-39 mnemonic phrase. \ | ||
If you did not specify a `--first-index` or count `--count`, by default this will \ | ||
only recover the keys associated with the validator at index 0 for an HD wallet \ | ||
in accordance with the EIP-2333 spec.") | ||
.arg( | ||
Arg::with_name(FIRST_INDEX_FLAG) | ||
.long(FIRST_INDEX_FLAG) | ||
.value_name("FIRST_INDEX") | ||
.help("The first of consecutive key indexes you wish to recover.") | ||
.takes_value(true) | ||
.required(false) | ||
.default_value("0"), | ||
) | ||
.arg( | ||
Arg::with_name(COUNT_FLAG) | ||
.long(COUNT_FLAG) | ||
.value_name("COUNT") | ||
.help("The number of validator keys you wish to recover. Counted consecutively from the provided `--first_index`.") | ||
.takes_value(true) | ||
.required(false) | ||
.default_value("1"), | ||
) | ||
.arg( | ||
Arg::with_name(MNEMONIC_FLAG) | ||
.long(MNEMONIC_FLAG) | ||
.value_name("MNEMONIC_PATH") | ||
.help( | ||
"If present, the mnemonic will be read in from this file.", | ||
) | ||
.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::with_name(SECRETS_DIR_FLAG) | ||
.long(SECRETS_DIR_FLAG) | ||
.value_name("SECRETS_DIR") | ||
.help( | ||
"The path where the validator keystore passwords will be stored. \ | ||
Defaults to ~/.lighthouse/secrets", | ||
) | ||
.takes_value(true), | ||
) | ||
.arg( | ||
Arg::with_name(STORE_WITHDRAW_FLAG) | ||
.long(STORE_WITHDRAW_FLAG) | ||
.help( | ||
"If present, the withdrawal keystore will be stored alongside the voting \ | ||
keypair. It is generally recommended to *not* store the withdrawal key and \ | ||
instead generate them from the wallet seed when required.", | ||
), | ||
) | ||
.arg( | ||
Arg::with_name(STDIN_PASSWORD_FLAG) | ||
.long(STDIN_PASSWORD_FLAG) | ||
.help("If present, read passwords from stdin instead of tty."), | ||
) | ||
} | ||
|
||
pub fn cli_run(matches: &ArgMatches) -> Result<(), String> { | ||
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 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); | ||
|
||
ensure_dir_exists(&validator_dir)?; | ||
ensure_dir_exists(&secrets_dir)?; | ||
|
||
eprintln!(""); | ||
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 seed = Seed::new(&mnemonic, ""); | ||
|
||
for index in first_index..first_index + count { | ||
let voting_password = random_password(); | ||
let withdrawal_password = random_password(); | ||
|
||
let derive = |key_type: KeyType, password: &[u8]| -> Result<Keystore, String> { | ||
let (secret, path) = | ||
recover_validator_secret_from_mnemonic(seed.as_bytes(), index, key_type) | ||
.map_err(|e| format!("Unable to recover validator keys: {:?}", e))?; | ||
|
||
let keypair = keypair_from_secret(secret.as_bytes()) | ||
.map_err(|e| format!("Unable build keystore: {:?}", e))?; | ||
|
||
KeystoreBuilder::new(&keypair, password, format!("{}", path)) | ||
.map_err(|e| format!("Unable build keystore: {:?}", e))? | ||
.build() | ||
.map_err(|e| format!("Unable build keystore: {:?}", e)) | ||
}; | ||
|
||
let keystores = ValidatorKeystores { | ||
voting: derive(KeyType::Voting, voting_password.as_bytes())?, | ||
withdrawal: derive(KeyType::Withdrawal, withdrawal_password.as_bytes())?, | ||
}; | ||
|
||
let voting_pubkey = keystores.voting.pubkey().to_string(); | ||
|
||
ValidatorDirBuilder::new(validator_dir.clone(), secrets_dir.clone()) | ||
.voting_keystore(keystores.voting, voting_password.as_bytes()) | ||
.withdrawal_keystore(keystores.withdrawal, withdrawal_password.as_bytes()) | ||
.store_withdrawal_keystore(matches.is_present(STORE_WITHDRAW_FLAG)) | ||
.build() | ||
.map_err(|e| format!("Unable to build validator directory: {:?}", e))?; | ||
|
||
println!( | ||
"{}/{}\tIndex: {}\t0x{}", | ||
index - first_index, | ||
count - first_index, | ||
index, | ||
voting_pubkey | ||
); | ||
} | ||
|
||
Ok(()) | ||
} |
Oops, something went wrong.