Merge rust-bitcoin/rust-bitcoin#2955: Add scriptPubkey extension traits to script types

bcf6d2839e Introduce scriptPubkey extension traits (Tobin C. Harding)
ee333defa4 Remove path from ScriptBuf (Tobin C. Harding)

Pull request description:

  Done in preparation for moving the script types to `primitives`.

  Add three public traits, one each for the three `script` types: `Builder`, `ScriptBuf`, and `Script`.

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

Tree-SHA512: 0ad11e474ddf1183d0119e36454cb4fd18d49a68655d274df800c6ef20afa7f8d0fdecd415c02595ea67a011e3a842b7ccc23c2d58f92ed9acbdc7f277fbd217
This commit is contained in:
merge-script 2024-07-14 19:53:26 +00:00
commit 11f28e2728
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
16 changed files with 340 additions and 206 deletions

View File

@ -25,6 +25,7 @@
use std::collections::BTreeMap;
use std::str::FromStr;
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub};
use bitcoin::locktime::absolute;
use bitcoin::psbt::Input;

View File

@ -32,6 +32,7 @@ use std::collections::BTreeMap;
use std::fmt;
use std::str::FromStr;
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub};
use bitcoin::consensus::encode;
use bitcoin::locktime::absolute;

View File

@ -1,3 +1,4 @@
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::{
consensus, ecdsa, sighash, Amount, CompressedPublicKey, Script, ScriptBuf, Transaction,
};

View File

@ -4,6 +4,7 @@
use std::str::FromStr;
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing};
use bitcoin::sighash::{EcdsaSighashType, SighashCache};

View File

@ -4,6 +4,7 @@
use std::str::FromStr;
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::key::{Keypair, TapTweak, TweakedKeypair, UntweakedPublicKey};
use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing, Verification};

View File

@ -23,6 +23,7 @@
use std::collections::BTreeMap;
use std::str::FromStr;
use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPath, Xpriv, Xpub};
use bitcoin::key::UntweakedPublicKey;
use bitcoin::locktime::absolute;

View File

@ -78,6 +78,7 @@ const UTXO_3: P2trUtxo = P2trUtxo {
use std::collections::BTreeMap;
use std::str::FromStr;
use bitcoin::address::script_pubkey::{BuilderExt as _, ScriptBufExt as _};
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpriv, Xpub};
use bitcoin::consensus::encode;
use bitcoin::key::{TapTweak, XOnlyPublicKey};

View File

@ -27,6 +27,7 @@
//! ```
pub mod error;
pub mod script_pubkey;
use core::fmt;
use core::marker::PhantomData;
@ -37,6 +38,7 @@ use bech32::primitives::hrp::Hrp;
use hashes::{hash160, HashEngine};
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use crate::address::script_pubkey::ScriptBufExt as _;
use crate::consensus::Params;
use crate::constants::{
PUBKEY_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_MAIN,
@ -604,7 +606,7 @@ impl Address {
Segwit { ref program, hrp: _ } => {
let prog = program.program();
let version = program.version();
ScriptBuf::new_witness_program_unchecked(version, prog)
script_pubkey::new_witness_program_unchecked(version, prog)
}
}
}
@ -803,7 +805,7 @@ impl Address<NetworkUnchecked> {
}
}
impl From<Address> for script::ScriptBuf {
impl From<Address> for ScriptBuf {
fn from(a: Address) -> Self { a.script_pubkey() }
}

View File

@ -0,0 +1,215 @@
// SPDX-License-Identifier: CC0-1.0
//! Bitcoin scriptPubkey script extensions.
use secp256k1::{Secp256k1, Verification};
use crate::internal_macros::define_extension_trait;
use crate::key::{
PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey, WPubkeyHash,
XOnlyPublicKey,
};
use crate::opcodes::all::*;
use crate::script::witness_program::WitnessProgram;
use crate::script::witness_version::WitnessVersion;
use crate::script::{
Builder, PushBytes, RedeemScriptSizeError, Script, ScriptBuf, ScriptHash, WScriptHash,
WitnessScriptSizeError,
};
use crate::taproot::TapNodeHash;
define_extension_trait! {
/// Extension functionality to add scriptPubkey support to the [`Builder`] type.
pub trait BuilderExt impl for Builder {
/// Adds instructions to push a public key onto the stack.
fn push_key(self, key: PublicKey) -> Builder {
if key.compressed {
self.push_slice(key.inner.serialize())
} else {
self.push_slice(key.inner.serialize_uncompressed())
}
}
/// Adds instructions to push an XOnly public key onto the stack.
fn push_x_only_key(self, x_only_key: XOnlyPublicKey) -> Builder {
self.push_slice(x_only_key.serialize())
}
}
}
define_extension_trait! {
/// Extension functionality to add scriptPubkey support to the [`Script`] type.
pub trait ScriptExt impl for Script {
/// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem
/// script").
fn to_p2wsh(&self) -> Result<ScriptBuf, WitnessScriptSizeError> {
self.wscript_hash().map(ScriptBuf::new_p2wsh)
}
/// Computes P2TR output with a given internal key and a single script spending path equal to
/// the current script, assuming that the script is a Tapscript.
fn to_p2tr<C: Verification>(&self, secp: &Secp256k1<C>, internal_key: UntweakedPublicKey) -> ScriptBuf {
let leaf_hash = self.tapscript_leaf_hash();
let merkle_root = TapNodeHash::from(leaf_hash);
ScriptBuf::new_p2tr(secp, internal_key, Some(merkle_root))
}
/// Computes the P2SH output corresponding to this redeem script.
fn to_p2sh(&self) -> Result<ScriptBuf, RedeemScriptSizeError> {
self.script_hash().map(ScriptBuf::new_p2sh)
}
/// Returns the script code used for spending a P2WPKH output if this script is a script pubkey
/// for a P2WPKH output. The `scriptCode` is described in [BIP143].
///
/// [BIP143]: <https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki>
fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
if self.is_p2wpkh() {
// The `self` script is 0x00, 0x14, <pubkey_hash>
let bytes = &self.as_bytes()[2..];
let wpkh = WPubkeyHash::from_slice(bytes).expect("length checked in is_p2wpkh()");
Some(ScriptBuf::p2wpkh_script_code(wpkh))
} else {
None
}
}
/// Checks whether a script pubkey is a P2PK output.
///
/// You can obtain the public key, if its valid,
/// by calling [`p2pk_public_key()`](Self::p2pk_public_key)
fn is_p2pk(&self) -> bool { self.p2pk_pubkey_bytes().is_some() }
/// Returns the public key if this script is P2PK with a **valid** public key.
///
/// This may return `None` even when [`is_p2pk()`](Self::is_p2pk) returns true.
/// This happens when the public key is invalid (e.g. the point not being on the curve).
/// In this situation the script is unspendable.
fn p2pk_public_key(&self) -> Option<PublicKey> {
PublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
}
}
}
define_extension_trait! {
pub(crate) trait ScriptExtPrivate impl for Script {
/// Returns the bytes of the (possibly invalid) public key if this script is P2PK.
fn p2pk_pubkey_bytes(&self) -> Option<&[u8]> {
match self.len() {
67 if self.as_bytes()[0] == OP_PUSHBYTES_65.to_u8()
&& self.as_bytes()[66] == OP_CHECKSIG.to_u8() =>
Some(&self.as_bytes()[1..66]),
35 if self.as_bytes()[0] == OP_PUSHBYTES_33.to_u8()
&& self.as_bytes()[34] == OP_CHECKSIG.to_u8() =>
Some(&self.as_bytes()[1..34]),
_ => None,
}
}
}
}
define_extension_trait! {
/// Extension functionality to add scriptPubkey support to the [`ScriptBuf`] type.
pub trait ScriptBufExt impl for ScriptBuf {
/// Generates P2PK-type of scriptPubkey.
fn new_p2pk(pubkey: PublicKey) -> Self {
Builder::new().push_key(pubkey).push_opcode(OP_CHECKSIG).into_script()
}
/// Generates P2PKH-type of scriptPubkey.
fn new_p2pkh(pubkey_hash: PubkeyHash) -> Self {
Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(pubkey_hash)
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script()
}
/// Generates P2SH-type of scriptPubkey with a given hash of the redeem script.
fn new_p2sh(script_hash: ScriptHash) -> Self {
Builder::new()
.push_opcode(OP_HASH160)
.push_slice(script_hash)
.push_opcode(OP_EQUAL)
.into_script()
}
/// Generates P2WPKH-type of scriptPubkey.
fn new_p2wpkh(pubkey_hash: WPubkeyHash) -> Self {
// pubkey hash is 20 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
new_witness_program_unchecked(WitnessVersion::V0, pubkey_hash)
}
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script.
fn new_p2wsh(script_hash: WScriptHash) -> Self {
// script hash is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
new_witness_program_unchecked(WitnessVersion::V0, script_hash)
}
/// Generates P2TR for script spending path using an internal public key and some optional
/// script tree Merkle root.
fn new_p2tr<C: Verification>(secp: &Secp256k1<C>, internal_key: UntweakedPublicKey, merkle_root: Option<TapNodeHash>) -> Self {
let (output_key, _) = internal_key.tap_tweak(secp, merkle_root);
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
fn new_p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
fn new_witness_program(witness_program: &WitnessProgram) -> Self {
Builder::new()
.push_opcode(witness_program.version().into())
.push_slice(witness_program.program())
.into_script()
}
}
}
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and the program bytes.
/// Does not do any checks on version or program length.
///
/// Convenience method used by `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
pub(super) fn new_witness_program_unchecked<T: AsRef<PushBytes>>(
version: WitnessVersion,
program: T,
) -> ScriptBuf {
let program = program.as_ref();
debug_assert!(program.len() >= 2 && program.len() <= 40);
// In segwit v0, the program must be 20 or 32 bytes long.
debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
Builder::new().push_opcode(version.into()).push_slice(program).into_script()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shortest_witness_program() {
let bytes = [0x00; 2]; // Arbitrary bytes, witprog must be between 2 and 40.
let version = WitnessVersion::V15; // Arbitrary version number, intentionally not 0 or 1.
let p = WitnessProgram::new(version, &bytes).expect("failed to create witness program");
let script = ScriptBuf::new_witness_program(&p);
assert_eq!(script.witness_version(), Some(version));
}
#[test]
fn longest_witness_program() {
let bytes = [0x00; 40]; // Arbitrary bytes, witprog must be between 2 and 40.
let version = WitnessVersion::V16; // Arbitrary version number, intentionally not 0 or 1.
let p = WitnessProgram::new(version, &bytes).expect("failed to create witness program");
let script = ScriptBuf::new_witness_program(&p);
assert_eq!(script.witness_version(), Some(version));
}
}

View File

@ -5,20 +5,17 @@ use core::ops::{
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
use secp256k1::{Secp256k1, Verification};
use super::witness_version::WitnessVersion;
use super::{
bytes_to_asm_fmt, Builder, Instruction, InstructionIndices, Instructions, PushBytes,
RedeemScriptSizeError, ScriptBuf, ScriptHash, WScriptHash, WitnessScriptSizeError,
};
use crate::consensus::Encodable;
use crate::key::{PublicKey, UntweakedPublicKey, WPubkeyHash};
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
use crate::policy::DUST_RELAY_TX_FEE;
use crate::prelude::{sink, Box, DisplayHex, String, ToOwned, Vec};
use crate::taproot::{LeafVersion, TapLeafHash, TapNodeHash};
use crate::taproot::{LeafVersion, TapLeafHash};
use crate::FeeRate;
/// Bitcoin script slice.
@ -148,26 +145,6 @@ impl Script {
#[inline]
pub fn bytes(&self) -> Bytes<'_> { Bytes(self.as_bytes().iter().copied()) }
/// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem
/// script").
#[inline]
pub fn to_p2wsh(&self) -> Result<ScriptBuf, WitnessScriptSizeError> {
self.wscript_hash().map(ScriptBuf::new_p2wsh)
}
/// Computes P2TR output with a given internal key and a single script spending path equal to
/// the current script, assuming that the script is a Tapscript.
#[inline]
pub fn to_p2tr<C: Verification>(
&self,
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
) -> ScriptBuf {
let leaf_hash = self.tapscript_leaf_hash();
let merkle_root = TapNodeHash::from(leaf_hash);
ScriptBuf::new_p2tr(secp, internal_key, Some(merkle_root))
}
/// Returns witness version of the script, if any, assuming the script is a `scriptPubkey`.
///
/// # Returns
@ -238,35 +215,6 @@ impl Script {
true
}
/// Checks whether a script pubkey is a P2PK output.
///
/// You can obtain the public key, if its valid,
/// by calling [`p2pk_public_key()`](Self::p2pk_public_key)
#[inline]
pub fn is_p2pk(&self) -> bool { self.p2pk_pubkey_bytes().is_some() }
/// Returns the public key if this script is P2PK with a **valid** public key.
///
/// This may return `None` even when [`is_p2pk()`](Self::is_p2pk) returns true.
/// This happens when the public key is invalid (e.g. the point not being on the curve).
/// In this situation the script is unspendable.
#[inline]
pub fn p2pk_public_key(&self) -> Option<PublicKey> {
PublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
}
/// Returns the bytes of the (possibly invalid) public key if this script is P2PK.
#[inline]
pub(in crate::blockdata::script) fn p2pk_pubkey_bytes(&self) -> Option<&[u8]> {
match self.len() {
67 if self.0[0] == OP_PUSHBYTES_65.to_u8() && self.0[66] == OP_CHECKSIG.to_u8() =>
Some(&self.0[1..66]),
35 if self.0[0] == OP_PUSHBYTES_33.to_u8() && self.0[34] == OP_CHECKSIG.to_u8() =>
Some(&self.0[1..34]),
_ => None,
}
}
/// Checks whether a script pubkey is a bare multisig output.
///
/// In a bare multisig pubkey script the keys are not hashed, the script
@ -390,26 +338,6 @@ impl Script {
}
}
/// Computes the P2SH output corresponding to this redeem script.
pub fn to_p2sh(&self) -> Result<ScriptBuf, RedeemScriptSizeError> {
self.script_hash().map(ScriptBuf::new_p2sh)
}
/// Returns the script code used for spending a P2WPKH output if this script is a script pubkey
/// for a P2WPKH output. The `scriptCode` is described in [BIP143].
///
/// [BIP143]: <https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki>
pub fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
if self.is_p2wpkh() {
// The `self` script is 0x00, 0x14, <pubkey_hash>
let bytes = &self.0[2..];
let wpkh = WPubkeyHash::from_slice(bytes).expect("length checked in is_p2wpkh()");
Some(ScriptBuf::p2wpkh_script_code(wpkh))
} else {
None
}
}
/// Get redeemScript following BIP16 rules regarding P2SH spending.
///
/// This does not guarantee that this represents a P2SH input [`Script`].
@ -709,31 +637,3 @@ delegate_index!(
RangeToInclusive<usize>,
(Bound<usize>, Bound<usize>)
);
#[cfg(test)]
mod tests {
use super::*;
use crate::script::witness_program::WitnessProgram;
#[test]
fn shortest_witness_program() {
let bytes = [0x00; 2]; // Arbitrary bytes, witprog must be between 2 and 40.
let version = WitnessVersion::V15; // Arbitrary version number, intentionally not 0 or 1.
let p = WitnessProgram::new(version, &bytes).expect("failed to create witness program");
let script = ScriptBuf::new_witness_program(&p);
assert_eq!(script.witness_version(), Some(version));
}
#[test]
fn longest_witness_program() {
let bytes = [0x00; 40]; // Arbitrary bytes, witprog must be between 2 and 40.
let version = WitnessVersion::V16; // Arbitrary version number, intentionally not 0 or 1.
let p = WitnessProgram::new(version, &bytes).expect("failed to create witness program");
let script = ScriptBuf::new_witness_program(&p);
assert_eq!(script.witness_version(), Some(version));
}
}

View File

@ -2,10 +2,7 @@
use core::fmt;
use secp256k1::XOnlyPublicKey;
use super::{opcode_to_verify, write_scriptint, PushBytes, Script, ScriptBuf};
use crate::key::PublicKey;
use crate::locktime::absolute;
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
@ -63,20 +60,6 @@ impl Builder {
self
}
/// Adds instructions to push a public key onto the stack.
pub fn push_key(self, key: PublicKey) -> Builder {
if key.compressed {
self.push_slice(key.inner.serialize())
} else {
self.push_slice(key.inner.serialize_uncompressed())
}
}
/// 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: Opcode) -> Builder {
self.0.push_opcode(data);

View File

@ -4,18 +4,12 @@
use core::ops::Deref;
use hex::FromHex;
use secp256k1::{Secp256k1, Verification};
use super::witness_program::WitnessProgram;
use super::witness_version::WitnessVersion;
use super::{opcode_to_verify, Builder, Instruction, PushBytes, Script, ScriptHash, WScriptHash};
use crate::key::{
PubkeyHash, PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey, WPubkeyHash,
};
use super::{opcode_to_verify, Builder, Instruction, PushBytes, Script};
use crate::key::WPubkeyHash;
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
use crate::prelude::{Box, Vec};
use crate::taproot::TapNodeHash;
/// An owned, growable script.
///
@ -73,84 +67,6 @@ impl ScriptBuf {
/// Creates a new script builder
pub fn builder() -> Builder { Builder::new() }
/// Generates P2PK-type of scriptPubkey.
pub fn new_p2pk(pubkey: PublicKey) -> Self {
Builder::new().push_key(pubkey).push_opcode(OP_CHECKSIG).into_script()
}
/// Generates P2PKH-type of scriptPubkey.
pub fn new_p2pkh(pubkey_hash: PubkeyHash) -> Self {
Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(pubkey_hash)
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script()
}
/// Generates P2SH-type of scriptPubkey with a given hash of the redeem script.
pub fn new_p2sh(script_hash: ScriptHash) -> Self {
Builder::new()
.push_opcode(OP_HASH160)
.push_slice(script_hash)
.push_opcode(OP_EQUAL)
.into_script()
}
/// Generates P2WPKH-type of scriptPubkey.
pub fn new_p2wpkh(pubkey_hash: WPubkeyHash) -> Self {
// pubkey hash is 20 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, pubkey_hash)
}
/// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script.
pub fn new_p2wsh(script_hash: WScriptHash) -> Self {
// script hash is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, script_hash)
}
/// Generates P2TR for script spending path using an internal public key and some optional
/// script tree Merkle root.
pub fn new_p2tr<C: Verification>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
merkle_root: Option<TapNodeHash>,
) -> Self {
let (output_key, _) = internal_key.tap_tweak(secp, merkle_root);
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
pub fn new_p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
pub fn new_witness_program(witness_program: &WitnessProgram) -> Self {
Builder::new()
.push_opcode(witness_program.version().into())
.push_slice(witness_program.program())
.into_script()
}
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and the program bytes.
/// Does not do any checks on version or program length.
///
/// Convenience method used by `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
pub(crate) fn new_witness_program_unchecked<T: AsRef<PushBytes>>(
version: WitnessVersion,
program: T,
) -> Self {
let program = program.as_ref();
debug_assert!(program.len() >= 2 && program.len() <= 40);
// In segwit v0, the program must be 20 or 32 bytes long.
debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
Builder::new().push_opcode(version.into()).push_slice(program).into_script()
}
/// Creates the script code used for spending a P2WPKH output.
///
/// The `scriptCode` is described in [BIP143].

View File

@ -5,6 +5,9 @@ use core::str::FromStr;
use hex_lit::hex;
use super::*;
use crate::address::script_pubkey::{
BuilderExt as _, ScriptBufExt as _, ScriptExt as _, ScriptExtPrivate as _,
};
use crate::consensus::encode::{deserialize, serialize};
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
use crate::FeeRate;

View File

@ -17,6 +17,7 @@ use hashes::{hash_newtype, sha256, sha256d, sha256t_hash_newtype};
use internals::write_err;
use io::Write;
use crate::address::script_pubkey::ScriptExt as _;
use crate::consensus::{encode, Encodable};
use crate::prelude::{Borrow, BorrowMut, String, ToOwned, Vec};
use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX};

View File

@ -211,3 +211,108 @@ macro_rules! impl_asref_push_bytes {
};
}
pub(crate) use impl_asref_push_bytes;
/// Defines an trait `$trait_name` and implements it for `ty`, used to define extension traits.
macro_rules! define_extension_trait {
// With self, no generics.
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
$(
$(#[$($fn_attrs:tt)*])*
fn $fn:ident($slf:ident $(, $param_name:ident: $param_type:ty)*) $( -> $ret:ty )? $body:block
)*
}) => {
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name {
$(
$(#[$($fn_attrs)*])*
fn $fn($slf $(, $param_name: $param_type)*) $( -> $ret )?;
)*
}
impl $trait_name for $ty {
$(
fn $fn($slf $(, $param_name: $param_type)*) $( -> $ret )? $body
)*
}
};
// With &self, no generics.
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
$(
$(#[$($fn_attrs:tt)*])*
fn $fn:ident(&$slf:ident $(, $param_name:ident: $param_type:ty)*) $( -> $ret:ty )? $body:block
)*
}) => {
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name {
$(
$(#[$($fn_attrs)*])*
fn $fn(&$slf $(, $param_name: $param_type)*) $( -> $ret )?;
)*
}
impl $trait_name for $ty {
$(
fn $fn(&$slf $(, $param_name: $param_type)*) $( -> $ret )? $body
)*
}
};
// With &self, with generics.
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
$(
$(#[$($fn_attrs:tt)*])*
fn $fn:ident$(<$($gen:ident: $gent:ident),*>)?(&$slf:ident $(, $param_name:ident: $param_type:ty)*) $( -> $ret:ty )? $body:block
)*
}) => {
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name {
$(
$(#[$($fn_attrs)*])*
fn $fn$(<$($gen: $gent),*>)?(&$slf $(, $param_name: $param_type)*) $( -> $ret )?;
)*
}
impl $trait_name for $ty {
$(
fn $fn$(<$($gen: $gent),*>)?(&$slf $(, $param_name: $param_type)*) $( -> $ret )? $body
)*
}
};
// No self, with generics.
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
$(
$(#[$($fn_attrs:tt)*])*
fn $fn:ident$(<$($gen:ident: $gent:ident),*>)?($($param_name:ident: $param_type:ty),*) $( -> $ret:ty )? $body:block
)*
}) => {
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name {
$(
$(#[$($fn_attrs)*])*
fn $fn$(<$($gen: $gent),*>)?($($param_name: $param_type),*) $( -> $ret )?;
)*
}
impl $trait_name for $ty {
$(
fn $fn$(<$($gen: $gent),*>)?($($param_name: $param_type),*) $( -> $ret )? $body
)*
}
};
// No self, with generic `<T: AsRef<PushBytes>>`
($(#[$($trait_attrs:tt)*])* $trait_vis:vis trait $trait_name:ident impl for $ty:ident {
$(
$(#[$($fn_attrs:tt)*])*
fn $fn:ident<T: AsRef<PushBytes>>($($param_name:ident: $param_type:ty),*) $( -> $ret:ty )? $body:block
)*
}) => {
$(#[$($trait_attrs)*])* $trait_vis trait $trait_name {
$(
$(#[$($fn_attrs)*])*
fn $fn<T: AsRef<PushBytes>>($($param_name: $param_type),*) $( -> $ret )?;
)*
}
impl $trait_name for $ty {
$(
fn $fn<T: AsRef<PushBytes>>($($param_name: $param_type),*) $( -> $ret )? $body
)*
}
};
}
pub(crate) use define_extension_trait;

View File

@ -1211,6 +1211,7 @@ mod tests {
use secp256k1::{All, SecretKey};
use super::*;
use crate::address::script_pubkey::ScriptExt as _;
use crate::bip32::ChildNumber;
use crate::locktime::absolute;
use crate::network::NetworkKind;
@ -2249,6 +2250,7 @@ mod tests {
#[test]
#[cfg(feature = "rand-std")]
fn sign_psbt() {
use crate::address::script_pubkey::ScriptBufExt as _;
use crate::bip32::{DerivationPath, Fingerprint};
use crate::witness_version::WitnessVersion;
use crate::WitnessProgram;