Move is_ and new_ script functions to primitives
What changed, and why it matters
This commit is a routine internal code reorganization. It moves helper functions that identify and create common Bitcoin script types (like P2SH, P2WSH, P2WPKH, P2A) from the main `bitcoin` crate into the lower-level `primitives` crate. The actual logic of these functions is copied unchanged; only their location in the codebase changes. There is no indication this fixes or introduces a security vulnerability.
No security action required. Treat as normal maintenance/refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates is_p2wsh, is_p2wpkh, is_p2sh, is_p2pkh, is_witness_program, is_p2a, new_p2sh, new_p2wsh, and new_p2a from bitcoin/src/blockdata/script/ to primitives/src/script/. Implementations are preserved verbatim, including byte-level checks and builder sequences. A #[allow(dead_code)] annotation is removed from new_witness_program_unchecked because it is now used by the moved constructors. One doc comment in witness.rs is updated to reflect the new method path. This is pure refactoring with no semantic change to script validation or creation behavior.
Changed components
bitcoin/src/blockdata/script/borrowed.rsbitcoin/src/blockdata/script/owned.rsbitcoin/src/address/mod.rsbitcoin/src/blockdata/witness.rsprimitives/src/script/borrowed.rsprimitives/src/script/owned.rsprimitives/src/script/mod.rsInspect captured patch +93 / −86
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index bc078a6b..4964f06b 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -65,9 +65,8 @@ use crate::script::witness_program::WitnessProgram;
use crate::script::witness_version::WitnessVersion;
use crate::script::{
self, BuilderExt as _, RedeemScriptSizeError, Script, ScriptExt as _, ScriptHash,
- ScriptHashableTag, ScriptPubKey, ScriptPubKeyBuf, ScriptPubKeyBufExt as _,
- ScriptPubKeyExt as _, WScriptHash, WitnessScript, WitnessScriptExt as _,
- WitnessScriptSizeError,
+ ScriptHashableTag, ScriptPubKey, ScriptPubKeyBuf, ScriptPubKeyBufExt as _, WScriptHash,
+ WitnessScript, WitnessScriptExt as _, WitnessScriptSizeError,
};
use crate::taproot::TapNodeHash;
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index 79351bd8..84cf2b67 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -18,7 +18,6 @@ use crate::policy::{DUST_RELAY_TX_FEE, MAX_OP_RETURN_RELAY};
use crate::prelude::{String, ToString};
use crate::script::{self, ScriptPubKeyBufExt as _};
use crate::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _, TapNodeHash};
-use crate::witness_program::P2A_PROGRAM;
use crate::{internal_macros, Amount, FeeRate, ScriptPubKeyBuf, ToU64 as _, WitnessScriptBuf};
internal_macros::define_extension_trait! {
@@ -179,26 +178,6 @@ internal_macros::define_extension_trait! {
None
}
}
-
- /// Checks whether a script pubkey is a P2WSH output.
- #[inline]
- fn is_p2wsh(&self) -> bool
- where T: ScriptHashableTag
- {
- self.len() == 34
- && self.witness_version() == Some(WitnessVersion::V0)
- && self.as_bytes()[1] == OP_PUSHBYTES_32.to_u8()
- }
-
- /// Checks whether a script pubkey is a P2WPKH output.
- #[inline]
- fn is_p2wpkh(&self) -> bool
- where T: ScriptHashableTag
- {
- self.len() == 22
- && self.witness_version() == Some(WitnessVersion::V0)
- && self.as_bytes()[1] == OP_PUSHBYTES_20.to_u8()
- }
}
}
@@ -260,26 +239,6 @@ internal_macros::define_extension_trait! {
LegacyPublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
}
- /// Checks whether a script pubkey is a P2SH output.
- #[inline]
- fn is_p2sh(&self) -> bool {
- self.len() == 23
- && self.as_bytes()[0] == OP_HASH160.to_u8()
- && self.as_bytes()[1] == OP_PUSHBYTES_20.to_u8()
- && self.as_bytes()[22] == OP_EQUAL.to_u8()
- }
-
- /// Checks whether a script pubkey is a P2PKH output.
- #[inline]
- fn is_p2pkh(&self) -> bool {
- self.len() == 25
- && self.as_bytes()[0] == OP_DUP.to_u8()
- && self.as_bytes()[1] == OP_HASH160.to_u8()
- && self.as_bytes()[2] == OP_PUSHBYTES_20.to_u8()
- && self.as_bytes()[23] == OP_EQUALVERIFY.to_u8()
- && self.as_bytes()[24] == OP_CHECKSIG.to_u8()
- }
-
/// Checks whether a script pubkey is a bare multisig output.
///
/// In a bare multisig pubkey script the keys are not hashed, the script
@@ -320,10 +279,6 @@ internal_macros::define_extension_trait! {
instructions.next().is_none()
}
- /// Checks whether a script pubkey is a Segregated Witness (SegWit) program.
- #[inline]
- fn is_witness_program(&self) -> bool { self.witness_version().is_some() }
-
/// Checks whether a script pubkey is a P2TR output.
#[inline]
fn is_p2tr(&self) -> bool {
@@ -332,15 +287,6 @@ internal_macros::define_extension_trait! {
&& self.as_bytes()[1] == OP_PUSHBYTES_32.to_u8()
}
- /// Checks whether a script pubkey is a P2A output.
- #[inline]
- fn is_p2a(&self) -> bool {
- self.len() == 4
- && self.witness_version() == Some(WitnessVersion::V1)
- && self.as_bytes()[1] == OP_PUSHBYTES_2.to_u8()
- && self.as_bytes()[2..] == P2A_PROGRAM
- }
-
/// Check if this is a consensus-valid OP_RETURN output.
///
/// To validate if the OP_RETURN obeys Bitcoin Core's current standardness policy, use
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index 8e14d3da..ffd5a297 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -14,9 +14,9 @@ use crate::key::{
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
use crate::prelude::Vec;
-use crate::script::witness_program::{WitnessProgram, P2A_PROGRAM};
+use crate::script::witness_program::WitnessProgram;
use crate::script::witness_version::WitnessVersion;
-use crate::script::{self, BuilderExt as _, ScriptHash, WScriptHash};
+use crate::script::{self, BuilderExt as _};
use crate::taproot::TapNodeHash;
use crate::{internal_macros, ToU64 as _};
@@ -147,21 +147,6 @@ crate::internal_macros::define_extension_trait! {
.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 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)
- script::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<K: Into<UntweakedPublicKey>>(
@@ -180,11 +165,6 @@ crate::internal_macros::define_extension_trait! {
script::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
}
- /// Generates pay to anchor output.
- fn new_p2a() -> Self {
- script::new_witness_program_unchecked(WitnessVersion::V1, P2A_PROGRAM)
- }
-
/// Generates P2WSH-type of scriptPubkey with a given [`WitnessProgram`].
fn new_witness_program(witness_program: &WitnessProgram) -> Self {
Builder::new()
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index 0a82f2f8..f237e090 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -152,7 +152,7 @@ internal_macros::define_extension_trait! {
/// Unlike the Taproot case, we do no validation to determine whether this is a
/// witness script: it may be a Taproot control block, annex, or some other kind
/// of object. If you are not certain whether the output being spent is Segwit v0,
- /// use [`crate::script::ScriptExt::is_p2wsh`] on the output's script.
+ /// use [`crate::script::Script::is_p2wsh`] on the output's script.
fn witness_script(&self) -> Option<&WitnessScript> { self.last().map(WitnessScript::from_bytes) }
}
}
diff --git a/primitives/src/script/borrowed.rs b/primitives/src/script/borrowed.rs
index a1c2c975..851e8093 100644
--- a/primitives/src/script/borrowed.rs
+++ b/primitives/src/script/borrowed.rs
@@ -12,11 +12,13 @@ use core::ops::{
use arbitrary::{Arbitrary, Unstructured};
use encoding::{BytesEncoder, CompactSizeEncoder, Encode, Encoder2};
-use super::ScriptBuf;
-use crate::opcodes::Opcode;
+use super::{ScriptBuf, P2A_PROGRAM};
+use crate::opcodes::all::{OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160};
+use crate::opcodes::{Opcode, OP_PUSHBYTES_2, OP_PUSHBYTES_20, OP_PUSHBYTES_32};
use crate::prelude::{Box, ToOwned, Vec};
use crate::script::ScriptHashableTag;
use crate::witness_version::WitnessVersion;
+use crate::ScriptPubKey;
// Defined in `REPO_DIR/include/newtype.rs`.
crate::transparent_newtype! {
@@ -219,6 +221,63 @@ impl<T> Script<T> {
WitnessVersion::try_from(ver_opcode).ok()
}
+
+ /// Checks whether a script pubkey is a P2WSH output.
+ #[inline]
+ pub fn is_p2wsh(&self) -> bool
+ where
+ T: ScriptHashableTag,
+ {
+ self.len() == 34
+ && self.witness_version() == Some(WitnessVersion::V0)
+ && self.as_bytes()[1] == OP_PUSHBYTES_32.to_u8()
+ }
+
+ /// Checks whether a script pubkey is a P2WPKH output.
+ #[inline]
+ pub fn is_p2wpkh(&self) -> bool
+ where
+ T: ScriptHashableTag,
+ {
+ self.len() == 22
+ && self.witness_version() == Some(WitnessVersion::V0)
+ && self.as_bytes()[1] == OP_PUSHBYTES_20.to_u8()
+ }
+}
+
+impl ScriptPubKey {
+ /// Checks whether a script pubkey is a Segregated Witness (SegWit) program.
+ #[inline]
+ pub fn is_witness_program(&self) -> bool { self.witness_version().is_some() }
+
+ /// Checks whether a script pubkey is a P2SH output.
+ #[inline]
+ pub fn is_p2sh(&self) -> bool {
+ self.len() == 23
+ && self.as_bytes()[0] == OP_HASH160.to_u8()
+ && self.as_bytes()[1] == OP_PUSHBYTES_20.to_u8()
+ && self.as_bytes()[22] == OP_EQUAL.to_u8()
+ }
+
+ /// Checks whether a script pubkey is a P2PKH output.
+ #[inline]
+ pub fn is_p2pkh(&self) -> bool {
+ self.len() == 25
+ && self.as_bytes()[0] == OP_DUP.to_u8()
+ && self.as_bytes()[1] == OP_HASH160.to_u8()
+ && self.as_bytes()[2] == OP_PUSHBYTES_20.to_u8()
+ && self.as_bytes()[23] == OP_EQUALVERIFY.to_u8()
+ && self.as_bytes()[24] == OP_CHECKSIG.to_u8()
+ }
+
+ /// Checks whether a script pubkey is a P2A output.
+ #[inline]
+ pub fn is_p2a(&self) -> bool {
+ self.len() == 4
+ && self.witness_version() == Some(WitnessVersion::V1)
+ && self.as_bytes()[1] == OP_PUSHBYTES_2.to_u8()
+ && self.as_bytes()[2..] == P2A_PROGRAM
+ }
}
impl<T> Encode for Script<T> {
diff --git a/primitives/src/script/mod.rs b/primitives/src/script/mod.rs
index cb9f4f0a..dbd027ee 100644
--- a/primitives/src/script/mod.rs
+++ b/primitives/src/script/mod.rs
@@ -97,7 +97,6 @@ pub(crate) const P2A_PROGRAM: [u8; 2] = [78, 115];
/// Does not do any checks on version or program length.
///
/// Convenience method used by `new_p2a`, `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
-#[allow(dead_code)]
pub(crate) fn new_witness_program_unchecked<T: AsRef<PushBytes>, Tg>(
version: WitnessVersion,
program: T,
diff --git a/primitives/src/script/owned.rs b/primitives/src/script/owned.rs
index 995bd1b6..0ae94588 100644
--- a/primitives/src/script/owned.rs
+++ b/primitives/src/script/owned.rs
@@ -7,11 +7,13 @@ use core::ops::{Deref, DerefMut};
use arbitrary::{Arbitrary, Unstructured};
use encoding::{ByteVecDecoder, DecoderStatus};
-use super::{Script, ScriptBufDecoderError};
-use crate::opcodes::all::{OP_1, OP_1NEGATE};
+use super::{Script, ScriptBufDecoderError, P2A_PROGRAM};
+use crate::opcodes::all::{OP_1, OP_1NEGATE, OP_EQUAL, OP_HASH160};
use crate::opcodes::{self, Opcode};
use crate::prelude::{Box, Vec};
-use crate::script::PushBytes;
+use crate::script::{Builder, PushBytes, ScriptHash, WScriptHash};
+use crate::witness_version::WitnessVersion;
+use crate::ScriptPubKeyBuf;
/// An owned, growable script.
///
@@ -258,6 +260,28 @@ impl<T> ScriptBuf<T> {
}
}
+impl ScriptPubKeyBuf {
+ /// 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 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)
+ super::new_witness_program_unchecked(WitnessVersion::V0, script_hash)
+ }
+
+ /// Generates pay to anchor output.
+ pub fn new_p2a() -> Self {
+ super::new_witness_program_unchecked(WitnessVersion::V1, P2A_PROGRAM)
+ }
+}
+
// Cannot derive due to generics.
impl<T> Default for ScriptBuf<T> {
fn default() -> Self { Self(PhantomData, Vec::new()) }
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.