Move script hexification functions to primitives
What changed, and why it matters
This is a routine internal code reorganization. It moves two helper functions that convert Bitcoin scripts to hexadecimal strings from the main `bitcoin` crate into the lower-level `primitives` crate. The functions still behave the same way; they are just located in a different module. There is no security fix or vulnerability here.
No security action required. Treat as normal refactoring. Reviewers may optionally follow up on the TODO about avoiding the extra allocation in `to_hex_string_prefixed`.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit relocates to_hex_string_prefixed and to_hex_string_no_length_prefix from bitcoin::blockdata::script::borrowed to primitives::script::borrowed. The implementations are updated to use primitives encoding/hex utilities instead of bitcoin::consensus::encode::serialize_hex. The old extension trait methods in bitcoin are removed, and callers now use the inherent methods on primitives::Script. A TODO notes a possible extra allocation in the new prefixed implementation. Tests remain in place to verify compatibility.
Changed components
bitcoin/src/blockdata/script/borrowed.rsprimitives/src/script/borrowed.rsprimitives/src/script/owned.rsbitcoin/examples/script.rsbitcoin/src/blockdata/script/tests.rsInspect captured patch +30 / −20
diff --git a/bitcoin/examples/script.rs b/bitcoin/examples/script.rs
index 1c48e292..c308ca0e 100644
--- a/bitcoin/examples/script.rs
+++ b/bitcoin/examples/script.rs
@@ -9,8 +9,7 @@
use bitcoin::consensus::encode;
use bitcoin::key::WPubkeyHash;
-use bitcoin::script::{self, ScriptExt as _};
-use bitcoin::WitnessScriptBuf;
+use bitcoin::{script, WitnessScriptBuf};
fn main() {
let pk = "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb".parse::<WPubkeyHash>().unwrap();
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index fbd2f65f..91ae73d4 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -2,7 +2,6 @@
use core::fmt;
-use hex_unstable::DisplayHex as _;
use internals::array::ArrayExt; // For `split_first`.
use internals::ToU64 as _;
@@ -12,7 +11,7 @@ use super::{
RedeemScriptSizeError, Script, ScriptHash, ScriptHashableTag, ScriptPubKey, ScriptSig,
TapScript, WScriptHash, WitnessScript, WitnessScriptSizeError,
};
-use crate::consensus::{self, Encodable};
+use crate::consensus::Encodable;
use crate::key::{PublicKey, UntweakedPublicKey, WPubkeyHash};
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
@@ -138,20 +137,6 @@ internal_macros::define_extension_trait! {
#[deprecated(since = "TBD", note = "use `to_hex_string_no_length_prefix` instead")]
fn to_hex_string(&self) -> String { self.to_hex_string_no_length_prefix() }
- /// Consensus encodes the script as lower-case hex.
- ///
- /// Consensus encoding includes a length prefix. To hex encode without the length prefix use
- /// `to_hex_string_no_length_prefix`.
- fn to_hex_string_prefixed(&self) -> String { consensus::encode::serialize_hex(self) }
-
- /// Encodes the script as lower-case hex.
- ///
- /// This is **not** consensus encoding. The returned hex string will not include the length
- /// prefix. See `to_hex_string_prefixed`.
- fn to_hex_string_no_length_prefix(&self) -> String {
- self.as_bytes().to_lower_hex_string()
- }
-
/// Returns the first opcode of the script (if there is any).
fn first_opcode(&self) -> Option<Opcode> {
self.as_bytes().first().copied().map(From::from)
diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs
index 860f0baa..954c6f97 100644
--- a/bitcoin/src/blockdata/script/tests.rs
+++ b/bitcoin/src/blockdata/script/tests.rs
@@ -1086,8 +1086,6 @@ fn hex() {
// touching the `bitcoin::consensus::encode` functions.
use alloc::format;
- use crate::blockdata::script::borrowed::ScriptExt as _;
-
let consensus = "04deadbeef";
let raw = "deadbeef";
diff --git a/primitives/src/script/borrowed.rs b/primitives/src/script/borrowed.rs
index a9456b1a..cab33adf 100644
--- a/primitives/src/script/borrowed.rs
+++ b/primitives/src/script/borrowed.rs
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: CC0-1.0
+#[cfg(all(feature = "hex", feature = "alloc"))]
+use alloc::string::String;
use core::marker::PhantomData;
use core::ops::{
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
@@ -119,6 +121,30 @@ impl<T> Script<T> {
#[deprecated(since = "0.101.0", note = "use to_vec instead")]
pub fn to_bytes(&self) -> Vec<u8> { self.to_vec() }
+ /// Consensus encodes the script as lower-case hex.
+ ///
+ /// Consensus encoding includes a length prefix. To hex encode without the length prefix use
+ /// `to_hex_string_no_length_prefix`.
+ #[cfg(all(feature = "hex", feature = "alloc"))]
+ pub fn to_hex_string_prefixed(&self) -> String {
+ use hex_unstable::{BytesToHexIter, Case};
+
+ // TODO: Can we remove allocation and use an iterator (like in `hex_codec`)?
+ let v = encoding::encode_to_vec(self);
+ BytesToHexIter::new(v.iter(), Case::Lower).collect()
+ }
+
+ /// Encodes the script as lower-case hex.
+ ///
+ /// This is **not** consensus encoding. The returned hex string will not include the length
+ /// prefix. See `to_hex_string_prefixed`.
+ #[cfg(all(feature = "hex", feature = "alloc"))]
+ pub fn to_hex_string_no_length_prefix(&self) -> String {
+ use hex_unstable::DisplayHex as _;
+
+ self.as_bytes().to_lower_hex_string()
+ }
+
/// Returns the length in bytes of the script.
#[inline]
pub const fn len(&self) -> usize { self.as_bytes().len() }
diff --git a/primitives/src/script/owned.rs b/primitives/src/script/owned.rs
index d7019d36..7b7b57de 100644
--- a/primitives/src/script/owned.rs
+++ b/primitives/src/script/owned.rs
@@ -8,6 +8,8 @@ use core::ops::{Deref, DerefMut};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use encoding::{ByteVecDecoder, ByteVecDecoderError, Decodable, Decoder};
+#[cfg(feature = "hex")]
+use crate::hex;
use internals::write_err;
use super::Script;
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.