Skip to content

Commit afd3607

Browse files
committed
Inline script_pubkey Script extension traits
The separation of extension traits into the `script_pubkey` module does not assist clarity and makes it less ergonomic for users having to import traits from different locations. We can put these methods on the `script::ScriptExt` and fix both issues.
1 parent f8823f5 commit afd3607

File tree

5 files changed

+79
-88
lines changed

5 files changed

+79
-88
lines changed

bitcoin/src/address/script_pubkey.rs

Lines changed: 2 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
//! Bitcoin scriptPubkey script extensions.
44
5-
use internals::array::ArrayExt;
65
use secp256k1::{Secp256k1, Verification};
76

87
use crate::internal_macros::define_extension_trait;
@@ -12,90 +11,9 @@ use crate::key::{
1211
use crate::opcodes::all::*;
1312
use crate::script::witness_program::{WitnessProgram, P2A_PROGRAM};
1413
use crate::script::witness_version::WitnessVersion;
15-
use crate::script::{
16-
self, Builder, PushBytes, RedeemScriptSizeError, Script, ScriptBuf, ScriptExt as _, ScriptHash,
17-
WScriptHash, WitnessScriptSizeError,
18-
};
14+
use crate::script::{Builder, PushBytes, Script, ScriptBuf, ScriptHash, WScriptHash};
1915
use crate::taproot::TapNodeHash;
2016

21-
define_extension_trait! {
22-
/// Extension functionality to add scriptPubkey support to the [`Script`] type.
23-
pub trait ScriptExt impl for Script {
24-
/// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem
25-
/// script").
26-
fn to_p2wsh(&self) -> Result<ScriptBuf, WitnessScriptSizeError> {
27-
self.wscript_hash().map(ScriptBuf::new_p2wsh)
28-
}
29-
30-
/// Computes P2TR output with a given internal key and a single script spending path equal to
31-
/// the current script, assuming that the script is a Tapscript.
32-
fn to_p2tr<C: Verification, K: Into<UntweakedPublicKey>>(
33-
&self,
34-
secp: &Secp256k1<C>,
35-
internal_key: K,
36-
) -> ScriptBuf {
37-
let internal_key = internal_key.into();
38-
let leaf_hash = self.tapscript_leaf_hash();
39-
let merkle_root = TapNodeHash::from(leaf_hash);
40-
ScriptBuf::new_p2tr(secp, internal_key, Some(merkle_root))
41-
}
42-
43-
/// Computes the P2SH output corresponding to this redeem script.
44-
fn to_p2sh(&self) -> Result<ScriptBuf, RedeemScriptSizeError> {
45-
self.script_hash().map(ScriptBuf::new_p2sh)
46-
}
47-
48-
/// Returns the script code used for spending a P2WPKH output if this script is a script pubkey
49-
/// for a P2WPKH output. The `scriptCode` is described in [BIP143].
50-
///
51-
/// [BIP143]: <https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki>
52-
fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
53-
if self.is_p2wpkh() {
54-
// The `self` script is 0x00, 0x14, <pubkey_hash>
55-
let bytes = <[u8; 20]>::try_from(&self.as_bytes()[2..]).expect("length checked in is_p2wpkh()");
56-
let wpkh = WPubkeyHash::from_byte_array(bytes);
57-
Some(script::p2wpkh_script_code(wpkh))
58-
} else {
59-
None
60-
}
61-
}
62-
63-
/// Checks whether a script pubkey is a P2PK output.
64-
///
65-
/// You can obtain the public key, if its valid,
66-
/// by calling [`p2pk_public_key()`](Self::p2pk_public_key)
67-
fn is_p2pk(&self) -> bool { self.p2pk_pubkey_bytes().is_some() }
68-
69-
/// Returns the public key if this script is P2PK with a **valid** public key.
70-
///
71-
/// This may return `None` even when [`is_p2pk()`](Self::is_p2pk) returns true.
72-
/// This happens when the public key is invalid (e.g. the point not being on the curve).
73-
/// In this situation the script is unspendable.
74-
fn p2pk_public_key(&self) -> Option<PublicKey> {
75-
PublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
76-
}
77-
}
78-
}
79-
80-
define_extension_trait! {
81-
pub(crate) trait ScriptExtPrivate impl for Script {
82-
/// Returns the bytes of the (possibly invalid) public key if this script is P2PK.
83-
fn p2pk_pubkey_bytes(&self) -> Option<&[u8]> {
84-
if let Ok(bytes) = <&[u8; 67]>::try_from(self.as_bytes()) {
85-
let (&first, bytes) = bytes.split_first::<66>();
86-
let (&last, pubkey) = bytes.split_last::<65>();
87-
(first == OP_PUSHBYTES_65.to_u8() && last == OP_CHECKSIG.to_u8()).then_some(pubkey)
88-
} else if let Ok(bytes) = <&[u8; 35]>::try_from(self.as_bytes()) {
89-
let (&first, bytes) = bytes.split_first::<34>();
90-
let (&last, pubkey) = bytes.split_last::<33>();
91-
(first == OP_PUSHBYTES_33.to_u8() && last == OP_CHECKSIG.to_u8()).then_some(pubkey)
92-
} else {
93-
None
94-
}
95-
}
96-
}
97-
}
98-
9917
define_extension_trait! {
10018
/// Extension functionality to add scriptPubkey support to the [`ScriptBuf`] type.
10119
pub trait ScriptBufExt impl for ScriptBuf {
@@ -195,6 +113,7 @@ mod sealed {
195113
#[cfg(test)]
196114
mod tests {
197115
use super::*;
116+
use crate::script::ScriptExt as _;
198117

199118
#[test]
200119
fn shortest_witness_program() {

bitcoin/src/blockdata/script/borrowed.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@
33
use core::fmt;
44

55
use hex::DisplayHex as _;
6+
use internals::array::ArrayExt; // For `split_first`.
67
use internals::ToU64 as _;
8+
use secp256k1::{Secp256k1, Verification};
79

810
use super::witness_version::WitnessVersion;
911
use super::{
1012
Builder, Instruction, InstructionIndices, Instructions, PushBytes, RedeemScriptSizeError,
1113
ScriptHash, WScriptHash, WitnessScriptSizeError,
1214
};
15+
use crate::address::script_pubkey::ScriptBufExt as _;
1316
use crate::consensus::{self, Encodable};
17+
use crate::key::{PublicKey, UntweakedPublicKey, WPubkeyHash};
1418
use crate::opcodes::all::*;
1519
use crate::opcodes::{self, Opcode};
1620
use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
1721
use crate::prelude::{sink, String, ToString};
18-
use crate::taproot::{LeafVersion, TapLeafHash};
19-
use crate::{Amount, FeeRate};
22+
use crate::taproot::{LeafVersion, TapLeafHash, TapNodeHash};
23+
use crate::{script, Amount, FeeRate, ScriptBuf};
2024

2125
#[rustfmt::skip] // Keep public re-exports separate.
2226
#[doc(inline)]
@@ -50,6 +54,60 @@ crate::internal_macros::define_extension_trait! {
5054
TapLeafHash::from_script(self, LeafVersion::TapScript)
5155
}
5256

57+
/// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem
58+
/// script").
59+
fn to_p2wsh(&self) -> Result<ScriptBuf, WitnessScriptSizeError> {
60+
self.wscript_hash().map(ScriptBuf::new_p2wsh)
61+
}
62+
63+
/// Computes P2TR output with a given internal key and a single script spending path equal to
64+
/// the current script, assuming that the script is a Tapscript.
65+
fn to_p2tr<C: Verification, K: Into<UntweakedPublicKey>>(
66+
&self,
67+
secp: &Secp256k1<C>,
68+
internal_key: K,
69+
) -> ScriptBuf {
70+
let internal_key = internal_key.into();
71+
let leaf_hash = self.tapscript_leaf_hash();
72+
let merkle_root = TapNodeHash::from(leaf_hash);
73+
ScriptBuf::new_p2tr(secp, internal_key, Some(merkle_root))
74+
}
75+
76+
/// Computes the P2SH output corresponding to this redeem script.
77+
fn to_p2sh(&self) -> Result<ScriptBuf, RedeemScriptSizeError> {
78+
self.script_hash().map(ScriptBuf::new_p2sh)
79+
}
80+
81+
/// Returns the script code used for spending a P2WPKH output if this script is a script pubkey
82+
/// for a P2WPKH output. The `scriptCode` is described in [BIP143].
83+
///
84+
/// [BIP143]: <https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki>
85+
fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
86+
if self.is_p2wpkh() {
87+
// The `self` script is 0x00, 0x14, <pubkey_hash>
88+
let bytes = <[u8; 20]>::try_from(&self.as_bytes()[2..]).expect("length checked in is_p2wpkh()");
89+
let wpkh = WPubkeyHash::from_byte_array(bytes);
90+
Some(script::p2wpkh_script_code(wpkh))
91+
} else {
92+
None
93+
}
94+
}
95+
96+
/// Checks whether a script pubkey is a P2PK output.
97+
///
98+
/// You can obtain the public key, if its valid,
99+
/// by calling [`p2pk_public_key()`](Self::p2pk_public_key)
100+
fn is_p2pk(&self) -> bool { self.p2pk_pubkey_bytes().is_some() }
101+
102+
/// Returns the public key if this script is P2PK with a **valid** public key.
103+
///
104+
/// This may return `None` even when [`is_p2pk()`](Self::is_p2pk) returns true.
105+
/// This happens when the public key is invalid (e.g. the point not being on the curve).
106+
/// In this situation the script is unspendable.
107+
fn p2pk_public_key(&self) -> Option<PublicKey> {
108+
PublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
109+
}
110+
53111
/// Returns witness version of the script, if any, assuming the script is a `scriptPubkey`.
54112
///
55113
/// # Returns
@@ -407,6 +465,21 @@ mod sealed {
407465

408466
crate::internal_macros::define_extension_trait! {
409467
pub(crate) trait ScriptExtPriv impl for Script {
468+
/// Returns the bytes of the (possibly invalid) public key if this script is P2PK.
469+
fn p2pk_pubkey_bytes(&self) -> Option<&[u8]> {
470+
if let Ok(bytes) = <&[u8; 67]>::try_from(self.as_bytes()) {
471+
let (&first, bytes) = bytes.split_first::<66>();
472+
let (&last, pubkey) = bytes.split_last::<65>();
473+
(first == OP_PUSHBYTES_65.to_u8() && last == OP_CHECKSIG.to_u8()).then_some(pubkey)
474+
} else if let Ok(bytes) = <&[u8; 35]>::try_from(self.as_bytes()) {
475+
let (&first, bytes) = bytes.split_first::<34>();
476+
let (&last, pubkey) = bytes.split_last::<33>();
477+
(first == OP_PUSHBYTES_33.to_u8() && last == OP_CHECKSIG.to_u8()).then_some(pubkey)
478+
} else {
479+
None
480+
}
481+
}
482+
410483
fn minimal_non_dust_internal(&self, dust_relay_fee_rate_per_kvb: u64) -> Option<Amount> {
411484
// This must never be lower than Bitcoin Core's GetDustThreshold() (as of v0.21) as it may
412485
// otherwise allow users to create transactions which likely can never be broadcast/confirmed.

bitcoin/src/blockdata/script/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use hex_lit::hex;
44

55
use super::*;
6-
use crate::address::script_pubkey::{ScriptBufExt as _, ScriptExt as _, ScriptExtPrivate as _};
6+
use crate::address::script_pubkey::ScriptBufExt as _;
77
use crate::consensus::encode::{deserialize, serialize};
88
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
99
use crate::{opcodes, Amount, FeeRate};

bitcoin/src/crypto/sighash.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ use hashes::{hash_newtype, sha256, sha256d, sha256t, sha256t_tag};
2020
use internals::write_err;
2121
use io::Write;
2222

23-
use crate::address::script_pubkey::ScriptExt as _;
2423
use crate::consensus::{encode, Encodable};
2524
use crate::prelude::{Borrow, BorrowMut, String, ToOwned};
25+
use crate::script::ScriptExt as _;
2626
use crate::taproot::{LeafVersion, TapLeafHash, TapLeafTag, TAPROOT_ANNEX_PREFIX};
2727
use crate::transaction::TransactionExt as _;
2828
use crate::witness::Witness;

bitcoin/src/psbt/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,6 @@ mod tests {
13371337
};
13381338

13391339
use super::*;
1340-
use crate::address::script_pubkey::ScriptExt as _;
13411340
use crate::bip32::{ChildNumber, DerivationPath};
13421341
use crate::locktime::absolute;
13431342
use crate::network::NetworkKind;

0 commit comments

Comments
 (0)