diff --git a/Cargo.lock b/Cargo.lock index 3d257c2d2a0640..27b062cdf4d7ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -808,6 +808,16 @@ dependencies = [ "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dialoguer" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "console 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "diff" version = "0.1.11" @@ -4019,6 +4029,7 @@ dependencies = [ "rpassword 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "solana-clap-utils 0.24.0", "solana-cli-config 0.24.0", + "solana-remote-wallet 0.24.0", "solana-sdk 0.24.0", "tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4325,6 +4336,7 @@ name = "solana-remote-wallet" version = "0.24.0" dependencies = [ "base32 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dialoguer 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "hidapi 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4332,7 +4344,6 @@ dependencies = [ "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "solana-sdk 0.24.0", "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", - "titlecase 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5364,15 +5375,6 @@ dependencies = [ "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "titlecase" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "tokio" version = "0.1.22" @@ -6158,6 +6160,7 @@ dependencies = [ "checksum curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" "checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" "checksum derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a" +"checksum dialoguer 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94616e25d2c04fc97253d145f6ca33ad84a584258dc70c4e621cc79a57f903b6" "checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" @@ -6517,7 +6520,6 @@ dependencies = [ "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" "checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" -"checksum titlecase 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f565e410cfc24c2f2a89960b023ca192689d7f77d3f8d4f4af50c2d8affe1117" "checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" "checksum tokio 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1bef565a52394086ecac0a6fa3b8ace4cb3a138ee1d96bd2b93283b56824e3" "checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" diff --git a/keygen/Cargo.toml b/keygen/Cargo.toml index 476481424661ff..b681364a368f80 100644 --- a/keygen/Cargo.toml +++ b/keygen/Cargo.toml @@ -16,6 +16,7 @@ num_cpus = "1.12.0" rpassword = "4.0" solana-clap-utils = { path = "../clap-utils", version = "0.24.0" } solana-cli-config = { path = "../cli-config", version = "0.24.0" } +solana-remote-wallet = { path = "../remote-wallet", version = "0.24.0" } solana-sdk = { path = "../sdk", version = "0.24.0" } tiny-bip39 = "0.7.0" diff --git a/keygen/src/keygen.rs b/keygen/src/keygen.rs index 0ee3ed33b256e5..96aaef3d2d9963 100644 --- a/keygen/src/keygen.rs +++ b/keygen/src/keygen.rs @@ -5,12 +5,20 @@ use clap::{ SubCommand, }; use num_cpus; -use solana_clap_utils::keypair::{ - keypair_from_seed_phrase, prompt_passphrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG, +use solana_clap_utils::{ + input_parsers::derivation_of, + input_validators::is_derivation, + keypair::{ + keypair_from_seed_phrase, prompt_passphrase, ASK_KEYWORD, SKIP_SEED_PHRASE_VALIDATION_ARG, + }, }; use solana_cli_config::config::{Config, CONFIG_FILE}; +use solana_remote_wallet::{ + ledger::get_ledger_from_info, + remote_wallet::{RemoteWallet, RemoteWalletInfo}, +}; use solana_sdk::{ - pubkey::write_pubkey_file, + pubkey::{write_pubkey_file, Pubkey}, signature::{ keypair_from_seed, read_keypair, read_keypair_file, write_keypair, write_keypair_file, Keypair, KeypairUtil, Signature, @@ -65,11 +73,47 @@ fn get_keypair_from_matches( } else if keypair == ASK_KEYWORD { let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name); keypair_from_seed_phrase("pubkey recovery", skip_validation, false) + } else if keypair.starts_with("usb://") { + Err(String::from("Remote wallet signing not yet implemented").into()) } else { read_keypair_file(keypair) } } +fn get_pubkey_from_matches( + matches: &ArgMatches, + config: Config, +) -> Result> { + let mut path = dirs::home_dir().expect("home directory"); + let keypair = if matches.is_present("keypair") { + matches.value_of("keypair").unwrap() + } else if config.keypair_path != "" { + &config.keypair_path + } else { + path.extend(&[".config", "solana", "id.json"]); + path.to_str().unwrap() + }; + + if keypair == "-" { + let mut stdin = std::io::stdin(); + read_keypair(&mut stdin).map(|keypair| keypair.pubkey()) + } else if keypair == ASK_KEYWORD { + let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name); + keypair_from_seed_phrase("pubkey recovery", skip_validation, false) + .map(|keypair| keypair.pubkey()) + } else if keypair.starts_with("usb://") { + let (remote_wallet_info, mut derivation_path) = + RemoteWalletInfo::parse_path(keypair.to_string())?; + if let Some(derivation) = derivation_of(matches, "derivation_path") { + derivation_path = derivation; + } + let ledger = get_ledger_from_info(remote_wallet_info)?; + Ok(ledger.get_pubkey(derivation_path)?) + } else { + read_keypair_file(keypair).map(|keypair| keypair.pubkey()) + } +} + fn output_keypair( keypair: &Keypair, outfile: &str, @@ -326,7 +370,7 @@ fn main() -> Result<(), Box> { .index(1) .value_name("PATH") .takes_value(true) - .help("Path to keypair file"), + .help("Path to keypair file or remote wallet"), ) .arg( Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name) @@ -346,6 +390,14 @@ fn main() -> Result<(), Box> { .short("f") .long("force") .help("Overwrite the output file if it exists"), + ) + .arg( + Arg::with_name("derivation_path") + .long("derivation-path") + .value_name("ACCOUNT or ACCOUNT/CHANGE") + .takes_value(true) + .validator(is_derivation) + .help("Derivation path to use: m/44'/501'/ACCOUNT'/CHANGE'; default key is device base pubkey: m/44'/501'/0'") ), ) .subcommand( @@ -382,14 +434,14 @@ fn main() -> Result<(), Box> { match matches.subcommand() { ("pubkey", Some(matches)) => { - let keypair = get_keypair_from_matches(matches, config)?; + let pubkey = get_pubkey_from_matches(matches, config)?; if matches.is_present("outfile") { let outfile = matches.value_of("outfile").unwrap(); check_for_overwrite(&outfile, &matches); - write_pubkey_file(outfile, keypair.pubkey())?; + write_pubkey_file(outfile, pubkey)?; } else { - println!("{}", keypair.pubkey()); + println!("{}", pubkey); } } ("new", Some(matches)) => {