Skip to content

Commit

Permalink
Merge branch 'master' of github.com:rust-bitcoin/rust-bitcoin into ma…
Browse files Browse the repository at this point in the history
…ster_copy2
  • Loading branch information
JeremyRubin committed Apr 3, 2022
2 parents 8c159bc + cb4d34f commit 06b697c
Show file tree
Hide file tree
Showing 18 changed files with 960 additions and 602 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -13,7 +13,7 @@

The previous release changed the behavior of `Display` for `ChildNumber`, assuming that any correct usage would not be
affected. [Issue 608](https://github.com/rust-bitcoin/rust-bitcoin/issues/608) goes into the details of why this isn't
the case and how we broke both `rust-miniscript` and BDK.
the case and how we broke both `rust-miniscript` and BDK.

# 0.26.1 - 2021-06-06 (yanked, see explanation above)

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bitcoin"
version = "0.28.0-rc.1"
version = "0.28.0-rc.2"
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
license = "CC0-1.0"
homepage = "https://github.com/rust-bitcoin/rust-bitcoin/"
Expand Down
18 changes: 17 additions & 1 deletion src/blockdata/script.rs
Expand Up @@ -43,7 +43,7 @@ use OutPoint;
use util::key::PublicKey;
use util::address::WitnessVersion;
use util::taproot::{LeafVersion, TapBranchHash, TapLeafHash};
use secp256k1::{Secp256k1, Verification};
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};

/// A Bitcoin script.
Expand Down Expand Up @@ -919,6 +919,11 @@ impl Builder {
}
}

/// Adds instructions to push an XOnly public key onto the stack.
pub fn push_x_only_key(self, x_only_key: &XOnlyPublicKey) -> Builder {
self.push_slice(&x_only_key.serialize())
}

/// Adds a single opcode to the script.
pub fn push_opcode(mut self, data: opcodes::All) -> Builder {
self.0.push(data.into_u8());
Expand Down Expand Up @@ -1117,6 +1122,17 @@ mod test {
script = script.push_opcode(opcodes::all::OP_CHECKSIG); comp.push(0xACu8); assert_eq!(&script[..], &comp[..]);
}

#[test]
fn script_x_only_key() {
// Notice the "20" which prepends the keystr. That 20 is hexidecimal for "32". The Builder automatically adds the 32 opcode
// to our script in order to give a heads up to the script compiler that it should add the next 32 bytes to the stack.
// From: https://github.com/bitcoin-core/btcdeb/blob/e8c2750c4a4702768c52d15640ed03bf744d2601/doc/tapscript-example.md?plain=1#L43
let keystr = "209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be";
let x_only_key = XOnlyPublicKey::from_str(&keystr[2..]).unwrap();
let script = Builder::new().push_x_only_key(&x_only_key);
assert_eq!(script.0, Vec::from_hex(keystr).unwrap());
}

#[test]
fn script_builder() {
// from txid 3bb5e6434c11fb93f64574af5d116736510717f2c595eb45b52c28e31622dfff which was in my mempool when I wrote the test
Expand Down
215 changes: 110 additions & 105 deletions src/blockdata/transaction.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/hash_types.rs
Expand Up @@ -42,7 +42,7 @@ macro_rules! impl_hashencode {
hash_newtype!(Txid, sha256d::Hash, 32, doc="A bitcoin transaction hash/transaction ID.");
hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID.");
hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash.");
hash_newtype!(SigHash, sha256d::Hash, 32, doc="Hash of the transaction according to the signature algorithm");
hash_newtype!(Sighash, sha256d::Hash, 32, doc="Hash of the transaction according to the signature algorithm");

hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key.");
hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode.");
Expand All @@ -61,7 +61,7 @@ hash_newtype!(FilterHeader, sha256d::Hash, 32, doc="Filter header, as defined in
impl_hashencode!(Txid);
impl_hashencode!(Wtxid);
impl_hashencode!(BlockHash);
impl_hashencode!(SigHash);
impl_hashencode!(Sighash);

impl_hashencode!(TxMerkleNode);
impl_hashencode!(WitnessMerkleNode);
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Expand Up @@ -123,7 +123,7 @@ pub use blockdata::transaction::Transaction;
pub use blockdata::transaction::TxIn;
pub use blockdata::transaction::TxOut;
pub use blockdata::transaction::OutPoint;
pub use blockdata::transaction::EcdsaSigHashType;
pub use blockdata::transaction::EcdsaSighashType;
pub use blockdata::witness::Witness;
pub use consensus::encode::VarInt;
pub use network::constants::Network;
Expand All @@ -134,7 +134,7 @@ pub use util::amount::Amount;
pub use util::amount::Denomination;
pub use util::amount::SignedAmount;
pub use util::merkleblock::MerkleBlock;
pub use util::sighash::SchnorrSigHashType;
pub use util::sighash::SchnorrSighashType;

pub use util::ecdsa::{self, EcdsaSig, EcdsaSigError};
pub use util::schnorr::{self, SchnorrSig, SchnorrSigError};
Expand Down
134 changes: 132 additions & 2 deletions src/util/address.rs
Expand Up @@ -39,9 +39,9 @@ use core::num::ParseIntError;
use core::str::FromStr;
#[cfg(feature = "std")] use std::error;

use secp256k1::{Secp256k1, Verification};
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use bech32;
use hashes::Hash;
use hashes::{sha256, Hash, HashEngine};
use hash_types::{PubkeyHash, ScriptHash};
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};
Expand Down Expand Up @@ -714,6 +714,37 @@ impl Address {
(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
Expand Down Expand Up @@ -857,6 +888,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)]
mod tests {
use core::str::FromStr;
Expand Down Expand Up @@ -1260,4 +1299,95 @@ mod tests {
assert_eq!(address.address_type(), Some(AddressType::P2tr));
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);
}
}

0 comments on commit 06b697c

Please sign in to comment.