From 1d9f53158173aff2c4bfd5559941c03b78314831 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 10 Jan 2020 23:41:22 +0100 Subject: [PATCH 1/4] Pubkey hash functions (normal and witness) --- src/util/key.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/util/key.rs b/src/util/key.rs index 89946be7..bccb0f8b 100644 --- a/src/util/key.rs +++ b/src/util/key.rs @@ -22,6 +22,8 @@ use std::str::FromStr; use secp256k1::{self, Secp256k1}; use network::constants::Network; +use hashes::Hash; +use hash_types::{PubkeyHash, WPubkeyHash}; use util::base58; /// A key-related error. @@ -80,6 +82,24 @@ pub struct PublicKey { } impl PublicKey { + /// Returns bitcoin 160-bit hash of the public key + pub fn pubkey_hash(&self) -> PubkeyHash { + if self.compressed { + PubkeyHash::hash(&self.key.serialize()) + } else { + PubkeyHash::hash(&self.key.serialize_uncompressed()) + } + } + + /// Returns bitcoin 160-bit hash of the public key for witness program + pub fn wpubkey_hash(&self) -> WPubkeyHash { + if self.compressed { + WPubkeyHash::hash(&self.key.serialize()) + } else { + WPubkeyHash::hash(&self.key.serialize_uncompressed()) + } + } + /// Write the public key into a writer pub fn write_into(&self, mut writer: W) { let write_res: io::Result<()> = if self.compressed { @@ -360,6 +380,7 @@ mod tests { use super::{PrivateKey, PublicKey}; use secp256k1::Secp256k1; use std::str::FromStr; + use hashes::hex::ToHex; use network::constants::Network::Testnet; use network::constants::Network::Bitcoin; use util::address::Address; @@ -400,6 +421,22 @@ mod tests { assert_eq!(pk, PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap()); } + #[test] + fn test_pubkey_hash() { + let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); + let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); + assert_eq!(pk.pubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); + assert_eq!(upk.pubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a"); + } + + #[test] + fn test_wpubkey_hash() { + let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); + let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); + assert_eq!(pk.wpubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); + assert_eq!(upk.wpubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a"); + } + #[cfg(feature = "serde")] #[test] fn test_key_serde() { From 8363c76f5ce604e006e43b67a5c8809dbcc3b636 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 11 Jan 2020 00:07:20 +0100 Subject: [PATCH 2/4] Script hash functions (normal and witness) --- src/blockdata/script.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index e3f5e4b7..cd590470 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -38,6 +38,7 @@ use hashes::Hash; #[cfg(feature="bitcoinconsensus")] use OutPoint; use util::key::PublicKey; +use util::psbt::serialize::Serialize; #[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] /// A Bitcoin script @@ -275,6 +276,16 @@ impl Script { .into_script() } + /// Returns 160-bit hash of the script + pub fn script_hash(&self) -> ScriptHash { + ScriptHash::hash(&self.serialize()) + } + + /// Returns 256-bit hash of the script for P2WSH outputs + pub fn wscript_hash(&self) -> WScriptHash { + WScriptHash::hash(&self.serialize()) + } + /// The length in bytes of the script pub fn len(&self) -> usize { self.0.len() } @@ -292,13 +303,13 @@ impl Script { /// Compute the P2SH output corresponding to this redeem script pub fn to_p2sh(&self) -> Script { - Script::new_p2sh(&ScriptHash::hash(&self.0)) + Script::new_p2sh(&self.script_hash()) } /// Compute the P2WSH output corresponding to this witnessScript (aka the "witness redeem /// script") pub fn to_v0_p2wsh(&self) -> Script { - Script::new_v0_wsh(&WScriptHash::hash(&self.0)) + Script::new_v0_wsh(&self.wscript_hash()) } /// Checks whether a script pubkey is a p2sh output @@ -1011,6 +1022,13 @@ mod test { assert!(read_scriptint(&build_scriptint(-(1 << 31))).is_err()); } + #[test] + fn script_hashes() { + let script = hex_script!("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac"); + assert_eq!(script.script_hash().to_hex(), "8292bcfbef1884f73c813dfe9c82fd7e814291ea"); + assert_eq!(script.wscript_hash().to_hex(), "3e1525eb183ad4f9b3c5fa3175bdca2a52e947b135bbb90383bf9f6408e2c324"); + } + #[test] fn provably_unspendable_test() { // p2pk From 1342d73734d4a5c92db24396caf0a8dac81e980a Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 3 Sep 2020 16:51:34 +0200 Subject: [PATCH 3/4] Script hash functions with non-allocating serialization --- src/blockdata/script.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index cd590470..3d42d741 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -38,7 +38,6 @@ use hashes::Hash; #[cfg(feature="bitcoinconsensus")] use OutPoint; use util::key::PublicKey; -use util::psbt::serialize::Serialize; #[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] /// A Bitcoin script @@ -278,12 +277,12 @@ impl Script { /// Returns 160-bit hash of the script pub fn script_hash(&self) -> ScriptHash { - ScriptHash::hash(&self.serialize()) + ScriptHash::hash(&self.as_bytes()) } /// Returns 256-bit hash of the script for P2WSH outputs pub fn wscript_hash(&self) -> WScriptHash { - WScriptHash::hash(&self.serialize()) + WScriptHash::hash(&self.as_bytes()) } /// The length in bytes of the script From ff1b4a8dbd64e1c61378bcacf7abc4c8ef1a98ae Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 11 Sep 2020 15:43:05 +0200 Subject: [PATCH 4/4] WPubkeyHash constructor failing on uncompressed PublicKey --- src/hash_types.rs | 1 + src/util/key.rs | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/hash_types.rs b/src/hash_types.rs index 3eefe39a..fea1166a 100644 --- a/src/hash_types.rs +++ b/src/hash_types.rs @@ -19,6 +19,7 @@ use consensus::encode::{Encodable, Decodable, Error}; use hashes::{Hash, sha256, sha256d, ripemd160, hash160}; use hashes::hex::{FromHex, ToHex}; +use util::key::PublicKey; macro_rules! impl_hashencode { ($hashtype:ident) => { diff --git a/src/util/key.rs b/src/util/key.rs index bccb0f8b..f36c206d 100644 --- a/src/util/key.rs +++ b/src/util/key.rs @@ -22,7 +22,7 @@ use std::str::FromStr; use secp256k1::{self, Secp256k1}; use network::constants::Network; -use hashes::Hash; +use hashes::{Hash, hash160}; use hash_types::{PubkeyHash, WPubkeyHash}; use util::base58; @@ -92,11 +92,15 @@ impl PublicKey { } /// Returns bitcoin 160-bit hash of the public key for witness program - pub fn wpubkey_hash(&self) -> WPubkeyHash { + pub fn wpubkey_hash(&self) -> Option { if self.compressed { - WPubkeyHash::hash(&self.key.serialize()) + Some(WPubkeyHash::from_inner( + hash160::Hash::hash(&self.key.serialize()).into_inner() + )) } else { - WPubkeyHash::hash(&self.key.serialize_uncompressed()) + // We can't create witness pubkey hashes for an uncompressed + // public keys + None } } @@ -433,8 +437,8 @@ mod tests { fn test_wpubkey_hash() { let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); - assert_eq!(pk.wpubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); - assert_eq!(upk.wpubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a"); + assert_eq!(pk.wpubkey_hash().unwrap().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); + assert_eq!(upk.wpubkey_hash(), None); } #[cfg(feature = "serde")]