Allow secure keypair input for solana-archiver
and solana
cli tools (#7106)
* Add seed phrase keypair recover to archiver * Add seed phrase keypair to cli with ASK keyword * cli main tweaks
This commit is contained in:
@ -219,19 +219,28 @@ pub struct CliConfig {
|
||||
pub rpc_client: Option<RpcClient>,
|
||||
}
|
||||
|
||||
impl Default for CliConfig {
|
||||
fn default() -> CliConfig {
|
||||
impl CliConfig {
|
||||
pub fn default_keypair_path() -> String {
|
||||
let mut keypair_path = dirs::home_dir().expect("home directory");
|
||||
keypair_path.extend(&[".config", "solana", "id.json"]);
|
||||
keypair_path.to_str().unwrap().to_string()
|
||||
}
|
||||
|
||||
pub fn default_json_rpc_url() -> String {
|
||||
"http://127.0.0.1:8899".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CliConfig {
|
||||
fn default() -> CliConfig {
|
||||
CliConfig {
|
||||
command: CliCommand::Balance {
|
||||
pubkey: Some(Pubkey::default()),
|
||||
use_lamports_unit: false,
|
||||
},
|
||||
json_rpc_url: "http://127.0.0.1:8899".to_string(),
|
||||
json_rpc_url: Self::default_json_rpc_url(),
|
||||
keypair: Keypair::new(),
|
||||
keypair_path: Some(keypair_path.to_str().unwrap().to_string()),
|
||||
keypair_path: Some(Self::default_keypair_path()),
|
||||
rpc_client: None,
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,14 @@ lazy_static! {
|
||||
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
|
||||
pub struct Config {
|
||||
pub url: String,
|
||||
pub keypair: String,
|
||||
pub keypair_path: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(url: &str, keypair: &str) -> Self {
|
||||
pub fn new(url: &str, keypair_path: &str) -> Self {
|
||||
Self {
|
||||
url: url.to_string(),
|
||||
keypair: keypair.to_string(),
|
||||
keypair_path: keypair_path.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,13 @@
|
||||
use clap::{crate_description, crate_name, Arg, ArgGroup, ArgMatches, SubCommand};
|
||||
use console::style;
|
||||
|
||||
use solana_clap_utils::input_validators::is_url;
|
||||
use solana_clap_utils::{
|
||||
input_validators::is_url,
|
||||
keypair::{
|
||||
self, keypair_input, KeypairWithSource, ASK_SEED_PHRASE_ARG,
|
||||
SKIP_SEED_PHRASE_VALIDATION_ARG,
|
||||
},
|
||||
};
|
||||
use solana_cli::{
|
||||
cli::{app, parse_command, process_command, CliCommandInfo, CliConfig, CliError},
|
||||
config::{self, Config},
|
||||
@ -15,22 +21,25 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
||||
let parse_args = match matches.subcommand() {
|
||||
("get", Some(subcommand_matches)) => {
|
||||
if let Some(config_file) = matches.value_of("config_file") {
|
||||
let default_cli_config = CliConfig::default();
|
||||
let config = Config::load(config_file).unwrap_or_default();
|
||||
if let Some(field) = subcommand_matches.value_of("specific_setting") {
|
||||
let (value, default_value) = match field {
|
||||
"url" => (config.url, default_cli_config.json_rpc_url),
|
||||
"keypair" => (config.keypair, default_cli_config.keypair_path.unwrap()),
|
||||
"url" => (config.url, CliConfig::default_json_rpc_url()),
|
||||
"keypair" => (config.keypair_path, CliConfig::default_keypair_path()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
println_name_value_or(&format!("* {}:", field), &value, &default_value);
|
||||
} else {
|
||||
println_name_value("Wallet Config:", config_file);
|
||||
println_name_value_or("* url:", &config.url, &default_cli_config.json_rpc_url);
|
||||
println_name_value_or(
|
||||
"* url:",
|
||||
&config.url,
|
||||
&CliConfig::default_json_rpc_url(),
|
||||
);
|
||||
println_name_value_or(
|
||||
"* keypair:",
|
||||
&config.keypair,
|
||||
&default_cli_config.keypair_path.unwrap(),
|
||||
&config.keypair_path,
|
||||
&CliConfig::default_keypair_path(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@ -48,12 +57,12 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
|
||||
config.url = url.to_string();
|
||||
}
|
||||
if let Some(keypair) = subcommand_matches.value_of("keypair") {
|
||||
config.keypair = keypair.to_string();
|
||||
config.keypair_path = keypair.to_string();
|
||||
}
|
||||
config.save(config_file)?;
|
||||
println_name_value("Wallet Config Updated:", config_file);
|
||||
println_name_value("* url:", &config.url);
|
||||
println_name_value("* keypair:", &config.keypair);
|
||||
println_name_value("* keypair:", &config.keypair_path);
|
||||
} else {
|
||||
println!(
|
||||
"{} Either provide the `--config` arg or ensure home directory exists to use the default config location",
|
||||
@ -88,28 +97,37 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
|
||||
} = parse_command(&matches)?;
|
||||
|
||||
let (keypair, keypair_path) = if require_keypair {
|
||||
let keypair_path = if matches.is_present("keypair") {
|
||||
matches.value_of("keypair").unwrap().to_string()
|
||||
} else if config.keypair != "" {
|
||||
config.keypair
|
||||
} else {
|
||||
let default = CliConfig::default();
|
||||
let maybe_keypair_path = default.keypair_path.unwrap();
|
||||
if !std::path::Path::new(&maybe_keypair_path).exists() {
|
||||
return Err(CliError::KeypairFileNotFound(
|
||||
"Generate a new keypair with `solana-keygen new`".to_string(),
|
||||
)
|
||||
.into());
|
||||
let KeypairWithSource { keypair, source } = keypair_input(&matches, "keypair")?;
|
||||
match source {
|
||||
keypair::Source::File => (
|
||||
keypair,
|
||||
Some(matches.value_of("keypair").unwrap().to_string()),
|
||||
),
|
||||
keypair::Source::SeedPhrase => (keypair, None),
|
||||
keypair::Source::Generated => {
|
||||
let keypair_path = if config.keypair_path != "" {
|
||||
config.keypair_path
|
||||
} else {
|
||||
let default_keypair_path = CliConfig::default_keypair_path();
|
||||
if !std::path::Path::new(&default_keypair_path).exists() {
|
||||
return Err(CliError::KeypairFileNotFound(
|
||||
"Generate a new keypair with `solana-keygen new`".to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
default_keypair_path
|
||||
};
|
||||
|
||||
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
|
||||
Err(CliError::BadParameter(format!(
|
||||
"{}: Unable to open keypair file: {}",
|
||||
err, keypair_path
|
||||
)))
|
||||
})?;
|
||||
|
||||
(keypair, Some(keypair_path))
|
||||
}
|
||||
maybe_keypair_path
|
||||
};
|
||||
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
|
||||
Err(CliError::BadParameter(format!(
|
||||
"{}: Unable to open keypair file: {}",
|
||||
err, keypair_path
|
||||
)))
|
||||
})?;
|
||||
(keypair, Some(keypair_path.to_string()))
|
||||
}
|
||||
} else {
|
||||
let default = CliConfig::default();
|
||||
(default.keypair, None)
|
||||
@ -164,6 +182,21 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
.takes_value(true)
|
||||
.help("/path/to/id.json"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ASK_SEED_PHRASE_ARG.name)
|
||||
.long(ASK_SEED_PHRASE_ARG.long)
|
||||
.value_name("KEYPAIR NAME")
|
||||
.global(true)
|
||||
.takes_value(true)
|
||||
.possible_values(&["keypair"])
|
||||
.help(ASK_SEED_PHRASE_ARG.help),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
|
||||
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
|
||||
.global(true)
|
||||
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("get")
|
||||
.about("Get cli config settings")
|
||||
|
@ -41,7 +41,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||
.value_name("STAKE ACCOUNT")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_keypair)
|
||||
.validator(is_keypair_or_ask_keyword)
|
||||
.help("Keypair of the stake account to fund")
|
||||
)
|
||||
.arg(
|
||||
|
@ -35,7 +35,7 @@ impl StorageSubCommands for App<'_, '_> {
|
||||
.value_name("STORAGE ACCOUNT")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_keypair),
|
||||
.validator(is_keypair_or_ask_keyword),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
@ -55,7 +55,7 @@ impl StorageSubCommands for App<'_, '_> {
|
||||
.value_name("STORAGE ACCOUNT")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_keypair),
|
||||
.validator(is_keypair_or_ask_keyword),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
|
@ -30,7 +30,7 @@ impl VoteSubCommands for App<'_, '_> {
|
||||
.value_name("VOTE ACCOUNT KEYPAIR")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.validator(is_keypair)
|
||||
.validator(is_keypair_or_ask_keyword)
|
||||
.help("Vote account keypair to fund"),
|
||||
)
|
||||
.arg(
|
||||
|
Reference in New Issue
Block a user