Integrate newly-added PublicKey with Address

- Switch util::address::Payload::Pubkey variant to wrap
  util:🔑:PublicKey
- Switch util::address::Address::p*k* constructors to use
  util:🔑:PublicKey
- Fix tests for aforementioned switch
- Add convenience methods for util:🔑:PublicKey to
  util:🔑:PrivateKey conversion
- Switch BIP143 tests to use util:🔑:PublicKey
This commit is contained in:
Carl Dong 2018-11-15 20:21:41 -08:00
parent 53a6efe33c
commit b3cc3d50ef
3 changed files with 40 additions and 28 deletions

View File

@ -24,13 +24,17 @@
//! //!
//! use bitcoin::network::constants::Network; //! use bitcoin::network::constants::Network;
//! use bitcoin::util::address::Address; //! use bitcoin::util::address::Address;
//! use bitcoin::util::key;
//! use secp256k1::Secp256k1; //! use secp256k1::Secp256k1;
//! use rand::thread_rng; //! use rand::thread_rng;
//! //!
//! fn main() { //! fn main() {
//! // Generate random key pair //! // Generate random key pair
//! let s = Secp256k1::new(); //! let s = Secp256k1::new();
//! let (_, public_key) = s.generate_keypair(&mut thread_rng()); //! let public_key = key::PublicKey {
//! compressed: true,
//! key: s.generate_keypair(&mut thread_rng()).1,
//! };
//! //!
//! // Generate pay-to-pubkey-hash address //! // Generate pay-to-pubkey-hash address
//! let address = Address::p2pkh(&public_key, Network::Bitcoin); //! let address = Address::p2pkh(&public_key, Network::Bitcoin);
@ -42,7 +46,6 @@ use std::str::FromStr;
use bitcoin_bech32::{self, WitnessProgram, u5}; use bitcoin_bech32::{self, WitnessProgram, u5};
use bitcoin_hashes::{hash160, Hash}; use bitcoin_hashes::{hash160, Hash};
use secp256k1::key::PublicKey;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde; use serde;
@ -52,6 +55,7 @@ use blockdata::script;
use network::constants::Network; use network::constants::Network;
use consensus::encode; use consensus::encode;
use util::base58; use util::base58;
use util::key;
/// The method used to produce an address /// The method used to produce an address
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -70,28 +74,20 @@ pub struct Address {
/// The type of the address /// The type of the address
pub payload: Payload, pub payload: Payload,
/// The network on which this address is usable /// The network on which this address is usable
pub network: Network pub network: Network,
} }
impl Address { impl Address {
/// Creates a pay to (compressed) public key hash address from a public key /// Creates a pay to (compressed) public key hash address from a public key
/// This is the preferred non-witness type address /// This is the preferred non-witness type address
#[inline] #[inline]
pub fn p2pkh(pk: &PublicKey, network: Network) -> Address { pub fn p2pkh(pk: &key::PublicKey, network: Network) -> Address {
Address { let mut hash_engine = hash160::Hash::engine();
network: network, pk.write_into(&mut hash_engine);
payload: Payload::PubkeyHash(hash160::Hash::hash(&pk.serialize()[..]))
}
}
/// Creates a pay to uncompressed public key hash address from a public key
/// This address type is discouraged as it uses more space but otherwise equivalent to p2pkh
/// therefore only adds ambiguity
#[inline]
pub fn p2upkh(pk: &PublicKey, network: Network) -> Address {
Address { Address {
network: network, network: network,
payload: Payload::PubkeyHash(hash160::Hash::hash(&pk.serialize_uncompressed()[..])) payload: Payload::PubkeyHash(hash160::Hash::from_engine(hash_engine))
} }
} }
@ -107,23 +103,30 @@ impl Address {
/// Create a witness pay to public key address from a public key /// Create a witness pay to public key address from a public key
/// This is the native segwit address type for an output redeemable with a single signature /// This is the native segwit address type for an output redeemable with a single signature
pub fn p2wpkh (pk: &PublicKey, network: Network) -> Address { pub fn p2wpkh (pk: &key::PublicKey, network: Network) -> Address {
let mut hash_engine = hash160::Hash::engine();
pk.write_into(&mut hash_engine);
Address { Address {
network: network, network: network,
payload: Payload::WitnessProgram( payload: Payload::WitnessProgram(
// unwrap is safe as witness program is known to be correct as above // unwrap is safe as witness program is known to be correct as above
WitnessProgram::new(u5::try_from_u8(0).expect("0<32"), WitnessProgram::new(u5::try_from_u8(0).expect("0<32"),
hash160::Hash::hash(&pk.serialize()[..])[..].to_vec(), hash160::Hash::from_engine(hash_engine)[..].to_vec(),
Address::bech_network(network)).unwrap()) Address::bech_network(network)).unwrap())
} }
} }
/// Create a pay to script address that embeds a witness pay to public key /// Create a pay to script address that embeds a witness pay to public key
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients /// This is a segwit address type that looks familiar (as p2sh) to legacy clients
pub fn p2shwpkh (pk: &PublicKey, network: Network) -> Address { pub fn p2shwpkh (pk: &key::PublicKey, network: Network) -> Address {
let mut hash_engine = hash160::Hash::engine();
pk.write_into(&mut hash_engine);
let builder = script::Builder::new() let builder = script::Builder::new()
.push_int(0) .push_int(0)
.push_slice(&hash160::Hash::hash(&pk.serialize()[..])[..]); .push_slice(&hash160::Hash::from_engine(hash_engine)[..]);
Address { Address {
network: network, network: network,
payload: Payload::ScriptHash( payload: Payload::ScriptHash(
@ -146,7 +149,7 @@ impl Address {
sha256::Hash::hash(&script[..])[..].to_vec(), sha256::Hash::hash(&script[..])[..].to_vec(),
Address::bech_network(network) Address::bech_network(network)
).unwrap() ).unwrap()
) ),
} }
} }
@ -252,7 +255,7 @@ impl FromStr for Address {
} }
return Ok(Address { return Ok(Address {
network: network, network: network,
payload: Payload::WitnessProgram(witprog) payload: Payload::WitnessProgram(witprog),
}); });
} }
@ -359,11 +362,12 @@ mod tests {
use std::string::ToString; use std::string::ToString;
use bitcoin_hashes::{hash160, Hash}; use bitcoin_hashes::{hash160, Hash};
use secp256k1::key::PublicKey;
use hex::decode as hex_decode; use hex::decode as hex_decode;
use blockdata::script::Script; use blockdata::script::Script;
use network::constants::Network::{Bitcoin, Testnet, Regtest}; use network::constants::Network::{Bitcoin, Testnet, Regtest};
use util::key::PublicKey;
use super::*; use super::*;
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap())); macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
@ -388,7 +392,7 @@ mod tests {
#[test] #[test]
fn test_p2pkh_from_key() { fn test_p2pkh_from_key() {
let key = hex_key!("048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183"); let key = hex_key!("048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183");
let addr = Address::p2upkh(&key, Bitcoin); let addr = Address::p2pkh(&key, Bitcoin);
assert_eq!(&addr.to_string(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY"); assert_eq!(&addr.to_string(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY");
let key = hex_key!(&"03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f"); let key = hex_key!(&"03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f");

View File

@ -107,8 +107,8 @@ mod tests {
use network::constants::Network; use network::constants::Network;
use util::misc::hex_bytes; use util::misc::hex_bytes;
use util::address::Address; use util::address::Address;
use util::key::PublicKey;
use hex; use hex;
use secp256k1::PublicKey;
use super::*; use super::*;

View File

@ -25,7 +25,7 @@ use network::constants::Network;
use util::base58; use util::base58;
/// A Bitcoin ECDSA public key /// A Bitcoin ECDSA public key
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PublicKey { pub struct PublicKey {
/// Whether this public key represents a compressed address /// Whether this public key represents a compressed address
pub compressed: bool, pub compressed: bool,
@ -60,6 +60,11 @@ impl PublicKey {
key: key, key: key,
}) })
} }
/// Computes the public key as supposed to be used with this secret
pub fn from_private_key<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &PrivateKey) -> PublicKey {
sk.public_key(secp)
}
} }
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
@ -75,8 +80,11 @@ pub struct PrivateKey {
impl PrivateKey { impl PrivateKey {
/// Computes the public key as supposed to be used with this secret /// Computes the public key as supposed to be used with this secret
pub fn public_key<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> secp256k1::PublicKey { pub fn public_key<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
secp256k1::PublicKey::from_secret_key(secp, &self.key) PublicKey {
compressed: self.compressed,
key: secp256k1::PublicKey::from_secret_key(secp, &self.key)
}
} }
/// Format the private key to WIF format. /// Format the private key to WIF format.
@ -184,7 +192,7 @@ 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::p2upkh(&sk.public_key(&secp), sk.network); let pk = Address::p2pkh(&sk.public_key(&secp), sk.network);
assert_eq!(&pk.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); assert_eq!(&pk.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8");
} }
} }