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: ACKbcf6d2839e
apoelstra: ACKbcf6d2839e
Tree-SHA512: 0ad11e474ddf1183d0119e36454cb4fd18d49a68655d274df800c6ef20afa7f8d0fdecd415c02595ea67a011e3a842b7ccc23c2d58f92ed9acbdc7f277fbd217
This commit is contained in:
commit
11f28e2728
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use bitcoin::address::script_pubkey::ScriptBufExt as _;
|
||||
use bitcoin::{
|
||||
consensus, ecdsa, sighash, Amount, CompressedPublicKey, Script, ScriptBuf, Transaction,
|
||||
};
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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() }
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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].
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue