What changed, and why it matters
This commit is a routine internal code reorganization. It moves the `WitnessVersion` type and its related error types from the main `bitcoin` crate into a lower-level `primitives` crate, then re-exports them so existing users still see the same public API. There is no functional change, no bug fix, and no security relevance.
No security action needed. Treat as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates WitnessVersion, its FromStr, TryFrom<u8>, TryFrom<Opcode>, From<WitnessVersion> for Opcode, and the FromStrError/TryFromError error types from bitcoin/src/blockdata/script/witness_version.rs to a new primitives/src/witness_version.rs. The original module now re-exports the moved items via pub use primitives::witness_version::* and keeps only the Instruction-specific conversion logic. A new OP_PUSHBYTES_0 constant is added in primitives/src/opcodes.rs to support the moved code. The public API surface remains unchanged.
Changed components
bitcoin/src/blockdata/script/witness_version.rsprimitives/src/lib.rsprimitives/src/opcodes.rsprimitives/src/witness_version.rsInspect captured patch +229 / −197
diff --git a/bitcoin/src/blockdata/script/witness_version.rs b/bitcoin/src/blockdata/script/witness_version.rs
index 6119c31c..b662fa74 100644
--- a/bitcoin/src/blockdata/script/witness_version.rs
+++ b/bitcoin/src/blockdata/script/witness_version.rs
@@ -7,128 +7,14 @@
//!
//! [BIP-0141]: <https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki>
-use core::fmt;
-use core::str::FromStr;
-
-use crate::opcodes::all::*;
-use crate::opcodes::Opcode;
-use crate::parse_int;
use crate::script::Instruction;
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(no_inline)]
pub use self::error::{FromStrError, TryFromInstructionError, TryFromError};
-/// Version of the segregated witness program.
-///
-/// Helps limit possible versions of the witness according to the specification. If a plain `u8`
-/// type was used instead it would mean that the version may be > 16, which would be incorrect.
-///
-/// First byte of `scriptPubkey` in transaction output for transactions starting with opcodes
-/// ranging from 0 to 16 (inclusive).
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
-#[repr(u8)]
-pub enum WitnessVersion {
- /// Initial version of witness program. Used for P2WPKH and P2WSH outputs
- V0 = 0,
- /// Version of witness program used for Taproot P2TR outputs.
- V1 = 1,
- /// Future (unsupported) version of witness program.
- V2 = 2,
- /// Future (unsupported) version of witness program.
- V3 = 3,
- /// Future (unsupported) version of witness program.
- V4 = 4,
- /// Future (unsupported) version of witness program.
- V5 = 5,
- /// Future (unsupported) version of witness program.
- V6 = 6,
- /// Future (unsupported) version of witness program.
- V7 = 7,
- /// Future (unsupported) version of witness program.
- V8 = 8,
- /// Future (unsupported) version of witness program.
- V9 = 9,
- /// Future (unsupported) version of witness program.
- V10 = 10,
- /// Future (unsupported) version of witness program.
- V11 = 11,
- /// Future (unsupported) version of witness program.
- V12 = 12,
- /// Future (unsupported) version of witness program.
- V13 = 13,
- /// Future (unsupported) version of witness program.
- V14 = 14,
- /// Future (unsupported) version of witness program.
- V15 = 15,
- /// Future (unsupported) version of witness program.
- V16 = 16,
-}
-
-impl WitnessVersion {
- /// Returns integer version number representation for a given [`WitnessVersion`] value.
- ///
- /// NB: this is not the same as an integer representation of the opcode signifying witness
- /// version in bitcoin script. Thus, there is no function to directly convert witness version
- /// into a byte since the conversion requires context (bitcoin script or just a version number).
- pub fn to_num(self) -> u8 { self as u8 }
-}
-
-/// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without any prefix or suffix.
-impl fmt::Display for WitnessVersion {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as u8) }
-}
-
-impl FromStr for WitnessVersion {
- type Err = FromStrError;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- let version: u8 = parse_int::int_from_str(s)?;
- Ok(Self::try_from(version)?)
- }
-}
-
-impl TryFrom<u8> for WitnessVersion {
- type Error = TryFromError;
-
- fn try_from(no: u8) -> Result<Self, Self::Error> {
- use WitnessVersion::*;
-
- Ok(match no {
- 0 => V0,
- 1 => V1,
- 2 => V2,
- 3 => V3,
- 4 => V4,
- 5 => V5,
- 6 => V6,
- 7 => V7,
- 8 => V8,
- 9 => V9,
- 10 => V10,
- 11 => V11,
- 12 => V12,
- 13 => V13,
- 14 => V14,
- 15 => V15,
- 16 => V16,
- invalid => return Err(TryFromError { invalid }),
- })
- }
-}
-
-impl TryFrom<Opcode> for WitnessVersion {
- type Error = TryFromError;
-
- fn try_from(opcode: Opcode) -> Result<Self, Self::Error> {
- match opcode.to_u8() {
- 0 => Ok(Self::V0),
- version if version >= OP_1.to_u8() && version <= OP_16.to_u8() =>
- Self::try_from(version - OP_1.to_u8() + 1),
- invalid => Err(TryFromError { invalid }),
- }
- }
-}
+#[doc(inline)]
+pub use primitives::witness_version::WitnessVersion;
impl TryFrom<Instruction<'_>> for WitnessVersion {
type Error = TryFromInstructionError;
@@ -142,15 +28,6 @@ impl TryFrom<Instruction<'_>> for WitnessVersion {
}
}
-impl From<WitnessVersion> for Opcode {
- fn from(version: WitnessVersion) -> Self {
- match version {
- WitnessVersion::V0 => OP_PUSHBYTES_0,
- no => Self::from(OP_1.to_u8() + no.to_num() - 1),
- }
- }
-}
-
/// Error types for the segwit version number.
pub mod error {
use core::convert::Infallible;
@@ -158,50 +35,9 @@ pub mod error {
use internals::write_err;
- use crate::parse_int::ParseIntError;
-
- /// Error parsing [`WitnessVersion`] from a string.
- ///
- /// [`WitnessVersion`]: super::WitnessVersion
- #[derive(Clone, Debug, PartialEq, Eq)]
- #[non_exhaustive]
- pub enum FromStrError {
- /// Unable to parse integer from string.
- Unparsable(ParseIntError),
- /// String contained an invalid witness version number.
- Invalid(TryFromError),
- }
-
- impl From<Infallible> for FromStrError {
- fn from(never: Infallible) -> Self { match never {} }
- }
-
- impl fmt::Display for FromStrError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Unparsable(ref e) => write_err!(f, "integer parse error"; e),
- Self::Invalid(ref e) => write_err!(f, "invalid version number"; e),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for FromStrError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Unparsable(ref e) => Some(e),
- Self::Invalid(ref e) => Some(e),
- }
- }
- }
-
- impl From<ParseIntError> for FromStrError {
- fn from(e: ParseIntError) -> Self { Self::Unparsable(e) }
- }
-
- impl From<TryFromError> for FromStrError {
- fn from(e: TryFromError) -> Self { Self::Invalid(e) }
- }
+ #[rustfmt::skip] // Keep public re-exports separate.
+ #[doc(no_inline)]
+ pub use primitives::witness_version::error::{FromStrError, TryFromError};
/// Error attempting to create a [`WitnessVersion`] from an [`Instruction`]
///
@@ -243,32 +79,4 @@ pub mod error {
impl From<TryFromError> for TryFromInstructionError {
fn from(e: TryFromError) -> Self { Self::TryFrom(e) }
}
-
- /// Error attempting to create a [`WitnessVersion`] from an integer.
- ///
- /// [`WitnessVersion`]: super::WitnessVersion
- #[derive(Clone, Debug, PartialEq, Eq)]
- pub struct TryFromError {
- /// The invalid non-witness version integer.
- pub(super) invalid: u8,
- }
-
- impl TryFromError {
- /// Returns the invalid non-witness version integer.
- pub fn invalid_version(&self) -> u8 { self.invalid }
- }
-
- impl fmt::Display for TryFromError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "invalid witness script version: {}", self.invalid)
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for TryFromError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- let Self { invalid: _ } = self;
- None
- }
- }
}
diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs
index 49c2fc5f..f901db84 100644
--- a/primitives/src/lib.rs
+++ b/primitives/src/lib.rs
@@ -59,6 +59,7 @@ pub mod serde_as_consensus;
pub mod transaction;
#[cfg(feature = "alloc")]
pub mod witness;
+pub mod witness_version;
#[cfg(feature = "hex")]
mod hex_codec;
diff --git a/primitives/src/opcodes.rs b/primitives/src/opcodes.rs
index 4f676804..06b88a82 100644
--- a/primitives/src/opcodes.rs
+++ b/primitives/src/opcodes.rs
@@ -163,6 +163,9 @@ pub(crate) const OP_PUSHDATA2: u8 = 0x4d;
#[cfg(feature = "alloc")]
pub(crate) const OP_PUSHDATA4: u8 = 0x4e;
+/// Push an empty array onto the stack.
+pub(crate) const OP_PUSHBYTES_0: Opcode = Opcode::from_u8(0x00);
+
/// Format a byte as a script opcode.
#[cfg(feature = "alloc")]
pub(crate) fn fmt_opcode(op: u8, f: &mut fmt::Formatter) -> fmt::Result {
diff --git a/primitives/src/witness_version.rs b/primitives/src/witness_version.rs
new file mode 100644
index 00000000..44a748b6
--- /dev/null
+++ b/primitives/src/witness_version.rs
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! The segregated witness version byte as defined by [BIP-0141].
+//!
+//! > A scriptPubKey (or redeemScript as defined in BIP-0016/P2SH) that consists of a 1-byte push
+//! > opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new special
+//! > meaning. The value of the first push is called the "version byte". The following byte
+//! > vector pushed is called the "witness program".
+//!
+//! [BIP-0141]: <https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki>
+
+use core::fmt;
+use core::str::FromStr;
+
+use units::parse_int;
+
+use crate::opcodes::all::{OP_1, OP_16};
+use crate::opcodes::{Opcode, OP_PUSHBYTES_0};
+
+#[rustfmt::skip] // Keep public re-exports separate.
+#[doc(no_inline)]
+pub use self::error::{FromStrError, TryFromError};
+
+/// Version of the segregated witness program.
+///
+/// Helps limit possible versions of the witness according to the specification. If a plain `u8`
+/// type was used instead it would mean that the version may be > 16, which would be incorrect.
+///
+/// First byte of `scriptPubkey` in transaction output for transactions starting with opcodes
+/// ranging from 0 to 16 (inclusive).
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[repr(u8)]
+pub enum WitnessVersion {
+ /// Initial version of witness program. Used for P2WPKH and P2WSH outputs
+ V0 = 0,
+ /// Version of witness program used for Taproot P2TR outputs.
+ V1 = 1,
+ /// Future (unsupported) version of witness program.
+ V2 = 2,
+ /// Future (unsupported) version of witness program.
+ V3 = 3,
+ /// Future (unsupported) version of witness program.
+ V4 = 4,
+ /// Future (unsupported) version of witness program.
+ V5 = 5,
+ /// Future (unsupported) version of witness program.
+ V6 = 6,
+ /// Future (unsupported) version of witness program.
+ V7 = 7,
+ /// Future (unsupported) version of witness program.
+ V8 = 8,
+ /// Future (unsupported) version of witness program.
+ V9 = 9,
+ /// Future (unsupported) version of witness program.
+ V10 = 10,
+ /// Future (unsupported) version of witness program.
+ V11 = 11,
+ /// Future (unsupported) version of witness program.
+ V12 = 12,
+ /// Future (unsupported) version of witness program.
+ V13 = 13,
+ /// Future (unsupported) version of witness program.
+ V14 = 14,
+ /// Future (unsupported) version of witness program.
+ V15 = 15,
+ /// Future (unsupported) version of witness program.
+ V16 = 16,
+}
+
+impl WitnessVersion {
+ /// Returns integer version number representation for a given [`WitnessVersion`] value.
+ ///
+ /// NB: this is not the same as an integer representation of the opcode signifying witness
+ /// version in bitcoin script. Thus, there is no function to directly convert witness version
+ /// into a byte since the conversion requires context (bitcoin script or just a version number).
+ pub fn to_num(self) -> u8 { self as u8 }
+}
+
+/// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without any prefix or suffix.
+impl fmt::Display for WitnessVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as u8) }
+}
+
+impl FromStr for WitnessVersion {
+ type Err = FromStrError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let version: u8 = parse_int::int_from_str(s)?;
+ Ok(Self::try_from(version)?)
+ }
+}
+
+impl TryFrom<u8> for WitnessVersion {
+ type Error = TryFromError;
+
+ fn try_from(no: u8) -> Result<Self, Self::Error> {
+ Ok(match no {
+ 0 => Self::V0,
+ 1 => Self::V1,
+ 2 => Self::V2,
+ 3 => Self::V3,
+ 4 => Self::V4,
+ 5 => Self::V5,
+ 6 => Self::V6,
+ 7 => Self::V7,
+ 8 => Self::V8,
+ 9 => Self::V9,
+ 10 => Self::V10,
+ 11 => Self::V11,
+ 12 => Self::V12,
+ 13 => Self::V13,
+ 14 => Self::V14,
+ 15 => Self::V15,
+ 16 => Self::V16,
+ invalid => return Err(TryFromError { invalid }),
+ })
+ }
+}
+
+impl TryFrom<Opcode> for WitnessVersion {
+ type Error = TryFromError;
+
+ fn try_from(opcode: Opcode) -> Result<Self, Self::Error> {
+ match opcode.to_u8() {
+ 0 => Ok(Self::V0),
+ version if version >= OP_1.to_u8() && version <= OP_16.to_u8() =>
+ Self::try_from(version - OP_1.to_u8() + 1),
+ invalid => Err(TryFromError { invalid }),
+ }
+ }
+}
+
+impl From<WitnessVersion> for Opcode {
+ fn from(version: WitnessVersion) -> Self {
+ match version {
+ WitnessVersion::V0 => OP_PUSHBYTES_0,
+ no => Self::from(OP_1.to_u8() + no.to_num() - 1),
+ }
+ }
+}
+
+/// Error types for the segwit version number.
+pub mod error {
+ use core::convert::Infallible;
+ use core::fmt;
+
+ use internals::write_err;
+ use units::parse_int::ParseIntError;
+
+ /// Error parsing [`WitnessVersion`] from a string.
+ ///
+ /// [`WitnessVersion`]: super::WitnessVersion
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ #[non_exhaustive]
+ pub enum FromStrError {
+ /// Unable to parse integer from string.
+ Unparsable(ParseIntError),
+ /// String contained an invalid witness version number.
+ Invalid(TryFromError),
+ }
+
+ impl From<Infallible> for FromStrError {
+ fn from(never: Infallible) -> Self { match never {} }
+ }
+
+ impl fmt::Display for FromStrError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Unparsable(ref e) => write_err!(f, "integer parse error"; e),
+ Self::Invalid(ref e) => write_err!(f, "invalid version number"; e),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for FromStrError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::Unparsable(ref e) => Some(e),
+ Self::Invalid(ref e) => Some(e),
+ }
+ }
+ }
+
+ impl From<ParseIntError> for FromStrError {
+ fn from(e: ParseIntError) -> Self { Self::Unparsable(e) }
+ }
+
+ impl From<TryFromError> for FromStrError {
+ fn from(e: TryFromError) -> Self { Self::Invalid(e) }
+ }
+
+ /// Error attempting to create a [`WitnessVersion`] from an integer.
+ ///
+ /// [`WitnessVersion`]: super::WitnessVersion
+ #[derive(Clone, Debug, PartialEq, Eq)]
+ pub struct TryFromError {
+ /// The invalid non-witness version integer.
+ pub(super) invalid: u8,
+ }
+
+ impl TryFromError {
+ /// Returns the invalid non-witness version integer.
+ pub fn invalid_version(&self) -> u8 { self.invalid }
+ }
+
+ impl fmt::Display for TryFromError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "invalid witness script version: {}", self.invalid)
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for TryFromError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ let Self { invalid: _ } = self;
+ None
+ }
+ }
+}
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.