2019-09-12 18:37:29 -07:00
use bip39 ::{ Language , Mnemonic , MnemonicType , Seed } ;
2019-06-07 17:54:54 -07:00
use clap ::{
2019-12-03 20:39:45 -08:00
crate_description , crate_name , value_t , values_t_or_exit , App , AppSettings , Arg , ArgMatches ,
SubCommand ,
2019-06-07 17:54:54 -07:00
} ;
2020-02-07 10:26:56 -08:00
use solana_clap_utils ::{
keypair ::{
2020-04-18 11:54:21 -07:00
keypair_from_seed_phrase , prompt_passphrase , signer_from_path ,
2020-02-13 13:08:35 -08:00
SKIP_SEED_PHRASE_VALIDATION_ARG ,
2020-02-07 10:26:56 -08:00
} ,
2020-03-12 23:20:49 -07:00
DisplayError ,
2019-11-26 12:30:07 -08:00
} ;
2020-03-09 12:29:31 -07:00
use solana_cli_config ::{ Config , CONFIG_FILE } ;
2020-04-18 11:54:21 -07:00
use solana_remote_wallet ::remote_wallet ::RemoteWalletManager ;
2019-11-03 19:41:26 -08:00
use solana_sdk ::{
2020-02-27 18:23:28 -08:00
instruction ::{ AccountMeta , Instruction } ,
message ::Message ,
pubkey ::{ write_pubkey_file , Pubkey } ,
2020-02-21 13:55:53 -08:00
signature ::{ keypair_from_seed , write_keypair , write_keypair_file , Keypair , Signer } ,
2019-11-03 19:41:26 -08:00
} ;
use std ::{
collections ::HashSet ,
error ,
path ::Path ,
process ::exit ,
sync ::{
2020-01-28 20:19:19 -08:00
atomic ::{ AtomicBool , AtomicU64 , Ordering } ,
2019-11-03 19:41:26 -08:00
Arc ,
} ,
thread ,
time ::Instant ,
2019-10-10 16:01:03 -07:00
} ;
2019-06-07 17:54:54 -07:00
2019-09-12 18:37:29 -07:00
const NO_PASSPHRASE : & str = " " ;
2020-01-28 20:19:19 -08:00
struct GrindMatch {
starts : String ,
ends : String ,
count : AtomicU64 ,
}
2019-06-07 17:54:54 -07:00
fn check_for_overwrite ( outfile : & str , matches : & ArgMatches ) {
let force = matches . is_present ( " force " ) ;
if ! force & & Path ::new ( outfile ) . exists ( ) {
eprintln! ( " Refusing to overwrite {} without --force flag " , outfile ) ;
exit ( 1 ) ;
}
}
2018-07-12 10:59:40 -07:00
2020-01-31 18:27:37 -08:00
fn get_keypair_from_matches (
matches : & ArgMatches ,
config : Config ,
2020-04-18 11:54:21 -07:00
wallet_manager : & mut Option < Arc < RemoteWalletManager > > ,
2020-02-20 13:28:55 -08:00
) -> Result < Box < dyn Signer > , Box < dyn error ::Error > > {
2019-12-05 14:32:42 -08:00
let mut path = dirs ::home_dir ( ) . expect ( " home directory " ) ;
2020-02-13 13:08:35 -08:00
let path = if matches . is_present ( " keypair " ) {
2020-01-31 18:27:37 -08:00
matches . value_of ( " keypair " ) . unwrap ( )
} else if config . keypair_path ! = " " {
& config . keypair_path
2019-12-05 14:32:42 -08:00
} else {
path . extend ( & [ " .config " , " solana " , " id.json " ] ) ;
path . to_str ( ) . unwrap ( )
} ;
2020-04-18 11:54:21 -07:00
signer_from_path ( matches , path , " pubkey recovery " , wallet_manager )
2020-02-07 10:26:56 -08:00
}
2019-10-10 16:01:03 -07:00
fn output_keypair (
keypair : & Keypair ,
outfile : & str ,
source : & str ,
) -> Result < ( ) , Box < dyn error ::Error > > {
if outfile = = " - " {
let mut stdout = std ::io ::stdout ( ) ;
write_keypair ( & keypair , & mut stdout ) ? ;
} else {
write_keypair_file ( & keypair , outfile ) ? ;
2020-05-11 08:39:10 -07:00
println! ( " Wrote {} keypair to {} " , source , outfile ) ;
2019-10-10 16:01:03 -07:00
}
Ok ( ( ) )
}
2020-01-28 20:19:19 -08:00
fn grind_validator_starts_with ( v : String ) -> Result < ( ) , String > {
if v . matches ( ':' ) . count ( ) ! = 1 | | ( v . starts_with ( ':' ) | | v . ends_with ( ':' ) ) {
return Err ( String ::from ( " Expected : between PREFIX and COUNT " ) ) ;
}
let args : Vec < & str > = v . split ( ':' ) . collect ( ) ;
bs58 ::decode ( & args [ 0 ] )
. into_vec ( )
. map_err ( | err | format! ( " {} : {:?} " , args [ 0 ] , err ) ) ? ;
let count = args [ 1 ] . parse ::< u64 > ( ) ;
if count . is_err ( ) | | count . unwrap ( ) = = 0 {
return Err ( String ::from ( " Expected COUNT to be of type u64 " ) ) ;
}
Ok ( ( ) )
}
fn grind_validator_ends_with ( v : String ) -> Result < ( ) , String > {
if v . matches ( ':' ) . count ( ) ! = 1 | | ( v . starts_with ( ':' ) | | v . ends_with ( ':' ) ) {
return Err ( String ::from ( " Expected : between SUFFIX and COUNT " ) ) ;
}
let args : Vec < & str > = v . split ( ':' ) . collect ( ) ;
bs58 ::decode ( & args [ 0 ] )
. into_vec ( )
. map_err ( | err | format! ( " {} : {:?} " , args [ 0 ] , err ) ) ? ;
let count = args [ 1 ] . parse ::< u64 > ( ) ;
if count . is_err ( ) | | count . unwrap ( ) = = 0 {
return Err ( String ::from ( " Expected COUNT to be of type u64 " ) ) ;
}
Ok ( ( ) )
}
fn grind_validator_starts_and_ends_with ( v : String ) -> Result < ( ) , String > {
if v . matches ( ':' ) . count ( ) ! = 2 | | ( v . starts_with ( ':' ) | | v . ends_with ( ':' ) ) {
return Err ( String ::from (
" Expected : between PREFIX and SUFFIX and COUNT " ,
) ) ;
}
let args : Vec < & str > = v . split ( ':' ) . collect ( ) ;
bs58 ::decode ( & args [ 0 ] )
. into_vec ( )
. map_err ( | err | format! ( " {} : {:?} " , args [ 0 ] , err ) ) ? ;
bs58 ::decode ( & args [ 1 ] )
. into_vec ( )
. map_err ( | err | format! ( " {} : {:?} " , args [ 1 ] , err ) ) ? ;
let count = args [ 2 ] . parse ::< u64 > ( ) ;
if count . is_err ( ) | | count . unwrap ( ) = = 0 {
return Err ( String ::from ( " Expected COUNT to be a u64 " ) ) ;
}
Ok ( ( ) )
}
fn grind_print_info ( grind_matches : & [ GrindMatch ] ) {
println! ( " Searching with {} threads for: " , num_cpus ::get ( ) ) ;
for gm in grind_matches {
let mut msg = Vec ::< String > ::new ( ) ;
if gm . count . load ( Ordering ::Relaxed ) > 1 {
msg . push ( " pubkeys " . to_string ( ) ) ;
msg . push ( " start " . to_string ( ) ) ;
msg . push ( " end " . to_string ( ) ) ;
} else {
msg . push ( " pubkey " . to_string ( ) ) ;
msg . push ( " starts " . to_string ( ) ) ;
msg . push ( " ends " . to_string ( ) ) ;
}
println! (
" \t {} {} that {} with '{}' and {} with '{}' " ,
gm . count . load ( Ordering ::Relaxed ) ,
msg [ 0 ] ,
msg [ 1 ] ,
gm . starts ,
msg [ 2 ] ,
gm . ends
) ;
}
}
fn grind_parse_args (
2020-03-17 07:00:37 -07:00
ignore_case : bool ,
2020-01-28 20:19:19 -08:00
starts_with_args : HashSet < String > ,
ends_with_args : HashSet < String > ,
starts_and_ends_with_args : HashSet < String > ,
) -> Vec < GrindMatch > {
let mut grind_matches = Vec ::< GrindMatch > ::new ( ) ;
for sw in starts_with_args {
let args : Vec < & str > = sw . split ( ':' ) . collect ( ) ;
grind_matches . push ( GrindMatch {
2020-03-17 07:29:17 -07:00
starts : if ignore_case {
args [ 0 ] . to_lowercase ( )
} else {
args [ 0 ] . to_string ( )
} ,
2020-01-28 20:19:19 -08:00
ends : " " . to_string ( ) ,
count : AtomicU64 ::new ( args [ 1 ] . parse ::< u64 > ( ) . unwrap ( ) ) ,
} ) ;
}
for ew in ends_with_args {
let args : Vec < & str > = ew . split ( ':' ) . collect ( ) ;
grind_matches . push ( GrindMatch {
starts : " " . to_string ( ) ,
2020-03-17 07:29:17 -07:00
ends : if ignore_case {
args [ 0 ] . to_lowercase ( )
} else {
args [ 0 ] . to_string ( )
} ,
2020-01-28 20:19:19 -08:00
count : AtomicU64 ::new ( args [ 1 ] . parse ::< u64 > ( ) . unwrap ( ) ) ,
} ) ;
}
for swew in starts_and_ends_with_args {
let args : Vec < & str > = swew . split ( ':' ) . collect ( ) ;
grind_matches . push ( GrindMatch {
2020-03-17 07:29:17 -07:00
starts : if ignore_case {
args [ 0 ] . to_lowercase ( )
} else {
args [ 0 ] . to_string ( )
} ,
ends : if ignore_case {
args [ 1 ] . to_lowercase ( )
} else {
args [ 1 ] . to_string ( )
} ,
2020-01-28 20:19:19 -08:00
count : AtomicU64 ::new ( args [ 2 ] . parse ::< u64 > ( ) . unwrap ( ) ) ,
} ) ;
}
grind_print_info ( & grind_matches ) ;
grind_matches
}
2018-12-08 21:44:20 -08:00
fn main ( ) -> Result < ( ) , Box < dyn error ::Error > > {
2019-03-13 20:54:30 -07:00
let matches = App ::new ( crate_name! ( ) )
. about ( crate_description! ( ) )
2020-05-11 15:02:01 -07:00
. version ( solana_version ::version! ( ) )
2019-06-07 17:54:54 -07:00
. setting ( AppSettings ::SubcommandRequiredElseHelp )
2020-01-31 18:27:37 -08:00
. arg ( {
let arg = Arg ::with_name ( " config_file " )
. short ( " C " )
. long ( " config " )
2020-03-16 08:24:59 -07:00
. value_name ( " FILEPATH " )
2020-01-31 18:27:37 -08:00
. takes_value ( true )
. global ( true )
. help ( " Configuration file to use " ) ;
if let Some ( ref config_file ) = * CONFIG_FILE {
arg . default_value ( & config_file )
} else {
arg
}
} )
2019-12-05 14:32:42 -08:00
. subcommand (
SubCommand ::with_name ( " verify " )
. about ( " Verify a keypair can sign and verify a message. " )
. arg (
2020-01-31 18:27:37 -08:00
Arg ::with_name ( " pubkey " )
2019-12-05 14:32:42 -08:00
. index ( 1 )
2020-03-16 08:24:59 -07:00
. value_name ( " PUBKEY " )
2019-12-05 14:32:42 -08:00
. takes_value ( true )
2020-01-31 18:27:37 -08:00
. required ( true )
. help ( " Public key " ) ,
2019-12-05 14:32:42 -08:00
)
. arg (
2020-01-31 18:27:37 -08:00
Arg ::with_name ( " keypair " )
2019-12-05 14:32:42 -08:00
. index ( 2 )
2020-03-16 11:27:09 -07:00
. value_name ( " KEYPAIR " )
2019-12-05 14:32:42 -08:00
. takes_value ( true )
2020-03-16 11:27:09 -07:00
. help ( " Filepath or URL to a keypair " ) ,
2019-12-05 14:32:42 -08:00
)
)
2019-03-19 14:19:50 -07:00
. subcommand (
SubCommand ::with_name ( " new " )
2019-11-25 20:33:15 -08:00
. about ( " Generate new keypair file from a passphrase and random seed phrase " )
2019-06-07 17:54:54 -07:00
. setting ( AppSettings ::DisableVersion )
2019-03-19 14:19:50 -07:00
. arg (
Arg ::with_name ( " outfile " )
. short ( " o " )
. long ( " outfile " )
2020-03-16 08:24:59 -07:00
. value_name ( " FILEPATH " )
2019-03-19 14:19:50 -07:00
. takes_value ( true )
. help ( " Path to generated file " ) ,
2019-06-07 17:54:54 -07:00
)
. arg (
Arg ::with_name ( " force " )
. short ( " f " )
. long ( " force " )
. help ( " Overwrite the output file if it exists " ) ,
2019-09-12 18:37:29 -07:00
)
2019-12-03 20:39:45 -08:00
. arg (
Arg ::with_name ( " word_count " )
. long ( " word-count " )
. possible_values ( & [ " 12 " , " 15 " , " 18 " , " 21 " , " 24 " ] )
. default_value ( " 12 " )
2020-03-16 08:24:59 -07:00
. value_name ( " NUMBER " )
2019-12-03 20:39:45 -08:00
. takes_value ( true )
. help ( " Specify the number of words that will be present in the generated seed phrase " ) ,
)
2019-11-25 20:33:15 -08:00
. arg (
Arg ::with_name ( " no_passphrase " )
. long ( " no-passphrase " )
. help ( " Do not prompt for a passphrase " ) ,
)
2019-11-25 21:43:03 -08:00
. arg (
Arg ::with_name ( " no_outfile " )
. long ( " no-outfile " )
. conflicts_with_all ( & [ " outfile " , " silent " ] )
. help ( " Only print a seed phrase and pubkey. Do not output a keypair file " ) ,
)
2019-09-12 18:37:29 -07:00
. arg (
Arg ::with_name ( " silent " )
. short ( " s " )
. long ( " silent " )
2019-11-25 20:33:15 -08:00
. help ( " Do not display seed phrase. Useful when piping output to other programs that prompt for user input, like gpg " ) ,
2019-10-30 20:47:42 -07:00
)
2019-11-03 19:41:26 -08:00
)
. subcommand (
SubCommand ::with_name ( " grind " )
. about ( " Grind for vanity keypairs " )
. setting ( AppSettings ::DisableVersion )
. arg (
Arg ::with_name ( " ignore_case " )
. long ( " ignore-case " )
2020-01-28 20:19:19 -08:00
. help ( " Performs case insensitive matches " ) ,
2019-11-03 19:41:26 -08:00
)
. arg (
2020-01-28 20:19:19 -08:00
Arg ::with_name ( " starts_with " )
. long ( " starts-with " )
. value_name ( " PREFIX:COUNT " )
. number_of_values ( 1 )
2019-11-03 19:41:26 -08:00
. takes_value ( true )
. multiple ( true )
2020-01-28 20:19:19 -08:00
. validator ( grind_validator_starts_with )
. help ( " Saves specified number of keypairs whos public key starts with the indicated prefix \n Example: --starts-with sol:4 \n PREFIX type is Base58 \n COUNT type is u64 " ) ,
2019-11-03 19:41:26 -08:00
)
2019-10-30 20:47:42 -07:00
. arg (
2020-01-28 20:19:19 -08:00
Arg ::with_name ( " ends_with " )
. long ( " ends-with " )
. value_name ( " SUFFIX:COUNT " )
. number_of_values ( 1 )
2019-10-30 20:47:42 -07:00
. takes_value ( true )
2019-11-03 19:41:26 -08:00
. multiple ( true )
2020-01-28 20:19:19 -08:00
. validator ( grind_validator_ends_with )
. help ( " Saves specified number of keypairs whos public key ends with the indicated suffix \n Example: --ends-with ana:4 \n SUFFIX type is Base58 \n COUNT type is u64 " ) ,
)
. arg (
Arg ::with_name ( " starts_and_ends_with " )
. long ( " starts-and-ends-with " )
. value_name ( " PREFIX:SUFFIX:COUNT " )
. number_of_values ( 1 )
. takes_value ( true )
. multiple ( true )
. validator ( grind_validator_starts_and_ends_with )
. help ( " Saves specified number of keypairs whos public key starts and ends with the indicated perfix and suffix \n Example: --starts-and-ends-with sol:ana:4 \n PREFIX and SUFFIX type is Base58 \n COUNT type is u64 " ) ,
2019-03-19 14:19:50 -07:00
) ,
)
. subcommand (
SubCommand ::with_name ( " pubkey " )
2019-04-23 19:24:42 -07:00
. about ( " Display the pubkey from a keypair file " )
2019-06-07 17:54:54 -07:00
. setting ( AppSettings ::DisableVersion )
2019-03-19 14:19:50 -07:00
. arg (
2020-01-31 18:27:37 -08:00
Arg ::with_name ( " keypair " )
2019-03-19 14:19:50 -07:00
. index ( 1 )
2020-03-16 11:27:09 -07:00
. value_name ( " KEYPAIR " )
2019-03-19 14:19:50 -07:00
. takes_value ( true )
2020-03-16 11:27:09 -07:00
. help ( " Filepath or URL to a keypair " ) ,
2019-03-19 14:19:50 -07:00
)
2019-11-26 12:30:07 -08:00
. arg (
Arg ::with_name ( SKIP_SEED_PHRASE_VALIDATION_ARG . name )
. long ( SKIP_SEED_PHRASE_VALIDATION_ARG . long )
. help ( SKIP_SEED_PHRASE_VALIDATION_ARG . help ) ,
)
2019-03-19 14:19:50 -07:00
. arg (
Arg ::with_name ( " outfile " )
. short ( " o " )
. long ( " outfile " )
2020-03-16 08:24:59 -07:00
. value_name ( " FILEPATH " )
2019-03-19 14:19:50 -07:00
. takes_value ( true )
. help ( " Path to generated file " ) ,
2019-06-07 17:54:54 -07:00
)
. arg (
Arg ::with_name ( " force " )
. short ( " f " )
. long ( " force " )
. help ( " Overwrite the output file if it exists " ) ,
2020-02-07 10:26:56 -08:00
)
2019-03-19 14:19:50 -07:00
)
2019-09-12 18:37:29 -07:00
. subcommand (
SubCommand ::with_name ( " recover " )
2019-11-25 20:33:15 -08:00
. about ( " Recover keypair from seed phrase and passphrase " )
2019-09-12 18:37:29 -07:00
. setting ( AppSettings ::DisableVersion )
. arg (
Arg ::with_name ( " outfile " )
. short ( " o " )
. long ( " outfile " )
2020-03-16 08:24:59 -07:00
. value_name ( " FILEPATH " )
2019-09-12 18:37:29 -07:00
. 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 " ) ,
2019-11-25 20:33:15 -08:00
)
. arg (
Arg ::with_name ( SKIP_SEED_PHRASE_VALIDATION_ARG . name )
. long ( SKIP_SEED_PHRASE_VALIDATION_ARG . long )
. help ( SKIP_SEED_PHRASE_VALIDATION_ARG . help ) ,
2019-09-12 18:37:29 -07:00
) ,
2019-11-25 20:33:15 -08:00
2019-09-12 18:37:29 -07:00
)
2018-12-07 19:01:28 -08:00
. get_matches ( ) ;
2020-03-12 23:20:49 -07:00
do_main ( & matches ) . map_err ( | err | DisplayError ::new_as_boxed ( err ) . into ( ) )
}
fn do_main ( matches : & ArgMatches < '_ > ) -> Result < ( ) , Box < dyn error ::Error > > {
2020-01-31 18:27:37 -08:00
let config = if let Some ( config_file ) = matches . value_of ( " config_file " ) {
Config ::load ( config_file ) . unwrap_or_default ( )
} else {
Config ::default ( )
} ;
2018-07-12 15:29:49 -07:00
2020-04-18 11:54:21 -07:00
let mut wallet_manager = None ;
2020-02-24 16:03:30 -08:00
2019-03-19 14:19:50 -07:00
match matches . subcommand ( ) {
2019-06-07 17:54:54 -07:00
( " pubkey " , Some ( matches ) ) = > {
2020-04-18 11:54:21 -07:00
let pubkey =
get_keypair_from_matches ( matches , config , & mut wallet_manager ) ? . try_pubkey ( ) ? ;
2019-03-19 14:19:50 -07:00
2019-06-07 17:54:54 -07:00
if matches . is_present ( " outfile " ) {
let outfile = matches . value_of ( " outfile " ) . unwrap ( ) ;
check_for_overwrite ( & outfile , & matches ) ;
2020-02-07 10:26:56 -08:00
write_pubkey_file ( outfile , pubkey ) ? ;
2019-03-19 14:19:50 -07:00
} else {
2020-02-07 10:26:56 -08:00
println! ( " {} " , pubkey ) ;
2019-03-19 14:19:50 -07:00
}
}
2019-06-07 17:54:54 -07:00
( " new " , Some ( matches ) ) = > {
2019-03-19 14:19:50 -07:00
let mut path = dirs ::home_dir ( ) . expect ( " home directory " ) ;
2019-06-07 17:54:54 -07:00
let outfile = if matches . is_present ( " outfile " ) {
2019-11-25 21:43:03 -08:00
matches . value_of ( " outfile " )
} else if matches . is_present ( " no_outfile " ) {
None
2019-03-19 14:19:50 -07:00
} else {
path . extend ( & [ " .config " , " solana " , " id.json " ] ) ;
2019-11-25 21:43:03 -08:00
Some ( path . to_str ( ) . unwrap ( ) )
2019-03-19 14:19:50 -07:00
} ;
2019-11-25 21:43:03 -08:00
match outfile {
Some ( " - " ) = > ( ) ,
Some ( outfile ) = > check_for_overwrite ( & outfile , & matches ) ,
None = > ( ) ,
2019-06-07 17:54:54 -07:00
}
2019-09-12 18:37:29 -07:00
2019-12-03 20:39:45 -08:00
let word_count = value_t! ( matches . value_of ( " word_count " ) , usize ) . unwrap ( ) ;
let mnemonic_type = MnemonicType ::for_word_count ( word_count ) ? ;
let mnemonic = Mnemonic ::new ( mnemonic_type , Language ::English ) ;
2019-11-25 20:33:15 -08:00
let passphrase = if matches . is_present ( " no_passphrase " ) {
NO_PASSPHRASE . to_string ( )
} else {
2020-05-11 08:39:10 -07:00
println! ( " Generating a new keypair " ) ;
2019-12-02 19:42:42 -08:00
prompt_passphrase (
" For added security, enter a passphrase (empty for no passphrase): " ,
2019-11-25 20:33:15 -08:00
) ?
} ;
let seed = Seed ::new ( & mnemonic , & passphrase ) ;
2019-11-03 19:41:26 -08:00
let keypair = keypair_from_seed ( seed . as_bytes ( ) ) ? ;
2019-09-12 18:37:29 -07:00
2019-11-25 21:43:03 -08:00
if let Some ( outfile ) = outfile {
2020-04-16 10:10:59 -07:00
output_keypair ( & keypair , & outfile , " new " )
. map_err ( | err | format! ( " Unable to write {} : {} " , outfile , err ) ) ? ;
2019-11-25 21:43:03 -08:00
}
2019-09-12 18:37:29 -07:00
let silent = matches . is_present ( " silent " ) ;
if ! silent {
2019-10-30 20:47:42 -07:00
let phrase : & str = mnemonic . phrase ( ) ;
2019-09-12 18:37:29 -07:00
let divider = String ::from_utf8 ( vec! [ b '=' ; phrase . len ( ) ] ) . unwrap ( ) ;
2020-05-11 08:39:10 -07:00
println! (
2019-11-25 20:33:15 -08:00
" {} \n pubkey: {} \n {} \n Save this seed phrase to recover your new keypair: \n {} \n {} " ,
2019-11-03 19:41:26 -08:00
& divider , keypair . pubkey ( ) , & divider , phrase , & divider
2019-09-12 18:37:29 -07:00
) ;
}
}
( " 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 ) ;
}
2019-11-25 20:33:15 -08:00
let skip_validation = matches . is_present ( SKIP_SEED_PHRASE_VALIDATION_ARG . name ) ;
2019-12-06 06:55:00 -08:00
let keypair = keypair_from_seed_phrase ( " recover " , skip_validation , true ) ? ;
2019-10-10 16:01:03 -07:00
output_keypair ( & keypair , & outfile , " recovered " ) ? ;
2019-03-19 14:19:50 -07:00
}
2019-11-03 19:41:26 -08:00
( " grind " , Some ( matches ) ) = > {
2019-11-12 13:24:37 -08:00
let ignore_case = matches . is_present ( " ignore_case " ) ;
2020-01-28 20:19:19 -08:00
let starts_with_args = if matches . is_present ( " starts_with " ) {
values_t_or_exit! ( matches , " starts_with " , String )
2019-11-03 19:41:26 -08:00
. into_iter ( )
2019-11-12 13:24:37 -08:00
. map ( | s | if ignore_case { s . to_lowercase ( ) } else { s } )
2019-11-03 19:41:26 -08:00
. collect ( )
} else {
HashSet ::new ( )
} ;
2020-01-28 20:19:19 -08:00
let ends_with_args = if matches . is_present ( " ends_with " ) {
values_t_or_exit! ( matches , " ends_with " , String )
. into_iter ( )
. map ( | s | if ignore_case { s . to_lowercase ( ) } else { s } )
. collect ( )
} else {
HashSet ::new ( )
} ;
let starts_and_ends_with_args = if matches . is_present ( " starts_and_ends_with " ) {
values_t_or_exit! ( matches , " starts_and_ends_with " , String )
2019-11-03 19:41:26 -08:00
. into_iter ( )
2019-11-12 13:24:37 -08:00
. map ( | s | if ignore_case { s . to_lowercase ( ) } else { s } )
2019-11-03 19:41:26 -08:00
. collect ( )
} else {
HashSet ::new ( )
} ;
2020-01-28 20:19:19 -08:00
if starts_with_args . is_empty ( )
& & ends_with_args . is_empty ( )
& & starts_and_ends_with_args . is_empty ( )
{
2019-11-03 19:41:26 -08:00
eprintln! (
2020-01-28 20:19:19 -08:00
" Error: No keypair search criteria provided (--starts-with or --ends-with or --starts-and-ends-with) "
2019-11-03 19:41:26 -08:00
) ;
exit ( 1 ) ;
}
2020-03-17 07:37:08 -07:00
let grind_matches = grind_parse_args (
ignore_case ,
starts_with_args ,
ends_with_args ,
starts_and_ends_with_args ,
) ;
2020-01-28 20:19:19 -08:00
let grind_matches_thread_safe = Arc ::new ( grind_matches ) ;
2019-11-03 19:41:26 -08:00
let attempts = Arc ::new ( AtomicU64 ::new ( 1 ) ) ;
let found = Arc ::new ( AtomicU64 ::new ( 0 ) ) ;
let start = Instant ::now ( ) ;
2020-01-28 20:19:19 -08:00
let done = Arc ::new ( AtomicBool ::new ( false ) ) ;
2019-11-03 19:41:26 -08:00
2020-03-24 10:33:53 -07:00
let thread_handles : Vec < _ > = ( 0 .. num_cpus ::get ( ) )
. map ( | _ | {
let done = done . clone ( ) ;
let attempts = attempts . clone ( ) ;
let found = found . clone ( ) ;
let grind_matches_thread_safe = grind_matches_thread_safe . clone ( ) ;
2019-11-03 19:41:26 -08:00
2020-03-24 10:33:53 -07:00
thread ::spawn ( move | | loop {
if done . load ( Ordering ::Relaxed ) {
break ;
2019-11-03 19:41:26 -08:00
}
2020-03-24 10:33:53 -07:00
let attempts = attempts . fetch_add ( 1 , Ordering ::Relaxed ) ;
if attempts % 1_000_000 = = 0 {
println! (
" Searched {} keypairs in {}s. {} matches found. " ,
attempts ,
start . elapsed ( ) . as_secs ( ) ,
found . load ( Ordering ::Relaxed ) ,
) ;
2019-11-03 19:41:26 -08:00
}
2020-03-24 10:33:53 -07:00
let keypair = Keypair ::new ( ) ;
let mut pubkey = bs58 ::encode ( keypair . pubkey ( ) ) . into_string ( ) ;
if ignore_case {
pubkey = pubkey . to_lowercase ( ) ;
}
let mut total_matches_found = 0 ;
for i in 0 .. grind_matches_thread_safe . len ( ) {
if grind_matches_thread_safe [ i ] . count . load ( Ordering ::Relaxed ) = = 0 {
total_matches_found + = 1 ;
continue ;
}
if ( ! grind_matches_thread_safe [ i ] . starts . is_empty ( )
& & grind_matches_thread_safe [ i ] . ends . is_empty ( )
& & pubkey . starts_with ( & grind_matches_thread_safe [ i ] . starts ) )
| | ( grind_matches_thread_safe [ i ] . starts . is_empty ( )
& & ! grind_matches_thread_safe [ i ] . ends . is_empty ( )
& & pubkey . ends_with ( & grind_matches_thread_safe [ i ] . ends ) )
| | ( ! grind_matches_thread_safe [ i ] . starts . is_empty ( )
& & ! grind_matches_thread_safe [ i ] . ends . is_empty ( )
& & pubkey . starts_with ( & grind_matches_thread_safe [ i ] . starts )
& & pubkey . ends_with ( & grind_matches_thread_safe [ i ] . ends ) )
{
let _found = found . fetch_add ( 1 , Ordering ::Relaxed ) ;
grind_matches_thread_safe [ i ]
. count
. fetch_sub ( 1 , Ordering ::Relaxed ) ;
println! (
" Wrote keypair to {} " ,
& format! ( " {} .json " , keypair . pubkey ( ) )
) ;
write_keypair_file ( & keypair , & format! ( " {} .json " , keypair . pubkey ( ) ) )
. unwrap ( ) ;
}
}
if total_matches_found = = grind_matches_thread_safe . len ( ) {
done . store ( true , Ordering ::Relaxed ) ;
}
} )
} )
. collect ( ) ;
for thread_handle in thread_handles {
thread_handle . join ( ) . unwrap ( ) ;
2020-01-28 20:19:19 -08:00
}
2019-11-03 19:41:26 -08:00
}
2019-12-05 14:32:42 -08:00
( " verify " , Some ( matches ) ) = > {
2020-04-18 11:54:21 -07:00
let keypair = get_keypair_from_matches ( matches , config , & mut wallet_manager ) ? ;
2020-03-11 14:37:23 -07:00
let simple_message = Message ::new ( & [ Instruction ::new (
2020-02-27 18:23:28 -08:00
Pubkey ::default ( ) ,
& 0 ,
vec! [ AccountMeta ::new ( keypair . pubkey ( ) , true ) ] ,
) ] )
. serialize ( ) ;
let signature = keypair . try_sign_message ( & simple_message ) ? ;
2019-12-05 14:32:42 -08:00
let pubkey_bs58 = matches . value_of ( " pubkey " ) . unwrap ( ) ;
let pubkey = bs58 ::decode ( pubkey_bs58 ) . into_vec ( ) . unwrap ( ) ;
2020-02-27 18:23:28 -08:00
if signature . verify ( & pubkey , & simple_message ) {
2019-12-05 14:32:42 -08:00
println! ( " Verification for public key: {} : Success " , pubkey_bs58 ) ;
} else {
println! ( " Verification for public key: {} : Failed " , pubkey_bs58 ) ;
exit ( 1 ) ;
}
}
2019-06-07 17:54:54 -07:00
_ = > unreachable! ( ) ,
2018-07-12 15:29:49 -07:00
}
2019-03-19 14:19:50 -07:00
2018-07-12 10:59:40 -07:00
Ok ( ( ) )
}