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)] #[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Pubkey(GenericArray); #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParsePubkeyError { WrongSize, 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; fn from_str(s: &str) -> Result { let pubkey_vec = bs58::decode(s) .into_vec() .map_err(|_| ParsePubkeyError::Invalid)?; if pubkey_vec.len() != mem::size_of::() { Err(ParsePubkeyError::WrongSize) } else { Ok(Pubkey::new(&pubkey_vec)) } } } impl Pubkey { pub fn new(pubkey_vec: &[u8]) -> Self { Pubkey(GenericArray::clone_from_slice(&pubkey_vec)) } } impl AsRef<[u8]> for Pubkey { fn as_ref(&self) -> &[u8] { &self.0[..] } } impl fmt::Debug for Pubkey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", bs58::encode(self.0).into_string()) } } impl fmt::Display for Pubkey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", bs58::encode(self.0).into_string()) } } 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 std::fs::remove_file; #[test] fn pubkey_fromstr() { let pubkey = Keypair::new().pubkey(); let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string(); assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); pubkey_base58_str.push_str(&bs58::encode(pubkey.0).into_string()); assert_eq!( pubkey_base58_str.parse::(), Err(ParsePubkeyError::WrongSize) ); pubkey_base58_str.truncate(pubkey_base58_str.len() / 2); assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); pubkey_base58_str.truncate(pubkey_base58_str.len() / 2); assert_eq!( pubkey_base58_str.parse::(), Err(ParsePubkeyError::WrongSize) ); let mut pubkey_base58_str = bs58::encode(pubkey.0).into_string(); assert_eq!(pubkey_base58_str.parse::(), Ok(pubkey)); // throw some non-base58 stuff in there pubkey_base58_str.replace_range(..1, "I"); assert_eq!( pubkey_base58_str.parse::(), Err(ParsePubkeyError::Invalid) ); } #[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(()) } }