diff --git a/Cargo.lock b/Cargo.lock index 208d08f1e0..0658de7fa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3197,8 +3197,10 @@ name = "solana-clap-utils" version = "0.21.0" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "solana-sdk 0.21.0", + "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3859,12 +3861,14 @@ dependencies = [ "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3874,6 +3878,7 @@ dependencies = [ "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "solana-crate-features 0.21.0", "solana-logger 0.21.0", + "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/book/src/running-validator/validator-start.md b/book/src/running-validator/validator-start.md index 762206c0f9..d8cd2e7a04 100644 --- a/book/src/running-validator/validator-start.md +++ b/book/src/running-validator/validator-start.md @@ -76,19 +76,19 @@ Then use one of the following commands, depending on your installation choice, t If this is a `solana-install`-installation: ```bash -solana-validator --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --entrypoint testnet.solana.com:8001 +solana-validator --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --entrypoint testnet.solana.com:8001 ``` Alternatively, the `solana-install run` command can be used to run the validator node while periodically checking for and applying software updates: ```bash -solana-install run solana-validator -- --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --entrypoint testnet.solana.com:8001 +solana-install run solana-validator -- --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 --entrypoint testnet.solana.com:8001 ``` If you built from source: ```bash -NDEBUG=1 USE_INSTALL=1 ./multinode-demo/validator.sh --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --rpc-port 8899 --entrypoint testnet.solana.com:8001 +NDEBUG=1 USE_INSTALL=1 ./multinode-demo/validator.sh --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --rpc-port 8899 --entrypoint testnet.solana.com:8001 ``` ### Enabling CUDA diff --git a/book/src/running-validator/validator-testnet.md b/book/src/running-validator/validator-testnet.md index d1e1438969..58fb112991 100644 --- a/book/src/running-validator/validator-testnet.md +++ b/book/src/running-validator/validator-testnet.md @@ -63,7 +63,7 @@ solana balance # Same result as command above Solana-gossip and solana-validator commands already require an explicit `--entrypoint` argument. Simply replace testnet.solana.com in the examples with an alternate url to interact with a different testnet. For example: ```bash -solana-validator --identity ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 beta.testnet.solana.com +solana-validator --identity-keypair ~/validator-keypair.json --voting-keypair ~/validator-vote-keypair.json --ledger ~/validator-config --rpc-port 8899 beta.testnet.solana.com ``` You can also submit JSON-RPC requests to a different testnet, like: diff --git a/clap-utils/Cargo.toml b/clap-utils/Cargo.toml index 6afcd978f0..f7db9082b8 100644 --- a/clap-utils/Cargo.toml +++ b/clap-utils/Cargo.toml @@ -10,8 +10,10 @@ edition = "2018" [dependencies] clap = "2.33.0" +rpassword = "4.0" semver = "0.9.0" solana-sdk = { path = "../sdk", version = "0.21.0" } +tiny-bip39 = "0.6.2" url = "2.1.0" [lib] diff --git a/clap-utils/src/keypair.rs b/clap-utils/src/keypair.rs new file mode 100644 index 0000000000..baa7881fd3 --- /dev/null +++ b/clap-utils/src/keypair.rs @@ -0,0 +1,95 @@ +use bip39::{Language, Mnemonic, Seed}; +use clap::values_t; +use rpassword::prompt_password_stderr; +use solana_sdk::signature::{ + keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair, + KeypairUtil, +}; +use std::error; + +pub const ASK_SEED_PHRASE_ARG: &str = "ask_seed_phrase"; +pub const SKIP_SEED_PHRASE_VALIDATION_ARG: &str = "skip_seed_phrase_validation"; + +/// Reads user input from stdin to retrieve a seed phrase and passphrase for keypair derivation +pub fn keypair_from_seed_phrase( + keypair_name: &str, + skip_validation: bool, +) -> Result> { + let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?; + let seed_phrase = seed_phrase.trim(); + let passphrase_prompt = format!( + "[{}] If this seed phrase has an associated passphrase, enter it now. Otherwise, press ENTER to continue: ", + keypair_name, + ); + + if skip_validation { + let passphrase = prompt_password_stderr(&passphrase_prompt)?; + keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase) + } else { + let mnemonic = Mnemonic::from_phrase(seed_phrase, Language::English)?; + let passphrase = prompt_password_stderr(&passphrase_prompt)?; + let seed = Seed::new(&mnemonic, &passphrase); + keypair_from_seed(seed.as_bytes()) + } +} + +pub struct KeypairWithGenerated { + pub keypair: Keypair, + pub generated: bool, +} + +impl KeypairWithGenerated { + fn new(keypair: Keypair, generated: bool) -> Self { + Self { keypair, generated } + } +} + +/// Checks CLI arguments to determine whether a keypair should be: +/// - inputted securely via stdin, +/// - read in from a file, +/// - or newly generated +pub fn keypair_input( + matches: &clap::ArgMatches, + keypair_name: &str, +) -> Result> { + let ask_seed_phrase_matches = + values_t!(matches.values_of(ASK_SEED_PHRASE_ARG), String).unwrap_or_default(); + let keypair_match_name = keypair_name.replace('-', "_"); + if ask_seed_phrase_matches + .iter() + .any(|s| s.as_str() == keypair_name) + { + if matches.value_of(keypair_match_name).is_some() { + let ask_seed_phrase_kebab = ASK_SEED_PHRASE_ARG.replace('_', "-"); + clap::Error::with_description( + &format!( + "`--{} {}` cannot be used with `{} `", + ask_seed_phrase_kebab, keypair_name, keypair_name + ), + clap::ErrorKind::ArgumentConflict, + ) + .exit(); + } + + let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG); + keypair_from_seed_phrase(keypair_name, skip_validation) + .map(|keypair| KeypairWithGenerated::new(keypair, false)) + } else if let Some(keypair_file) = matches.value_of(keypair_match_name) { + read_keypair_file(keypair_file).map(|keypair| KeypairWithGenerated::new(keypair, false)) + } else { + Ok(KeypairWithGenerated::new(Keypair::new(), true)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use clap::ArgMatches; + + #[test] + fn test_keypair_input() { + let arg_matches = ArgMatches::default(); + let KeypairWithGenerated { generated, .. } = keypair_input(&arg_matches, "").unwrap(); + assert!(generated); + } +} diff --git a/clap-utils/src/lib.rs b/clap-utils/src/lib.rs index 71addb6dbb..fbddbd2acc 100644 --- a/clap-utils/src/lib.rs +++ b/clap-utils/src/lib.rs @@ -19,3 +19,4 @@ macro_rules! version { pub mod input_parsers; pub mod input_validators; +pub mod keypair; diff --git a/multinode-demo/bootstrap-leader.sh b/multinode-demo/bootstrap-leader.sh index 92240ff155..22baa0c851 100755 --- a/multinode-demo/bootstrap-leader.sh +++ b/multinode-demo/bootstrap-leader.sh @@ -76,10 +76,10 @@ ledger_dir="$SOLANA_CONFIG_DIR"/bootstrap-leader args+=( --accounts "$SOLANA_CONFIG_DIR"/bootstrap-leader/accounts --enable-rpc-exit - --identity "$identity_keypair" --ledger "$ledger_dir" --rpc-port 8899 --snapshot-interval-slots 100 + --identity-keypair "$identity_keypair" --storage-keypair "$storage_keypair" --voting-keypair "$vote_keypair" --rpc-drone-address 127.0.0.1:9900 diff --git a/multinode-demo/validator.sh b/multinode-demo/validator.sh index 4b0b658cc6..dcd2a1fc19 100755 --- a/multinode-demo/validator.sh +++ b/multinode-demo/validator.sh @@ -70,7 +70,7 @@ while [[ -n $1 ]]; do elif [[ $1 = --expected-genesis-hash ]]; then args+=("$1" "$2") shift 2 - elif [[ $1 = --identity ]]; then + elif [[ $1 = --identity-keypair ]]; then identity_keypair_path=$2 args+=("$1" "$2") shift 2 @@ -170,7 +170,7 @@ fi if [[ -n $REQUIRE_KEYPAIRS ]]; then if [[ -z $identity_keypair_path ]]; then - usage "Error: --identity not specified" + usage "Error: --identity-keypair not specified" fi if [[ -z $voting_keypair_path ]]; then usage "Error: --voting-keypair not specified" @@ -209,7 +209,7 @@ if ((airdrops_enabled)); then default_arg --rpc-drone-address "$drone_address" fi -default_arg --identity "$identity_keypair_path" +default_arg --identity-keypair "$identity_keypair_path" default_arg --voting-keypair "$voting_keypair_path" default_arg --storage-keypair "$storage_keypair_path" default_arg --ledger "$ledger_dir" diff --git a/net/remote/remote-node.sh b/net/remote/remote-node.sh index f38a41b783..abee99a59f 100755 --- a/net/remote/remote-node.sh +++ b/net/remote/remote-node.sh @@ -304,7 +304,7 @@ EOF if [[ ! -f config/validator-identity.json ]]; then solana-keygen new -o config/validator-identity.json fi - args+=(--identity config/validator-identity.json) + args+=(--identity-keypair config/validator-identity.json) if [[ $airdropsEnabled != true ]]; then args+=(--no-airdrop) diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index ac25b5f37a..871e257475 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -375,6 +375,15 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ct-logs" version = "0.6.0" @@ -681,6 +690,15 @@ name = "hex" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "http" version = "0.1.18" @@ -1069,6 +1087,15 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pbkdf2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1557,7 +1584,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "solana-bpf-loader-api" +name = "solana-bpf-loader-program" version = "0.21.0" dependencies = [ "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1570,16 +1597,6 @@ dependencies = [ "solana_rbpf 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "solana-bpf-loader-program" -version = "0.21.0" -dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-bpf-loader-api 0.21.0", - "solana-logger 0.21.0", - "solana-sdk 0.21.0", -] - [[package]] name = "solana-bpf-programs" version = "0.21.0" @@ -1587,7 +1604,7 @@ dependencies = [ "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-bpf-loader-api 0.21.0", + "solana-bpf-loader-program 0.21.0", "solana-logger 0.21.0", "solana-runtime 0.21.0", "solana-sdk 0.21.0", @@ -1704,13 +1721,14 @@ dependencies = [ ] [[package]] -name = "solana-config-api" +name = "solana-config-program" version = "0.21.0" dependencies = [ "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-logger 0.21.0", "solana-sdk 0.21.0", ] @@ -1793,17 +1811,14 @@ dependencies = [ "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-bpf-loader-api 0.21.0", "solana-bpf-loader-program 0.21.0", "solana-logger 0.21.0", "solana-measure 0.21.0", "solana-metrics 0.21.0", "solana-rayon-threadlimit 0.21.0", "solana-sdk 0.21.0", - "solana-stake-api 0.21.0", "solana-stake-program 0.21.0", - "solana-storage-api 0.21.0", - "solana-vote-api 0.21.0", + "solana-storage-program 0.21.0", "solana-vote-program 0.21.0", "sys-info 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1820,11 +1835,14 @@ dependencies = [ "ed25519-dalek 1.0.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1840,36 +1858,26 @@ dependencies = [ name = "solana-sdk-bpf-test" version = "0.21.0" -[[package]] -name = "solana-stake-api" -version = "0.21.0" -dependencies = [ - "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-config-api 0.21.0", - "solana-logger 0.21.0", - "solana-metrics 0.21.0", - "solana-sdk 0.21.0", - "solana-vote-api 0.21.0", -] - [[package]] name = "solana-stake-program" version = "0.21.0" dependencies = [ + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-config-program 0.21.0", "solana-logger 0.21.0", + "solana-metrics 0.21.0", "solana-sdk 0.21.0", - "solana-stake-api 0.21.0", + "solana-vote-program 0.21.0", ] [[package]] -name = "solana-storage-api" +name = "solana-storage-program" version = "0.21.0" dependencies = [ "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1883,29 +1891,19 @@ dependencies = [ "solana-sdk 0.21.0", ] -[[package]] -name = "solana-vote-api" -version = "0.21.0" -dependencies = [ - "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-logger 0.21.0", - "solana-metrics 0.21.0", - "solana-sdk 0.21.0", -] - [[package]] name = "solana-vote-program" version = "0.21.0" dependencies = [ + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "solana-logger 0.21.0", + "solana-metrics 0.21.0", "solana-sdk 0.21.0", - "solana-vote-api 0.21.0", ] [[package]] @@ -1951,6 +1949,11 @@ name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "subtle" version = "2.1.1" @@ -2611,6 +2614,7 @@ dependencies = [ "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" "checksum ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" "checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" @@ -2646,6 +2650,7 @@ dependencies = [ "checksum hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12d790435639c06a7b798af9e1e331ae245b7ef915b92f70a39b4cf8c00686af" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" +"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" @@ -2689,6 +2694,7 @@ dependencies = [ "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" +"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" @@ -2750,6 +2756,7 @@ dependencies = [ "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" "checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" diff --git a/run.sh b/run.sh index 0a6ee826f4..d987447309 100755 --- a/run.sh +++ b/run.sh @@ -99,7 +99,7 @@ solana-drone --keypair "$dataDir"/faucet-keypair.json & drone=$! args=( - --identity "$dataDir"/leader-keypair.json + --identity-keypair "$dataDir"/leader-keypair.json --storage-keypair "$dataDir"/leader-storage-account-keypair.json --voting-keypair "$dataDir"/leader-vote-account-keypair.json --ledger "$ledgerDir" diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 44623c725f..67eeb551be 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -31,12 +31,14 @@ bs58 = "0.3.0" byteorder = { version = "1.3.2", optional = true } generic-array = { version = "0.13.2", default-features = false, features = ["serde", "more_lengths"] } hex = "0.4.0" +hmac = "0.7.0" itertools = { version = "0.8.1" } lazy_static = "1.4.0" log = { version = "0.4.8" } memmap = { version = "0.6.2", optional = true } num-derive = { version = "0.3" } num-traits = { version = "0.2" } +pbkdf2 = { version = "0.3.0", default-features = false } rand = { version = "0.6.5", optional = true } rand_chacha = { version = "0.1.1", optional = true } serde = "1.0.102" @@ -47,3 +49,6 @@ sha2 = "0.8.0" ed25519-dalek = { version = "1.0.0-pre.1", optional = true } solana-logger = { path = "../logger", version = "0.21.0", optional = true } solana-crate-features = { path = "../crate-features", version = "0.21.0", optional = true } + +[dev-dependencies] +tiny-bip39 = "0.6.2" diff --git a/sdk/src/signature.rs b/sdk/src/signature.rs index 80edf27cda..4f92787dd9 100644 --- a/sdk/src/signature.rs +++ b/sdk/src/signature.rs @@ -4,6 +4,7 @@ use crate::pubkey::Pubkey; use bs58; use ed25519_dalek; use generic_array::{typenum::U64, GenericArray}; +use hmac::Hmac; use rand::rngs::OsRng; use serde_json; use std::{ @@ -186,9 +187,29 @@ pub fn keypair_from_seed(seed: &[u8]) -> Result> Ok(keypair) } +pub fn keypair_from_seed_phrase_and_passphrase( + seed_phrase: &str, + passphrase: &str, +) -> Result> { + const PBKDF2_ROUNDS: usize = 2048; + const PBKDF2_BYTES: usize = 64; + + let salt = format!("mnemonic{}", passphrase); + + let mut seed = vec![0u8; PBKDF2_BYTES]; + pbkdf2::pbkdf2::>( + seed_phrase.as_bytes(), + salt.as_bytes(), + PBKDF2_ROUNDS, + &mut seed, + ); + keypair_from_seed(&seed[..]) +} + #[cfg(test)] mod tests { use super::*; + use bip39::{Language, Mnemonic, MnemonicType, Seed}; use std::mem; fn tmp_file_path(name: &str) -> String { @@ -300,4 +321,15 @@ mod tests { Err(ParseSignatureError::Invalid) ); } + + #[test] + fn test_keypair_from_seed_phrase_and_passphrase() { + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let passphrase = "42"; + let seed = Seed::new(&mnemonic, passphrase); + let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap(); + let keypair = + keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap(); + assert_eq!(keypair.pubkey(), expected_keypair.pubkey()); + } } diff --git a/validator/Cargo.toml b/validator/Cargo.toml index df1d42dc1b..04dd4cdf6c 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -30,8 +30,8 @@ solana-runtime = { path = "../runtime", version = "0.21.0" } solana-sdk = { path = "../sdk", version = "0.21.0" } solana-vote-program = { path = "../programs/vote", version = "0.21.0" } solana-vote-signer = { path = "../vote-signer", version = "0.21.0" } -tempfile = "3.1.0" tar = "0.4.26" +tempfile = "3.1.0" [target."cfg(unix)".dependencies] gag = "0.1.10" diff --git a/validator/src/main.rs b/validator/src/main.rs index 539d99058e..fa579a77e9 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -6,6 +6,9 @@ use log::*; use solana_clap_utils::{ input_parsers::pubkey_of, input_validators::{is_keypair, is_pubkey_or_keypair}, + keypair::{ + keypair_input, KeypairWithGenerated, ASK_SEED_PHRASE_ARG, SKIP_SEED_PHRASE_VALIDATION_ARG, + }, }; use solana_client::rpc_client::RpcClient; use solana_core::{ @@ -21,7 +24,7 @@ use solana_sdk::{ clock::Slot, hash::Hash, pubkey::Pubkey, - signature::{read_keypair_file, Keypair, KeypairUtil}, + signature::{Keypair, KeypairUtil}, }; use std::{ fs::{self, File}, @@ -323,9 +326,24 @@ pub fn main() { .help("Stream entries to this unix domain socket path") ) .arg( - Arg::with_name("identity") + Arg::with_name(ASK_SEED_PHRASE_ARG) + .long("ask-seed-phrase") + .value_name("KEYPAIR NAME") + .multiple(true) + .takes_value(true) + .possible_values(&["identity-keypair", "storage-keypair", "voting-keypair"]) + .help("Securely recover a keypair using a seed phrase and optional passphrase"), + ) + .arg( + Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG) + .long("skip-seed-phrase-validation") + .requires(ASK_SEED_PHRASE_ARG) + .help("Skip validation of seed phrases. Use this if your phrase does not use the BIP39 official English word list"), + ) + .arg( + Arg::with_name("identity_keypair") .short("i") - .long("identity") + .long("identity-keypair") .value_name("PATH") .takes_value(true) .validator(is_keypair) @@ -520,34 +538,27 @@ pub fn main() { ) .get_matches(); - let identity_keypair = if let Some(identity) = matches.value_of("identity") { - read_keypair_file(identity).unwrap_or_else(|err| { - error!("{}: Unable to open keypair file: {}", err, identity); + let identity_keypair = Arc::new( + keypair_input(&matches, "identity-keypair") + .unwrap_or_else(|err| { + eprintln!("Identity keypair input failed: {}", err); + exit(1); + }) + .keypair, + ); + let KeypairWithGenerated { + keypair: voting_keypair, + generated: ephemeral_voting_keypair, + } = keypair_input(&matches, "voting-keypair").unwrap_or_else(|err| { + eprintln!("Voting keypair input failed: {}", err); + exit(1); + }); + let storage_keypair = keypair_input(&matches, "storage-keypair") + .unwrap_or_else(|err| { + eprintln!("Storage keypair input failed: {}", err); exit(1); }) - } else { - Keypair::new() - }; - let identity_keypair = Arc::new(identity_keypair); - - let mut ephemeral_voting_keypair = false; - let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") { - read_keypair_file(identity).unwrap_or_else(|err| { - error!("{}: Unable to open keypair file: {}", err, identity); - exit(1); - }) - } else { - ephemeral_voting_keypair = true; - Keypair::new() - }; - let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") { - read_keypair_file(storage_keypair).unwrap_or_else(|err| { - error!("{}: Unable to open keypair file: {}", err, storage_keypair); - exit(1); - }) - } else { - Keypair::new() - }; + .keypair; let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap()); let entrypoint = matches.value_of("entrypoint"); @@ -582,7 +593,7 @@ pub fn main() { let snapshot_interval_slots = value_t_or_exit!(matches, "snapshot_interval_slots", usize); let snapshot_path = ledger_path.clone().join("snapshot"); fs::create_dir_all(&snapshot_path).unwrap_or_else(|err| { - error!( + eprintln!( "Failed to create snapshots directory {:?}: {}", snapshot_path, err );