Create Address::matches_script_pubkey method

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

fixes rust-bitcoin/rust-bitcoin#1604
This commit is contained in:
hashmap 2023-02-23 21:52:15 +01:00
parent 30633ceb72
commit d71c31c235
No known key found for this signature in database
GPG Key ID: F28AA7B0561B4B49
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);
}
}
}
} }