Add mnenomic keypair generation and recovery to cli (#5889)
* Add mnenomic keypair generation and recovery to cli * Use password input to retrieve mnemonic phrase * Direct users without keypair file to use solana-keygen
This commit is contained in:
46
Cargo.lock
generated
46
Cargo.lock
generated
@ -1822,6 +1822,14 @@ name = "number_prefix"
|
|||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -1952,6 +1960,15 @@ dependencies = [
|
|||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -2484,6 +2501,15 @@ dependencies = [
|
|||||||
"librocksdb-sys 5.18.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"librocksdb-sys 5.18.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpassword"
|
||||||
|
version = "4.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
@ -3273,7 +3299,9 @@ version = "0.19.0-pre0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"solana-sdk 0.19.0-pre0",
|
"solana-sdk 0.19.0-pre0",
|
||||||
|
"tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4461,6 +4489,20 @@ dependencies = [
|
|||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-bip39"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"once_cell 0.1.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)",
|
||||||
|
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-keccak"
|
name = "tiny-keccak"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -5212,6 +5254,7 @@ dependencies = [
|
|||||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||||
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
||||||
"checksum number_prefix 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a"
|
"checksum number_prefix 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b02fc0ff9a9e4b35b3342880f48e896ebf69f2967921fe8646bf5b7125956a"
|
||||||
|
"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37"
|
||||||
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
||||||
"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
|
"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
|
||||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||||
@ -5225,6 +5268,7 @@ dependencies = [
|
|||||||
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
||||||
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
||||||
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
|
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
|
||||||
|
"checksum pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9"
|
||||||
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||||
"checksum percent-encoding 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954"
|
"checksum percent-encoding 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954"
|
||||||
@ -5287,6 +5331,7 @@ dependencies = [
|
|||||||
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
|
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
|
||||||
"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c"
|
"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c"
|
||||||
"checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e"
|
"checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e"
|
||||||
|
"checksum rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f072d931f11a96546efd97642e1e75e807345aced86b947f9239102f262d0fcd"
|
||||||
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
||||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
@ -5385,6 +5430,7 @@ dependencies = [
|
|||||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||||
"checksum threshold_crypto 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95be1032c63011f20b01c5edb64930e2b51512782b43b458b1e3449613d70f87"
|
"checksum threshold_crypto 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95be1032c63011f20b01c5edb64930e2b51512782b43b458b1e3449613d70f87"
|
||||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||||
|
"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060"
|
||||||
"checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2"
|
"checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2"
|
||||||
"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
|
"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
|
||||||
"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
|
"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
|
||||||
|
@ -6,7 +6,7 @@ use solana_cli::{
|
|||||||
input_validators::is_url,
|
input_validators::is_url,
|
||||||
wallet::{app, parse_command, process_command, WalletConfig, WalletError},
|
wallet::{app, parse_command, process_command, WalletConfig, WalletError},
|
||||||
};
|
};
|
||||||
use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil};
|
use solana_sdk::signature::{read_keypair, KeypairUtil};
|
||||||
use std::error;
|
use std::error;
|
||||||
|
|
||||||
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
|
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
|
||||||
@ -91,8 +91,9 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn erro
|
|||||||
} else {
|
} else {
|
||||||
let default = WalletConfig::default();
|
let default = WalletConfig::default();
|
||||||
if !std::path::Path::new(&default.keypair_path).exists() {
|
if !std::path::Path::new(&default.keypair_path).exists() {
|
||||||
gen_keypair_file(&default.keypair_path)?;
|
Err(WalletError::KeypairFileNotFound(
|
||||||
println!("New keypair generated at: {}", default.keypair_path);
|
"Generate a new keypair with `solana-keygen new`".to_string(),
|
||||||
|
))?;
|
||||||
}
|
}
|
||||||
default.keypair_path
|
default.keypair_path
|
||||||
};
|
};
|
||||||
|
@ -114,6 +114,7 @@ pub enum WalletError {
|
|||||||
InsufficientFundsForFee,
|
InsufficientFundsForFee,
|
||||||
DynamicProgramError(String),
|
DynamicProgramError(String),
|
||||||
RpcRequestError(String),
|
RpcRequestError(String),
|
||||||
|
KeypairFileNotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for WalletError {
|
impl fmt::Display for WalletError {
|
||||||
|
@ -15,7 +15,9 @@ cuda = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
dirs = "2.0.2"
|
dirs = "2.0.2"
|
||||||
|
rpassword = "4.0"
|
||||||
solana-sdk = { path = "../sdk", version = "0.19.0-pre0" }
|
solana-sdk = { path = "../sdk", version = "0.19.0-pre0" }
|
||||||
|
tiny-bip39 = "0.6.2"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "solana-keygen"
|
name = "solana-keygen"
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
use bip39::{Language, Mnemonic, MnemonicType, Seed};
|
||||||
use clap::{
|
use clap::{
|
||||||
crate_description, crate_name, crate_version, App, AppSettings, Arg, ArgMatches, SubCommand,
|
crate_description, crate_name, crate_version, App, AppSettings, Arg, ArgMatches, SubCommand,
|
||||||
};
|
};
|
||||||
use solana_sdk::pubkey::write_pubkey;
|
use solana_sdk::pubkey::write_pubkey;
|
||||||
use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil};
|
use solana_sdk::signature::{keypair_from_seed, read_keypair, write_keypair, KeypairUtil};
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
const NO_PASSPHRASE: &str = "";
|
||||||
|
|
||||||
fn check_for_overwrite(outfile: &str, matches: &ArgMatches) {
|
fn check_for_overwrite(outfile: &str, matches: &ArgMatches) {
|
||||||
let force = matches.is_present("force");
|
let force = matches.is_present("force");
|
||||||
if !force && Path::new(outfile).exists() {
|
if !force && Path::new(outfile).exists() {
|
||||||
@ -37,6 +40,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.short("f")
|
.short("f")
|
||||||
.long("force")
|
.long("force")
|
||||||
.help("Overwrite the output file if it exists"),
|
.help("Overwrite the output file if it exists"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("silent")
|
||||||
|
.short("s")
|
||||||
|
.long("silent")
|
||||||
|
.help("Do not display mnemonic phrase"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
@ -65,6 +74,25 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
.help("Overwrite the output file if it exists"),
|
.help("Overwrite the output file if it exists"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("recover")
|
||||||
|
.about("Recover keypair from mnemonic phrase")
|
||||||
|
.setting(AppSettings::DisableVersion)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("outfile")
|
||||||
|
.short("o")
|
||||||
|
.long("outfile")
|
||||||
|
.value_name("PATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Path to generated file"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("force")
|
||||||
|
.short("f")
|
||||||
|
.long("force")
|
||||||
|
.help("Overwrite the output file if it exists"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
@ -98,11 +126,51 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
if outfile != "-" {
|
if outfile != "-" {
|
||||||
check_for_overwrite(&outfile, &matches);
|
check_for_overwrite(&outfile, &matches);
|
||||||
}
|
}
|
||||||
let serialized_keypair = gen_keypair_file(outfile)?;
|
|
||||||
|
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
|
||||||
|
let phrase: &str = mnemonic.phrase();
|
||||||
|
let seed = Seed::new(&mnemonic, NO_PASSPHRASE);
|
||||||
|
let keypair = keypair_from_seed(seed.as_bytes())?;
|
||||||
|
|
||||||
|
let serialized_keypair = write_keypair(&keypair, outfile)?;
|
||||||
if outfile == "-" {
|
if outfile == "-" {
|
||||||
println!("{}", serialized_keypair);
|
println!("{}", serialized_keypair);
|
||||||
} else {
|
} else {
|
||||||
println!("Wrote {}", outfile);
|
println!("Wrote new keypair to {}", outfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
let silent = matches.is_present("silent");
|
||||||
|
if !silent {
|
||||||
|
let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
|
||||||
|
println!(
|
||||||
|
"{}\nSave this mnemonic phrase to recover your new keypair:\n{}\n{}",
|
||||||
|
÷r, phrase, ÷r
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
("recover", Some(matches)) => {
|
||||||
|
let mut path = dirs::home_dir().expect("home directory");
|
||||||
|
let outfile = if matches.is_present("outfile") {
|
||||||
|
matches.value_of("outfile").unwrap()
|
||||||
|
} else {
|
||||||
|
path.extend(&[".config", "solana", "id.json"]);
|
||||||
|
path.to_str().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
if outfile != "-" {
|
||||||
|
check_for_overwrite(&outfile, &matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
let phrase = rpassword::prompt_password_stdout("Mnemonic recovery phrase: ").unwrap();
|
||||||
|
let mnemonic = Mnemonic::from_phrase(phrase.trim(), Language::English)?;
|
||||||
|
let seed = Seed::new(&mnemonic, NO_PASSPHRASE);
|
||||||
|
let keypair = keypair_from_seed(seed.as_bytes())?;
|
||||||
|
|
||||||
|
let serialized_keypair = write_keypair(&keypair, outfile)?;
|
||||||
|
if outfile == "-" {
|
||||||
|
println!("{}", serialized_keypair);
|
||||||
|
} else {
|
||||||
|
println!("Wrote recovered keypair to {}", outfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -133,8 +133,8 @@ pub fn read_keypair(path: &str) -> Result<Keypair, Box<dyn error::Error>> {
|
|||||||
Ok(keypair)
|
Ok(keypair)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_keypair_file(outfile: &str) -> Result<String, Box<dyn error::Error>> {
|
pub fn write_keypair(keypair: &Keypair, outfile: &str) -> Result<String, Box<dyn error::Error>> {
|
||||||
let keypair_bytes = Keypair::new().to_bytes();
|
let keypair_bytes = keypair.to_bytes();
|
||||||
let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
|
let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
|
||||||
|
|
||||||
if outfile != "-" {
|
if outfile != "-" {
|
||||||
@ -147,6 +147,21 @@ pub fn gen_keypair_file(outfile: &str) -> Result<String, Box<dyn error::Error>>
|
|||||||
Ok(serialized)
|
Ok(serialized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
|
||||||
|
if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
|
||||||
|
return Err("Seed is too short".into());
|
||||||
|
}
|
||||||
|
let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let public = ed25519_dalek::PublicKey::from(&secret);
|
||||||
|
let keypair = Keypair { secret, public };
|
||||||
|
Ok(keypair)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_keypair_file(outfile: &str) -> Result<String, Box<dyn error::Error>> {
|
||||||
|
write_keypair(&Keypair::new(), outfile)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -178,6 +193,15 @@ mod tests {
|
|||||||
assert!(!Path::new(&outfile).exists());
|
assert!(!Path::new(&outfile).exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keypair_from_seed() {
|
||||||
|
let good_seed = vec![0; 32];
|
||||||
|
assert!(keypair_from_seed(&good_seed).is_ok());
|
||||||
|
|
||||||
|
let too_short_seed = vec![0; 31];
|
||||||
|
assert!(keypair_from_seed(&too_short_seed).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_signature_fromstr() {
|
fn test_signature_fromstr() {
|
||||||
let signature = Keypair::new().sign_message(&[0u8]);
|
let signature = Keypair::new().sign_message(&[0u8]);
|
||||||
|
Reference in New Issue
Block a user