Merge rust-bitcoin/rust-bitcoin#1663: Create Address::matches_script_pubkey method

d71c31c235 Create Address::matches_script_pubkey method (hashmap)

Pull request description:

  to check if an address creates a particular script without allocating.

  fixes rust-bitcoin/rust-bitcoin#1604

ACKs for top commit:
  Kixunil:
    ACK d71c31c235
  apoelstra:
    ACK d71c31c235

Tree-SHA512: cb60a53ae2be7c47dcd27415c883a73c81d57cbbf0bc92eaf76243d79d9c8e2c2efe91bef65a7e67ed26fec376f11325709ff27025d054813b1907ea2bf4961c
This commit is contained in:
Andrew Poelstra 2023-02-24 14:38:25 +00:00
commit fbb3b82b93
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 50 additions and 0 deletions

View File

@ -469,6 +469,23 @@ impl Payload {
} }
} }
/// Returns true if the address creates a particular script
/// This function doesn't make any allocations.
pub fn matches_script_pubkey(&self, script: &Script) -> bool {
match *self {
Payload::PubkeyHash(ref hash) if script.is_p2pkh() => {
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash)
},
Payload::ScriptHash(ref hash) if script.is_p2sh() => {
&script.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash)
},
Payload::WitnessProgram(ref prog) if script.is_witness_program() => {
&script.as_bytes()[2..] == prog.program.as_bytes()
},
Payload::PubkeyHash(_) | Payload::ScriptHash(_) | Payload::WitnessProgram(_) => false,
}
}
/// Creates a pay to (compressed) public key hash payload from a public key /// Creates a pay to (compressed) public key hash payload from a public key
#[inline] #[inline]
pub fn p2pkh(pk: &PublicKey) -> Payload { Payload::PubkeyHash(pk.pubkey_hash()) } pub fn p2pkh(pk: &PublicKey) -> Payload { Payload::PubkeyHash(pk.pubkey_hash()) }
@ -955,6 +972,12 @@ impl Address {
let payload = self.payload.inner_prog_as_bytes(); let payload = self.payload.inner_prog_as_bytes();
payload == xonly_pubkey.serialize() payload == xonly_pubkey.serialize()
} }
/// Returns true if the address creates a particular script
/// This function doesn't make any allocations.
pub fn matches_script_pubkey(&self, script_pubkey: &Script) -> bool {
self.payload.matches_script_pubkey(script_pubkey)
}
} }
/// Methods that can be called only on `Address<NetworkUnchecked>`. /// Methods that can be called only on `Address<NetworkUnchecked>`.
@ -1723,4 +1746,31 @@ mod tests {
let want = Err(Error::UnknownAddressType("invalid".to_string())); let want = Err(Error::UnknownAddressType("invalid".to_string()));
assert_eq!(got, want); assert_eq!(got, want);
} }
#[test]
fn test_matches_script_pubkey() {
let addresses = [
"1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY",
"1J4LVanjHMu3JkXbVrahNuQCTGCRRgfWWx",
"33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k",
"3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE",
"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
"bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw",
"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
"bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e",
];
for addr in &addresses {
let addr = Address::from_str(addr)
.unwrap()
.require_network(Network::Bitcoin)
.unwrap();
for another in &addresses {
let another = Address::from_str(another)
.unwrap()
.require_network(Network::Bitcoin)
.unwrap();
assert_eq!(addr.matches_script_pubkey(&another.script_pubkey()), addr == another);
}
}
}
} }