What changed, and why it matters
This commit is a routine code reorganization. It moves the `Builder` type (used to construct Bitcoin scripts step by step) from the main `bitcoin` crate into the lower-level `primitives` crate, then re-exports it so existing users can still use it the same way. No behavior changes are visible in the diff.
No security action required. Treat as a normal refactoring/reorganization change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates Builder<T> and its Default, From<Vec<u8>>, Display, and Debug implementations from bitcoin/src/blockdata/script/builder.rs to a new file primitives/src/script/builder.rs. The original file now publicly re-exports primitives::script::Builder. The BuilderExt trait and sealed helper remain in the bitcoin crate. Imports are adjusted to use primitives equivalents (Opcode, PushBytes, Script, ScriptBuf, Vec).
Changed components
bitcoin/src/blockdata/script/builder.rsprimitives/src/script/builder.rsprimitives/src/script/mod.rsInspect captured patch +86 / −76
diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs
index 55da4b7d..14095176 100644
--- a/bitcoin/src/blockdata/script/builder.rs
+++ b/bitcoin/src/blockdata/script/builder.rs
@@ -1,68 +1,16 @@
// SPDX-License-Identifier: CC0-1.0
-use core::fmt;
-
-use super::{opcode_to_verify, Error, PushBytes, Script, ScriptBuf};
+use super::{opcode_to_verify, Error};
use crate::key::{LegacyPublicKey, XOnlyPublicKey};
use crate::locktime::absolute;
use crate::opcodes::all::*;
-use crate::opcodes::Opcode;
use crate::prelude::Vec;
use crate::script::{ScriptBufExt as _, ScriptBufExtPriv as _, ScriptExtPriv as _};
use crate::{relative, Sequence};
-/// An Object which can be used to construct a script piece by piece.
-///
-/// # Panics
-///
-/// `Builder` is backed by [`ScriptBuf`] and inherits its panic behavior. This means that
-/// attempting to construct scripts larger than `isize::MAX` bytes will panic.
-#[derive(PartialEq, Eq, Clone)]
-pub struct Builder<T>(ScriptBuf<T>);
-
-impl<T> Builder<T> {
- /// Constructs a new empty script.
- #[inline]
- pub const fn new() -> Self { Self(ScriptBuf::new()) }
-
- /// Adds instructions to push some arbitrary data onto the stack.
- ///
- /// If the data can be exactly produced by a numeric opcode, that opcode
- /// will be used, since its behavior is equivalent but will not violate minimality
- /// rules. To avoid this, use [`Builder::push_slice_non_minimal`] which will always
- /// use a push opcode.
- ///
- /// However, this method does *not* enforce any numeric minimality rules.
- /// If your pushes should be interpreted as numbers, ensure your input does
- /// not have any leading zeros. In particular, the number 0 should be encoded
- /// as an empty string rather than as a single 0 byte.
- pub fn push_slice<D: AsRef<PushBytes>>(mut self, data: D) -> Self {
- self.0.push_slice(data);
- self
- }
-
- /// Adds instructions to push some arbitrary data onto the stack without minimality.
- ///
- /// Standardness rules require push minimality according to [CheckMinimalPush] of core.
- ///
- /// [CheckMinimalPush]: <https://github.com/bitcoin/bitcoin/blob/99a4ddf5ab1b3e514d08b90ad8565827fda7b63b/src/script/script.cpp#L366>
- pub fn push_slice_non_minimal<D: AsRef<PushBytes>>(mut self, data: D) -> Self {
- self.0.push_slice_non_minimal(data);
- self
- }
-
- /// Adds a single opcode to the script.
- pub fn push_opcode(mut self, data: Opcode) -> Self {
- self.0.push_opcode(data);
- self
- }
-
- /// Converts the `Builder` into `ScriptBuf`.
- pub fn into_script(self) -> ScriptBuf<T> { self.0 }
-
- /// Returns the internal script
- pub fn as_script(&self) -> &Script<T> { &self.0 }
-}
+#[rustfmt::skip] // Keep public re-exports separate.
+#[doc(inline)]
+pub use primitives::script::Builder;
mod sealed {
pub trait Sealed {}
@@ -204,23 +152,3 @@ crate::internal_macros::define_extension_trait! {
}
}
}
-
-impl<T> Default for Builder<T> {
- fn default() -> Self { Self::new() }
-}
-
-/// Constructs a new builder from an existing vector.
-impl<T> From<Vec<u8>> for Builder<T> {
- fn from(v: Vec<u8>) -> Self {
- let script = ScriptBuf::from(v);
- Self(script)
- }
-}
-
-impl<T> fmt::Display for Builder<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
-}
-
-impl<T> fmt::Debug for Builder<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }
-}
diff --git a/primitives/src/script/builder.rs b/primitives/src/script/builder.rs
new file mode 100644
index 00000000..8703b9e6
--- /dev/null
+++ b/primitives/src/script/builder.rs
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: CC0-1.0
+
+use core::fmt;
+
+use super::{PushBytes, Script, ScriptBuf};
+use crate::opcodes::Opcode;
+use crate::prelude::Vec;
+
+/// An Object which can be used to construct a script piece by piece.
+///
+/// # Panics
+///
+/// `Builder` is backed by [`ScriptBuf`] and inherits its panic behavior. This means that
+/// attempting to construct scripts larger than `isize::MAX` bytes will panic.
+#[derive(PartialEq, Eq, Clone)]
+pub struct Builder<T>(ScriptBuf<T>);
+
+impl<T> Builder<T> {
+ /// Constructs a new empty script.
+ #[inline]
+ pub const fn new() -> Self { Self(ScriptBuf::new()) }
+
+ /// Adds instructions to push some arbitrary data onto the stack.
+ ///
+ /// If the data can be exactly produced by a numeric opcode, that opcode
+ /// will be used, since its behavior is equivalent but will not violate minimality
+ /// rules. To avoid this, use [`Builder::push_slice_non_minimal`] which will always
+ /// use a push opcode.
+ ///
+ /// However, this method does *not* enforce any numeric minimality rules.
+ /// If your pushes should be interpreted as numbers, ensure your input does
+ /// not have any leading zeros. In particular, the number 0 should be encoded
+ /// as an empty string rather than as a single 0 byte.
+ pub fn push_slice<D: AsRef<PushBytes>>(mut self, data: D) -> Self {
+ self.0.push_slice(data);
+ self
+ }
+
+ /// Adds instructions to push some arbitrary data onto the stack without minimality.
+ ///
+ /// Standardness rules require push minimality according to [CheckMinimalPush] of core.
+ ///
+ /// [CheckMinimalPush]: <https://github.com/bitcoin/bitcoin/blob/99a4ddf5ab1b3e514d08b90ad8565827fda7b63b/src/script/script.cpp#L366>
+ pub fn push_slice_non_minimal<D: AsRef<PushBytes>>(mut self, data: D) -> Self {
+ self.0.push_slice_non_minimal(data);
+ self
+ }
+
+ /// Adds a single opcode to the script.
+ pub fn push_opcode(mut self, data: Opcode) -> Self {
+ self.0.push_opcode(data);
+ self
+ }
+
+ /// Converts the `Builder` into `ScriptBuf`.
+ pub fn into_script(self) -> ScriptBuf<T> { self.0 }
+
+ /// Returns the internal script
+ pub fn as_script(&self) -> &Script<T> { &self.0 }
+}
+
+impl<T> Default for Builder<T> {
+ fn default() -> Self { Self::new() }
+}
+
+/// Constructs a new builder from an existing vector.
+impl<T> From<Vec<u8>> for Builder<T> {
+ fn from(v: Vec<u8>) -> Self {
+ let script = ScriptBuf::from(v);
+ Self(script)
+ }
+}
+
+impl<T> fmt::Display for Builder<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
+}
+
+impl<T> fmt::Debug for Builder<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }
+}
diff --git a/primitives/src/script/mod.rs b/primitives/src/script/mod.rs
index e84f4390..89912c2a 100644
--- a/primitives/src/script/mod.rs
+++ b/primitives/src/script/mod.rs
@@ -3,6 +3,7 @@
//! Bitcoin scripts.
mod borrowed;
+mod builder;
mod owned;
mod push_bytes;
mod tag;
@@ -29,6 +30,7 @@ use crate::prelude::{Borrow, BorrowMut, Box, Cow, ToOwned, Vec};
#[doc(inline)]
pub use self::{
borrowed::{Script, ScriptEncoder},
+ builder::Builder,
owned::{ScriptBuf, ScriptBufDecoder},
push_bytes::{PushBytes, PushBytesBuf, PushBytesErrorReport},
tag::{Tag, RedeemScriptTag, ScriptPubKeyTag, ScriptSigTag, SignetBlockScriptTag, TapScriptTag, WitnessScriptTag},
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.