From 4b3bc587abed6c413b631ff4050d2e8c16706d2b Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 30 Dec 2019 22:57:47 -0700 Subject: [PATCH] Add input validation for --creation-time/--lockup-date args (#7646) automerge --- Cargo.lock | 1 + clap-utils/src/input_parsers.rs | 5 ++++- clap-utils/src/input_validators.rs | 7 +++++++ cli/src/stake.rs | 3 ++- genesis/Cargo.toml | 1 + genesis/src/main.rs | 17 ++++++++++------- 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ae1ef97ed..8213da8588 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3489,6 +3489,7 @@ name = "solana-genesis" version = "0.23.0" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/clap-utils/src/input_parsers.rs b/clap-utils/src/input_parsers.rs index e8ffc5755a..4317f73ab9 100644 --- a/clap-utils/src/input_parsers.rs +++ b/clap-utils/src/input_parsers.rs @@ -33,7 +33,10 @@ where } } -pub fn unix_timestamp_of(matches: &ArgMatches<'_>, name: &str) -> Option { +pub fn unix_timestamp_from_rfc3339_datetime( + matches: &ArgMatches<'_>, + name: &str, +) -> Option { matches.value_of(name).and_then(|value| { DateTime::parse_from_rfc3339(value) .ok() diff --git a/clap-utils/src/input_validators.rs b/clap-utils/src/input_validators.rs index 2b29a6c323..26907f5374 100644 --- a/clap-utils/src/input_validators.rs +++ b/clap-utils/src/input_validators.rs @@ -1,4 +1,5 @@ use crate::keypair::ASK_KEYWORD; +use chrono::DateTime; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{read_keypair_file, Signature}; @@ -129,3 +130,9 @@ pub fn is_amount(amount: String) -> Result<(), String> { )) } } + +pub fn is_rfc3339_datetime(value: String) -> Result<(), String> { + DateTime::parse_from_rfc3339(&value) + .map(|_| ()) + .map_err(|e| format!("{:?}", e)) +} diff --git a/cli/src/stake.rs b/cli/src/stake.rs index a2d77b824e..5ef01abae3 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -86,6 +86,7 @@ impl StakeSubCommands for App<'_, '_> { Arg::with_name("lockup_date") .long("lockup-date") .value_name("RFC3339 DATE TIME") + .validator(is_rfc3339_datetime) .takes_value(true) .help("The date and time at which this account will be available for withdrawal") ) @@ -367,7 +368,7 @@ impl StakeSubCommands for App<'_, '_> { pub fn parse_stake_create_account(matches: &ArgMatches<'_>) -> Result { let stake_account = keypair_of(matches, "stake_account").unwrap(); let epoch = value_of(&matches, "lockup_epoch").unwrap_or(0); - let unix_timestamp = unix_timestamp_of(&matches, "lockup_date").unwrap_or(0); + let unix_timestamp = unix_timestamp_from_rfc3339_datetime(&matches, "lockup_date").unwrap_or(0); let custodian = pubkey_of(matches, "custodian").unwrap_or_default(); let staker = pubkey_of(matches, "authorized_staker"); let withdrawer = pubkey_of(matches, "authorized_withdrawer"); diff --git a/genesis/Cargo.toml b/genesis/Cargo.toml index 01d93faae6..c3f53f27ea 100644 --- a/genesis/Cargo.toml +++ b/genesis/Cargo.toml @@ -11,6 +11,7 @@ homepage = "https://solana.com/" [dependencies] base64 = "0.11.0" clap = "2.33.0" +chrono = "0.4" hex = "0.4.0" serde = "1.0.104" serde_derive = "1.0.103" diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 49656959e2..353a88cde4 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -1,9 +1,10 @@ //! A command-line executable for generating the chain's genesis config. +use chrono::{TimeZone, Utc}; use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches}; use solana_clap_utils::{ - input_parsers::{pubkey_of, unix_timestamp_of}, - input_validators::is_valid_percentage, + input_parsers::{pubkey_of, unix_timestamp_from_rfc3339_datetime}, + input_validators::{is_rfc3339_datetime, is_valid_percentage}, }; use solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account}; use solana_ledger::{blocktree::create_new_ledger, poh::compute_hashes_per_tick}; @@ -140,8 +141,9 @@ fn main() -> Result<(), Box> { Arg::with_name("creation_time") .long("creation-time") .value_name("RFC3339 DATE TIME") + .validator(is_rfc3339_datetime) .takes_value(true) - .help("Time when the bootrap leader will start, defaults to current system time"), + .help("Time when the bootstrap leader will start the cluster [default: current system time]"), ) .arg( Arg::with_name("bootstrap_leader_pubkey_file") @@ -246,7 +248,7 @@ fn main() -> Result<(), Box> { .default_value(default_lamports_per_byte_year) .help( "The cost in lamports that the cluster will charge per byte per year \ - for accounts with data.", + for accounts with data", ), ) .arg( @@ -257,7 +259,7 @@ fn main() -> Result<(), Box> { .default_value(default_rent_exemption_threshold) .help( "amount of time (in years) the balance has to include rent for \ - to qualify as rent exempted account.", + to qualify as rent exempted account", ), ) .arg( @@ -486,7 +488,7 @@ fn main() -> Result<(), Box> { ..GenesisConfig::default() }; - if let Some(creation_time) = unix_timestamp_of(&matches, "creation_time") { + if let Some(creation_time) = unix_timestamp_from_rfc3339_datetime(&matches, "creation_time") { genesis_config.creation_time = creation_time; } @@ -518,8 +520,9 @@ fn main() -> Result<(), Box> { create_new_ledger(&ledger_path, &genesis_config)?; println!( - "Genesis hash: {}\nOperating mode: {:?}\nHashes per tick: {:?}\nSlots per epoch: {}\nCapitalization: {} SOL in {} accounts", + "Genesis hash: {}\nCreation time: {}\nOperating mode: {:?}\nHashes per tick: {:?}\nSlots per epoch: {}\nCapitalization: {} SOL in {} accounts", genesis_config.hash(), + Utc.timestamp(genesis_config.creation_time, 0).to_rfc3339(), operating_mode, genesis_config.poh_config.hashes_per_tick, slots_per_epoch,