feat: Add Address.is_related_to_pubkey()
This commit is contained in:
parent
1871c3ada9
commit
51fef76129
|
@ -39,9 +39,9 @@ use core::num::ParseIntError;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
#[cfg(feature = "std")] use std::error;
|
#[cfg(feature = "std")] use std::error;
|
||||||
|
|
||||||
use secp256k1::{Secp256k1, Verification};
|
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
|
||||||
use bech32;
|
use bech32;
|
||||||
use hashes::Hash;
|
use hashes::{sha256, Hash, HashEngine};
|
||||||
use hash_types::{PubkeyHash, ScriptHash};
|
use hash_types::{PubkeyHash, ScriptHash};
|
||||||
use blockdata::{script, opcodes};
|
use blockdata::{script, opcodes};
|
||||||
use blockdata::constants::{PUBKEY_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_TEST, MAX_SCRIPT_ELEMENT_SIZE};
|
use blockdata::constants::{PUBKEY_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_TEST, MAX_SCRIPT_ELEMENT_SIZE};
|
||||||
|
@ -727,6 +727,37 @@ impl Address {
|
||||||
(Network::Testnet, _) | (Network::Regtest, _) | (Network::Signet, _) => true
|
(Network::Testnet, _) | (Network::Regtest, _) | (Network::Signet, _) => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the given pubkey is directly related to the address payload.
|
||||||
|
///
|
||||||
|
/// This is determined by directly comparing the address payload with either the
|
||||||
|
/// hash of the given public key or the segwit redeem hash generated from the
|
||||||
|
/// given key. For taproot addresses, the supplied key is assumed to be tweaked
|
||||||
|
pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool {
|
||||||
|
let pubkey_hash = pubkey.pubkey_hash();
|
||||||
|
let payload = self.payload_as_bytes();
|
||||||
|
let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
|
||||||
|
|
||||||
|
(*pubkey_hash == *payload) || (xonly_pubkey.serialize() == *payload) || (*segwit_redeem_hash(&pubkey_hash) == *payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the supplied xonly public key can be used to derive the address.
|
||||||
|
///
|
||||||
|
/// This will only work for Taproot addresses. The Public Key is
|
||||||
|
/// assumed to have already been tweaked.
|
||||||
|
pub fn is_related_to_xonly_pubkey(&self, xonly_pubkey: &XOnlyPublicKey) -> bool {
|
||||||
|
let payload = self.payload_as_bytes();
|
||||||
|
payload == xonly_pubkey.serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the address payload as a byte slice
|
||||||
|
fn payload_as_bytes(&self) -> &[u8] {
|
||||||
|
match &self.payload {
|
||||||
|
Payload::ScriptHash(hash) => hash,
|
||||||
|
Payload::PubkeyHash(hash) => hash,
|
||||||
|
Payload::WitnessProgram { program, .. } => program,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
|
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
|
||||||
|
@ -870,6 +901,14 @@ impl fmt::Debug for Address {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a byte array of a pubkey hash into a segwit redeem hash
|
||||||
|
fn segwit_redeem_hash(pubkey_hash: &[u8]) -> ::hashes::hash160::Hash {
|
||||||
|
let mut sha_engine = sha256::Hash::engine();
|
||||||
|
sha_engine.input(&[0, 20]);
|
||||||
|
sha_engine.input(pubkey_hash);
|
||||||
|
::hashes::hash160::Hash::from_engine(sha_engine)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
@ -1273,4 +1312,95 @@ mod tests {
|
||||||
assert_eq!(address.address_type(), Some(AddressType::P2tr));
|
assert_eq!(address.address_type(), Some(AddressType::P2tr));
|
||||||
roundtrips(&address);
|
roundtrips(&address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_related_to_pubkey_p2wpkh() {
|
||||||
|
let address_string = "bc1qhvd6suvqzjcu9pxjhrwhtrlj85ny3n2mqql5w4";
|
||||||
|
let address = Address::from_str(address_string).expect("address");
|
||||||
|
|
||||||
|
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
|
||||||
|
let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey");
|
||||||
|
|
||||||
|
let result = address.is_related_to_pubkey(&pubkey);
|
||||||
|
assert!(result);
|
||||||
|
|
||||||
|
let unused_pubkey = PublicKey::from_str("02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c").expect("pubkey");
|
||||||
|
assert!(!address.is_related_to_pubkey(&unused_pubkey))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_related_to_pubkey_p2shwpkh() {
|
||||||
|
let address_string = "3EZQk4F8GURH5sqVMLTFisD17yNeKa7Dfs";
|
||||||
|
let address = Address::from_str(address_string).expect("address");
|
||||||
|
|
||||||
|
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
|
||||||
|
let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey");
|
||||||
|
|
||||||
|
let result = address.is_related_to_pubkey(&pubkey);
|
||||||
|
assert!(result);
|
||||||
|
|
||||||
|
let unused_pubkey = PublicKey::from_str("02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c").expect("pubkey");
|
||||||
|
assert!(!address.is_related_to_pubkey(&unused_pubkey))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_related_to_pubkey_p2pkh() {
|
||||||
|
let address_string = "1J4LVanjHMu3JkXbVrahNuQCTGCRRgfWWx";
|
||||||
|
let address = Address::from_str(address_string).expect("address");
|
||||||
|
|
||||||
|
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
|
||||||
|
let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey");
|
||||||
|
|
||||||
|
let result = address.is_related_to_pubkey(&pubkey);
|
||||||
|
assert!(result);
|
||||||
|
|
||||||
|
let unused_pubkey = PublicKey::from_str("02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c").expect("pubkey");
|
||||||
|
assert!(!address.is_related_to_pubkey(&unused_pubkey))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_related_to_pubkey_p2pkh_uncompressed_key() {
|
||||||
|
let address_string = "msvS7KzhReCDpQEJaV2hmGNvuQqVUDuC6p";
|
||||||
|
let address = Address::from_str(address_string).expect("address");
|
||||||
|
|
||||||
|
let pubkey_string = "04e96e22004e3db93530de27ccddfdf1463975d2138ac018fc3e7ba1a2e5e0aad8e424d0b55e2436eb1d0dcd5cb2b8bcc6d53412c22f358de57803a6a655fbbd04";
|
||||||
|
let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey");
|
||||||
|
|
||||||
|
let result = address.is_related_to_pubkey(&pubkey);
|
||||||
|
assert!(result);
|
||||||
|
|
||||||
|
let unused_pubkey = PublicKey::from_str("02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c").expect("pubkey");
|
||||||
|
assert!(!address.is_related_to_pubkey(&unused_pubkey))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_related_to_pubkey_p2tr(){
|
||||||
|
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
|
||||||
|
let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey");
|
||||||
|
let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
|
||||||
|
let tweaked_pubkey = TweakedPublicKey::dangerous_assume_tweaked(xonly_pubkey);
|
||||||
|
let address = Address::p2tr_tweaked(tweaked_pubkey, Network::Bitcoin);
|
||||||
|
|
||||||
|
assert_eq!(address, Address::from_str("bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e").expect("address"));
|
||||||
|
|
||||||
|
let result = address.is_related_to_pubkey(&pubkey);
|
||||||
|
assert!(result);
|
||||||
|
|
||||||
|
let unused_pubkey = PublicKey::from_str("02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c").expect("pubkey");
|
||||||
|
assert!(!address.is_related_to_pubkey(&unused_pubkey));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_related_to_xonly_pubkey(){
|
||||||
|
let pubkey_string = "0347ff3dacd07a1f43805ec6808e801505a6e18245178609972a68afbc2777ff2b";
|
||||||
|
let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey");
|
||||||
|
let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
|
||||||
|
let tweaked_pubkey = TweakedPublicKey::dangerous_assume_tweaked(xonly_pubkey);
|
||||||
|
let address = Address::p2tr_tweaked(tweaked_pubkey, Network::Bitcoin);
|
||||||
|
|
||||||
|
assert_eq!(address, Address::from_str("bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e").expect("address"));
|
||||||
|
|
||||||
|
let result = address.is_related_to_xonly_pubkey(&xonly_pubkey);
|
||||||
|
assert!(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue