Merge pull request #241 from apoelstra/2019-03-pubkey-string

key: implement ToString and FromStr for PublicKey
This commit is contained in:
Andrew Poelstra 2019-03-04 17:47:36 +00:00 committed by GitHub
commit 7e1a6a4ab7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 3 deletions

View File

@ -37,6 +37,8 @@ use bitcoin_hashes::{hash160, sha256, Hash};
#[cfg(feature="bitcoinconsensus")] use std::convert; #[cfg(feature="bitcoinconsensus")] use std::convert;
#[cfg(feature="bitcoinconsensus")] use bitcoin_hashes::sha256d; #[cfg(feature="bitcoinconsensus")] use bitcoin_hashes::sha256d;
use util::key::PublicKey;
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] #[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
/// A Bitcoin script /// A Bitcoin script
pub struct Script(Box<[u8]>); pub struct Script(Box<[u8]>);
@ -599,6 +601,15 @@ impl Builder {
self self
} }
/// Pushes a public key
pub fn push_key(self, key: &PublicKey) -> Builder {
if key.compressed {
self.push_slice(&key.key.serialize()[..])
} else {
self.push_slice(&key.key.serialize_uncompressed()[..])
}
}
/// Adds a single opcode to the script /// Adds a single opcode to the script
pub fn push_opcode(mut self, data: opcodes::All) -> Builder { pub fn push_opcode(mut self, data: opcodes::All) -> Builder {
self.0.push(data.into_u8()); self.0.push(data.into_u8());
@ -694,6 +705,7 @@ impl<D: Decoder> Decodable<D> for Script {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::str::FromStr;
use hex::decode as hex_decode; use hex::decode as hex_decode;
use super::*; use super::*;
@ -701,6 +713,7 @@ mod test {
use consensus::encode::{deserialize, serialize}; use consensus::encode::{deserialize, serialize};
use blockdata::opcodes; use blockdata::opcodes;
use util::key::PublicKey;
#[test] #[test]
fn script() { fn script() {
@ -725,6 +738,14 @@ mod test {
// data // data
script = script.push_slice("NRA4VR".as_bytes()); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(&script[..], &comp[..]); script = script.push_slice("NRA4VR".as_bytes()); comp.extend([6u8, 78, 82, 65, 52, 86, 82].iter().cloned()); assert_eq!(&script[..], &comp[..]);
// keys
let keystr = "21032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af";
let key = PublicKey::from_str(&keystr[2..]).unwrap();
script = script.push_key(&key); comp.extend(hex_decode(keystr).unwrap().iter().cloned()); assert_eq!(&script[..], &comp[..]);
let keystr = "41042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133";
let key = PublicKey::from_str(&keystr[2..]).unwrap();
script = script.push_key(&key); comp.extend(hex_decode(keystr).unwrap().iter().cloned()); assert_eq!(&script[..], &comp[..]);
// opcodes // opcodes
script = script.push_opcode(opcodes::all::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]); script = script.push_opcode(opcodes::all::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);
script = script.push_opcode(opcodes::all::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]); script = script.push_opcode(opcodes::all::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);

View File

@ -64,6 +64,32 @@ impl PublicKey {
} }
} }
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.compressed {
for ch in &self.key.serialize()[..] {
write!(f, "{:02x}", ch)?;
}
} else {
for ch in &self.key.serialize_uncompressed()[..] {
write!(f, "{:02x}", ch)?;
}
}
Ok(())
}
}
impl FromStr for PublicKey {
type Err = encode::Error;
fn from_str(s: &str) -> Result<PublicKey, encode::Error> {
let key = secp256k1::PublicKey::from_str(s)?;
Ok(PublicKey {
key: key,
compressed: s.len() == 66
})
}
}
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
/// A Bitcoin ECDSA private key /// A Bitcoin ECDSA private key
pub struct PrivateKey { pub struct PrivateKey {
@ -161,7 +187,7 @@ impl ops::Index<ops::RangeFull> for PrivateKey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::PrivateKey; use super::{PrivateKey, PublicKey};
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use std::str::FromStr; use std::str::FromStr;
use network::constants::Network::Testnet; use network::constants::Network::Testnet;
@ -193,7 +219,14 @@ mod tests {
assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3");
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let pk = Address::p2pkh(&sk.public_key(&secp), sk.network); let mut pk = sk.public_key(&secp);
assert_eq!(&pk.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); assert_eq!(pk.compressed, false);
assert_eq!(&pk.to_string(), "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133");
assert_eq!(pk, PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap());
let addr = Address::p2pkh(&pk, sk.network);
assert_eq!(&addr.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8");
pk.compressed = true;
assert_eq!(&pk.to_string(), "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af");
assert_eq!(pk, PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap());
} }
} }