bitcoin: move hex and PSBT decoding from ScriptBufExt to GenericScriptBufExt
What changed, and why it matters
This is a routine internal code reorganization in the rust-bitcoin library. It moves hex decoding and PSBT serialization helpers from a trait that only applied to the standard script buffer into a more generic trait so the same helpers work with all script buffer types. There is no security fix or behavior change visible in the diff.
No security action required. Treat as a normal API refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors ScriptBufExt methods (from_hex, from_hex_prefixed, from_hex_no_length_prefix) into GenericScriptBufExt, and makes Encodable/Decodable/Serialize/Deserialize implementations generic over GenericScript<T> / GenericScriptBuf<T> instead of hard-coding Script/ScriptBuf. Call sites are updated to import GenericScriptBufExt. The logic of each function is preserved verbatim; only the trait bounds and import paths change.
Changed components
bitcoin/src/blockdata/script/owned.rsbitcoin/src/blockdata/script/mod.rsbitcoin/src/psbt/serialize.rsInspect captured patch +50 / −40
diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs
index 930b4899..e23b5f40 100644
--- a/bitcoin/examples/script.rs
+++ b/bitcoin/examples/script.rs
@@ -9,7 +9,7 @@
use bitcoin::consensus::encode;
use bitcoin::key::WPubkeyHash;
-use bitcoin::script::{self, ScriptBufExt, ScriptExt};
+use bitcoin::script::{self, GenericScriptBufExt, ScriptExt};
use bitcoin::ScriptBuf;
fn main() {
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index 3972d9f5..76b92eb2 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -1020,6 +1020,7 @@ mod tests {
use super::*;
use crate::network::Network::{Bitcoin, Testnet};
use crate::network::{params, TestnetVersion};
+ use crate::script::GenericScriptBufExt as _;
fn roundtrips(addr: &Address, network: Network) {
assert_eq!(
diff --git a/bitcoin/src/blockdata/script/mod.rs b/bitcoin/src/blockdata/script/mod.rs
index 09ee1dab..2e4319df 100644
--- a/bitcoin/src/blockdata/script/mod.rs
+++ b/bitcoin/src/blockdata/script/mod.rs
@@ -209,27 +209,27 @@ pub(crate) fn new_witness_program_unchecked<T: AsRef<PushBytes>>(
Builder::new().push_opcode(version.into()).push_slice(program).into_script()
}
-impl Encodable for Script {
+impl<T> Encodable for GenericScript<T> {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
crate::consensus::encode::consensus_encode_with_size(self.as_bytes(), w)
}
}
-impl Encodable for ScriptBuf {
+impl<T> Encodable for GenericScriptBuf<T> {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.as_script().consensus_encode(w)
}
}
-impl Decodable for ScriptBuf {
+impl<T> Decodable for GenericScriptBuf<T> {
#[inline]
fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
r: &mut R,
) -> Result<Self, encode::Error> {
let v: Vec<u8> = Decodable::consensus_decode_from_finite_reader(r)?;
- Ok(ScriptBuf::from_bytes(v))
+ Ok(Self::from_bytes(v))
}
}
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index fe5b1340..81610337 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -94,6 +94,34 @@ internal_macros::define_extension_trait! {
/// `Builder` if you're creating the script from scratch or if you want to push `OP_VERIFY`
/// multiple times.
fn scan_and_push_verify(&mut self) { self.push_verify(self.last_opcode()); }
+
+ /// Constructs a new [`ScriptBuf`] from a hex string.
+ ///
+ /// The input string is expected to be consensus encoded i.e., includes the length prefix.
+ fn from_hex_prefixed(s: &str) -> Result<Self, consensus::FromHexError>
+ where Self: Sized
+ {
+ consensus::encode::deserialize_hex(s)
+ }
+
+ /// Constructs a new [`ScriptBuf`] from a hex string.
+ #[deprecated(since = "TBD", note = "use `from_hex_string_no_length_prefix()` instead")]
+ fn from_hex(s: &str) -> Result<Self, hex::HexToBytesError>
+ where Self: Sized
+ {
+ Self::from_hex_no_length_prefix(s)
+ }
+
+ /// Constructs a new [`ScriptBuf`] from a hex string.
+ ///
+ /// This is **not** consensus encoding. If your hex string is a consensus encoded script
+ /// then use `ScriptBuf::from_hex_prefixed`.
+ fn from_hex_no_length_prefix(s: &str) -> Result<Self, hex::HexToBytesError>
+ where Self: Sized
+ {
+ let v = Vec::from_hex(s)?;
+ Ok(Self::from_bytes(v))
+ }
}
}
@@ -173,28 +201,6 @@ crate::internal_macros::define_extension_trait! {
.push_slice(witness_program.program())
.into_script()
}
-
- /// Constructs a new [`ScriptBuf`] from a hex string.
- ///
- /// The input string is expected to be consensus encoded i.e., includes the length prefix.
- fn from_hex_prefixed(s: &str) -> Result<ScriptBuf, consensus::FromHexError> {
- consensus::encode::deserialize_hex(s)
- }
-
- /// Constructs a new [`ScriptBuf`] from a hex string.
- #[deprecated(since = "TBD", note = "use `from_hex_string_no_length_prefix()` instead")]
- fn from_hex(s: &str) -> Result<ScriptBuf, hex::HexToBytesError> {
- Self::from_hex_no_length_prefix(s)
- }
-
- /// Constructs a new [`ScriptBuf`] from a hex string.
- ///
- /// This is **not** consensus encoding. If your hex string is a consensus encoded script
- /// then use `ScriptBuf::from_hex_prefixed`.
- fn from_hex_no_length_prefix(s: &str) -> Result<ScriptBuf, hex::HexToBytesError> {
- let v = Vec::from_hex(s)?;
- Ok(ScriptBuf::from_bytes(v))
- }
}
}
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index a929c77c..6cecddcb 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -1537,7 +1537,7 @@ mod tests {
use super::*;
use crate::consensus::deserialize;
use crate::locktime::absolute;
- use crate::script::{ScriptBuf, ScriptBufExt as _};
+ use crate::script::{GenericScriptBufExt as _, ScriptBuf};
use crate::TxIn;
extern crate serde_json;
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index d23a2da3..b67803ab 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -1332,6 +1332,7 @@ mod tests {
use {
crate::bip32::Fingerprint,
crate::locktime,
+ crate::script::ScriptBufExt as _,
crate::witness_version::WitnessVersion,
crate::WitnessProgram,
secp256k1::{All, SecretKey},
@@ -1342,7 +1343,7 @@ mod tests {
use crate::locktime::absolute;
use crate::network::NetworkKind;
use crate::psbt::serialize::{Deserialize, Serialize};
- use crate::script::{ScriptBuf, ScriptBufExt as _};
+ use crate::script::{GenericScriptBufExt as _, ScriptBuf};
use crate::transaction::{self, OutPoint, TxIn};
use crate::witness::Witness;
use crate::Sequence;
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index f1c89ad1..1d3141b0 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -18,7 +18,7 @@ use crate::crypto::{ecdsa, taproot};
use crate::io::Write;
use crate::prelude::{DisplayHex, String, Vec};
use crate::psbt::{Error, Psbt};
-use crate::script::ScriptBuf;
+use crate::script::GenericScriptBuf;
use crate::taproot::{
ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTree, TaprootBuilder,
};
@@ -152,11 +152,11 @@ impl_psbt_hash_de_serialize!(sha256d::Hash);
// Taproot
impl_psbt_de_serialize!(Vec<TapLeafHash>);
-impl Serialize for ScriptBuf {
+impl<T> Serialize for GenericScriptBuf<T> {
fn serialize(&self) -> Vec<u8> { self.to_vec() }
}
-impl Deserialize for ScriptBuf {
+impl<T> Deserialize for GenericScriptBuf<T> {
fn deserialize(bytes: &[u8]) -> Result<Self, Error> { Ok(Self::from(bytes.to_vec())) }
}
@@ -346,7 +346,7 @@ impl Deserialize for ControlBlock {
}
// Versioned ScriptBuf
-impl Serialize for (ScriptBuf, LeafVersion) {
+impl<T> Serialize for (GenericScriptBuf<T>, LeafVersion) {
fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.0.len() + 1);
buf.extend(self.0.as_bytes());
@@ -355,13 +355,13 @@ impl Serialize for (ScriptBuf, LeafVersion) {
}
}
-impl Deserialize for (ScriptBuf, LeafVersion) {
+impl<T> Deserialize for (GenericScriptBuf<T>, LeafVersion) {
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
if bytes.is_empty() {
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into());
}
// The last byte is LeafVersion.
- let script = ScriptBuf::deserialize(&bytes[..bytes.len() - 1])?;
+ let script = GenericScriptBuf::deserialize(&bytes[..bytes.len() - 1])?;
let leaf_ver = LeafVersion::from_consensus(bytes[bytes.len() - 1])
.map_err(|_| Error::InvalidLeafVersion)?;
Ok((script, leaf_ver))
@@ -415,7 +415,8 @@ impl Deserialize for TapTree {
let mut bytes_iter = bytes.iter();
while let Some(depth) = bytes_iter.next() {
let version = bytes_iter.next().ok_or(Error::Taproot("invalid Taproot Builder"))?;
- let (script, consumed) = deserialize_partial::<ScriptBuf>(bytes_iter.as_slice())?;
+ let (script, consumed) =
+ deserialize_partial::<GenericScriptBuf<_>>(bytes_iter.as_slice())?;
if consumed > 0 {
bytes_iter.nth(consumed - 1);
}
@@ -435,7 +436,8 @@ fn key_source_len(key_source: &KeySource) -> usize { 4 + 4 * (key_source.1).as_r
#[cfg(test)]
mod tests {
use super::*;
- use crate::script::ScriptBufExt as _;
+ use crate::script::GenericScriptBufExt as _;
+ use crate::ScriptBuf;
// Composes tree matching a given depth map, filled with dumb script leafs,
// each of which consists of a single push-int op code, with int value
diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index 4036189c..1e622200 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -1693,7 +1693,7 @@ mod test {
use secp256k1::VerifyOnly;
use super::*;
- use crate::script::ScriptBufExt as _;
+ use crate::script::GenericScriptBufExt as _;
use crate::sighash::TapSighashTag;
use crate::{Address, KnownHrp};
extern crate serde_json;
diff --git a/bitcoin/tests/bip_174.rs b/bitcoin/tests/bip_174.rs
index 16931b44..5f3ae395 100644
--- a/bitcoin/tests/bip_174.rs
+++ b/bitcoin/tests/bip_174.rs
@@ -9,7 +9,7 @@ use bitcoin::consensus::encode::{deserialize, serialize_hex};
use bitcoin::hex::FromHex;
use bitcoin::opcodes::OP_0;
use bitcoin::psbt::{Psbt, PsbtSighashType};
-use bitcoin::script::{PushBytes, ScriptBufExt as _};
+use bitcoin::script::{GenericScriptBufExt as _, PushBytes};
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{
absolute, script, transaction, NetworkKind, OutPoint, PrivateKey, PublicKey, ScriptBuf,
diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs
index 34467f86..1ad44ae9 100644
--- a/bitcoin/tests/serde.rs
+++ b/bitcoin/tests/serde.rs
@@ -27,7 +27,7 @@ use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
use bitcoin::hex::FromHex;
use bitcoin::locktime::{absolute, relative};
use bitcoin::psbt::{raw, Input, Output, Psbt, PsbtSighashType};
-use bitcoin::script::ScriptBufExt as _;
+use bitcoin::script::GenericScriptBufExt as _;
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder};
use bitcoin::witness::Witness;
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.