Skip to content

Commit

Permalink
Merge branch 'master' into directory-restructure
Browse files Browse the repository at this point in the history
  • Loading branch information
pawanjay176 committed Sep 22, 2020
2 parents 6759bd1 + a97ec31 commit 282a973
Show file tree
Hide file tree
Showing 78 changed files with 1,915 additions and 1,066 deletions.
416 changes: 203 additions & 213 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ members = [
"consensus/ssz_derive",
"consensus/ssz_types",
"consensus/serde_hex",
"consensus/serde_utils",
"consensus/state_processing",
"consensus/swap_or_not_shuffle",
"consensus/tree_hash",
Expand Down Expand Up @@ -79,4 +80,3 @@ eth2_ssz = { path = "consensus/ssz" }
eth2_ssz_derive = { path = "consensus/ssz_derive" }
eth2_ssz_types = { path = "consensus/ssz_types" }
eth2_hashing = { path = "crypto/eth2_hashing" }
leveldb-sys = { git = "https://github.com/michaelsproul/leveldb-sys", branch = "v2.0.6-cmake" }
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

An open-source Ethereum 2.0 client, written in Rust and maintained by Sigma Prime.

[![Build Status]][Build Link] [![Book Status]][Book Link] [![RustDoc Status]][RustDoc Link] [![Chat Badge]][Chat Link]
[![Build Status]][Build Link] [![Book Status]][Book Link] [![Chat Badge]][Chat Link]

[Build Status]: https://github.com/sigp/lighthouse/workflows/test-suite/badge.svg?branch=master
[Build Link]: https://github.com/sigp/lighthouse/actions
[Chat Badge]: https://img.shields.io/badge/chat-discord-%237289da
[Chat Link]: https://discord.gg/cyAszAh
[Book Status]:https://img.shields.io/badge/user--docs-master-informational
[Book Link]: http://lighthouse-book.sigmaprime.io/
[RustDoc Status]:https://img.shields.io/badge/code--docs-master-orange
[RustDoc Link]: http://lighthouse-docs.sigmaprime.io/

[Documentation](http://lighthouse-book.sigmaprime.io/)

Expand Down Expand Up @@ -59,9 +57,6 @@ Current development overview:
The [Lighthouse Book](http://lighthouse-book.sigmaprime.io/) contains information
for testnet users and developers.

Code documentation is generated via `cargo doc` and hosted at
[lighthouse-docs.sigmaprime.io](http://lighthouse-docs.sigmaprime.io/).

If you'd like some background on Sigma Prime, please see the [Lighthouse Update
\#00](https://lighthouse.sigmaprime.io/update-00.html) blog post or
[sigmaprime.io](https://sigmaprime.io).
Expand Down
2 changes: 1 addition & 1 deletion account_manager/Cargo.toml
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"

Expand Down
50 changes: 50 additions & 0 deletions account_manager/src/common.rs
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)
}
1 change: 1 addition & 0 deletions account_manager/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod common;
pub mod validator;
pub mod wallet;

Expand Down
17 changes: 17 additions & 0 deletions account_manager/src/validator/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use account_utils::{
recursively_find_voting_keystores, ValidatorDefinition, ValidatorDefinitions,
CONFIG_FILENAME,
},
ZeroizeString,
};
use clap::{App, Arg, ArgMatches};
use std::fs;
Expand All @@ -16,6 +17,7 @@ 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:";
pub const KEYSTORE_REUSE_WARNING: &str = "DO NOT USE THE ORIGINAL KEYSTORES TO VALIDATE WITH \
Expand Down Expand Up @@ -57,12 +59,18 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long(STDIN_PASSWORD_FLAG)
.help("If present, read passwords from stdin instead of tty."),
)
.arg(
Arg::with_name(REUSE_PASSWORD_FLAG)
.long(REUSE_PASSWORD_FLAG)
.help("If present, the same password will be used for all imported keystores."),
)
}

pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), String> {
let keystore: Option<PathBuf> = clap_utils::parse_optional(matches, KEYSTORE_FLAG)?;
let keystores_dir: Option<PathBuf> = clap_utils::parse_optional(matches, DIR_FLAG)?;
let stdin_password = matches.is_present(STDIN_PASSWORD_FLAG);
let reuse_password = matches.is_present(REUSE_PASSWORD_FLAG);

let mut defs = ValidatorDefinitions::open_or_create(&validator_dir)
.map_err(|e| format!("Unable to open {}: {:?}", CONFIG_FILENAME, e))?;
Expand Down Expand Up @@ -100,7 +108,9 @@ pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), Strin
// - Add the keystore to the validator definitions file.
//
// Skip keystores that already exist, but exit early if any operation fails.
// Reuses the same password for all keystores if the `REUSE_PASSWORD_FLAG` flag is set.
let mut num_imported_keystores = 0;
let mut previous_password: Option<ZeroizeString> = None;
for src_keystore in &keystore_paths {
let keystore = Keystore::from_json_file(src_keystore)
.map_err(|e| format!("Unable to read keystore JSON {:?}: {:?}", src_keystore, e))?;
Expand All @@ -118,6 +128,10 @@ pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), Strin
);

let password_opt = loop {
if let Some(password) = previous_password.clone() {
eprintln!("Reuse previous password.");
break Some(password);
}
eprintln!("");
eprintln!("{}", PASSWORD_PROMPT);

Expand All @@ -134,6 +148,9 @@ pub fn cli_run(matches: &ArgMatches, validator_dir: PathBuf) -> Result<(), Strin
eprintln!("Password is correct.");
eprintln!("");
sleep(Duration::from_secs(1)); // Provides nicer UX.
if reuse_password {
previous_password = Some(password.clone());
}
break Some(password);
}
Err(eth2_keystore::Error::InvalidPassword) => {
Expand Down
2 changes: 2 additions & 0 deletions account_manager/src/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod create;
pub mod deposit;
pub mod import;
pub mod list;
pub mod recover;

use crate::VALIDATOR_DIR_FLAG;
use clap::{App, Arg, ArgMatches};
Expand Down Expand Up @@ -31,6 +32,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.subcommand(deposit::cli_app())
.subcommand(import::cli_app())
.subcommand(list::cli_app())
.subcommand(recover::cli_app())
}

pub fn cli_run<T: EthSpec>(matches: &ArgMatches, env: Environment<T>) -> Result<(), String> {
Expand Down
157 changes: 157 additions & 0 deletions account_manager/src/validator/recover.rs
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(())
}
Loading

0 comments on commit 282a973

Please sign in to comment.