diff --git a/keygen/Cargo.toml b/keygen/Cargo.toml index dd64930b82..532792af79 100644 --- a/keygen/Cargo.toml +++ b/keygen/Cargo.toml @@ -19,4 +19,3 @@ solana-sdk = { path = "../sdk", version = "0.13.0" } [[bin]] name = "solana-keygen" path = "src/keygen.rs" - diff --git a/keygen/src/keygen.rs b/keygen/src/keygen.rs index a5b5e783bd..582a89f40e 100644 --- a/keygen/src/keygen.rs +++ b/keygen/src/keygen.rs @@ -1,5 +1,6 @@ -use clap::{crate_description, crate_name, crate_version, App, Arg}; -use solana_sdk::signature::gen_keypair_file; +use clap::{crate_description, crate_name, crate_version, App, Arg, SubCommand}; +use solana_sdk::pubkey::write_pubkey; +use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil}; use std::error; fn main() -> Result<(), Box> { @@ -14,19 +15,78 @@ fn main() -> Result<(), Box> { .takes_value(true) .help("Path to generated file"), ) + .subcommand( + SubCommand::with_name("new") + .about("Generate new keypair file") + .arg( + Arg::with_name("outfile") + .short("o") + .long("outfile") + .value_name("PATH") + .takes_value(true) + .help("Path to generated file"), + ), + ) + .subcommand( + SubCommand::with_name("pubkey") + .about("Generate a pubkey from keypair file") + .arg( + Arg::with_name("infile") + .index(1) + .value_name("PATH") + .takes_value(true) + .help("Path to keypair file"), + ) + .arg( + Arg::with_name("outfile") + .short("o") + .long("outfile") + .value_name("PATH") + .takes_value(true) + .help("Path to generated file"), + ), + ) .get_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() - }; + match matches.subcommand() { + ("pubkey", Some(pubkey_matches)) => { + let mut path = dirs::home_dir().expect("home directory"); + let infile = if pubkey_matches.is_present("infile") { + pubkey_matches.value_of("infile").unwrap() + } else { + path.extend(&[".config", "solana", "id.json"]); + path.to_str().unwrap() + }; + let keypair = read_keypair(infile)?; - let serialized_keypair = gen_keypair_file(outfile.to_string())?; - if outfile == "-" { - println!("{}", serialized_keypair); + if pubkey_matches.is_present("outfile") { + let outfile = pubkey_matches.value_of("outfile").unwrap(); + write_pubkey(outfile, keypair.pubkey())?; + } else { + println!("{}", keypair.pubkey()); + } + } + match_tuple => { + let working_matches = if let (_, Some(new_matches)) = match_tuple { + new_matches + } else { + &matches + }; + + let mut path = dirs::home_dir().expect("home directory"); + let outfile = if working_matches.is_present("outfile") { + working_matches.value_of("outfile").unwrap() + } else { + path.extend(&[".config", "solana", "id.json"]); + path.to_str().unwrap() + }; + + let serialized_keypair = gen_keypair_file(outfile.to_string())?; + if outfile == "-" { + println!("{}", serialized_keypair); + } + } } + Ok(()) } diff --git a/sdk/src/pubkey.rs b/sdk/src/pubkey.rs index 7680759119..4a46344d45 100644 --- a/sdk/src/pubkey.rs +++ b/sdk/src/pubkey.rs @@ -1,8 +1,11 @@ -use bs58; use generic_array::typenum::U32; use generic_array::GenericArray; +use std::error; use std::fmt; +use std::fs::{self, File}; +use std::io::Write; use std::mem; +use std::path::Path; use std::str::FromStr; #[repr(C)] @@ -15,6 +18,14 @@ pub enum ParsePubkeyError { Invalid, } +impl fmt::Display for ParsePubkeyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ParsePubkeyError: {:?}", self) + } +} + +impl error::Error for ParsePubkeyError {} + impl FromStr for Pubkey { type Err = ParsePubkeyError; @@ -54,11 +65,30 @@ impl fmt::Display for Pubkey { } } +pub fn write_pubkey(outfile: &str, pubkey: Pubkey) -> Result<(), Box> { + let printable = format!("{}", pubkey); + let serialized = serde_json::to_string(&printable)?; + + if let Some(outdir) = Path::new(&outfile).parent() { + fs::create_dir_all(outdir)?; + } + let mut f = File::create(outfile)?; + f.write_all(&serialized.clone().into_bytes())?; + + Ok(()) +} + +pub fn read_pubkey(infile: &str) -> Result> { + let f = File::open(infile.to_string())?; + let printable: String = serde_json::from_reader(f)?; + Ok(Pubkey::from_str(&printable)?) +} + #[cfg(test)] mod tests { use super::*; use crate::signature::{Keypair, KeypairUtil}; - use bs58; + use std::fs::remove_file; #[test] fn pubkey_fromstr() { @@ -93,4 +123,14 @@ mod tests { ); } + #[test] + fn test_read_write_pubkey() -> Result<(), Box> { + let filename = "test_pubkey.json"; + let pubkey = Keypair::new().pubkey(); + write_pubkey(filename, pubkey)?; + let read = read_pubkey(filename)?; + assert_eq!(read, pubkey); + remove_file(filename)?; + Ok(()) + } }