bitcoin: Replace script hex decoding with new consensus crate
What changed, and why it matters
This commit swaps out the old way of turning hex strings into Bitcoin scripts for a newer, more stable set of libraries. It also introduces a clearer error type so callers can tell whether something went wrong with the hex string itself or with decoding the script. The change is described by the project as a hardening/cleanup move, not a fix for a known vulnerability.
Treat as routine maintenance/hardening. Review the new error handling paths and ensure downstream consumers handle `FromHexError` correctly. No immediate security response is indicated by the commit itself.
Security signals we found
Refactoring of parsing/deserialization code
Introduction of a dedicated error type for hex parsing failures
Use of newer stable crates for hex and consensus decoding
Commit message uses hardening language ('shore up') but does not describe a vulnerability
Evidence from the diff
The patch replaces consensus::encode::deserialize_hex in ScriptBuf::from_hex_prefixed with hex::decode_to_vec plus encoding::decode_from_slice. It adds a new public FromHexError enum wrapping hex::DecodeVariableLengthBytesError and encoding::DecodeError<ScriptBufDecoderError>, with Display, std::error::Error, and From implementations. The commit message frames this as using stable crates to ‘shore up’ script hex parsing.
Changed components
bitcoin/src/blockdata/script/owned.rsScriptBuf::from_hex_prefixedFromHexErrorInspect captured patch +49 / −5
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index eb0fca18..d22bbd60 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: CC0-1.0
+use core::convert::Infallible;
#[cfg(doc)]
use core::ops::Deref;
+use core::fmt;
-use internals::ToU64 as _;
+use internals::{write_err, ToU64 as _};
use super::{
opcode_to_verify, write_scriptint, Builder, Error, Instruction, PushBytes, ScriptBuf,
@@ -18,9 +20,9 @@ use crate::opcodes::{self, Opcode};
use crate::prelude::Vec;
use crate::script::witness_program::{WitnessProgram, P2A_PROGRAM};
use crate::script::witness_version::WitnessVersion;
-use crate::script::{self, ScriptHash, WScriptHash};
+use crate::script::{self, ScriptBufDecoderError, ScriptHash, WScriptHash};
use crate::taproot::TapNodeHash;
-use crate::{consensus, internal_macros};
+use crate::internal_macros;
internal_macros::define_extension_trait! {
/// Extension functionality for the [`ScriptBuf`] type.
@@ -148,10 +150,11 @@ internal_macros::define_extension_trait! {
/// 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>
+ fn from_hex_prefixed(s: &str) -> Result<Self, FromHexError>
where Self: Sized
{
- consensus::encode::deserialize_hex(s)
+ let v = hex::decode_to_vec(s)?;
+ Ok(encoding::decode_from_slice(&v)?)
}
/// Constructs a new [`ScriptBuf`] from a hex string.
@@ -407,3 +410,44 @@ impl<T> Drop for ScriptBufAsVec<'_, T> {
*(self.0) = ScriptBuf::from_bytes(vec);
}
}
+
+/// An error parsing a script from hex.
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum FromHexError {
+ /// Error parsing the hex input string.
+ Hex(hex::DecodeVariableLengthBytesError),
+ /// Error when decoding the script.
+ Decoder(encoding::DecodeError<ScriptBufDecoderError>),
+}
+
+impl From<Infallible> for FromHexError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for FromHexError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Self::Hex(ref e) => write_err!(f, "script hex"; e),
+ Self::Decoder(ref e) => write_err!(f, "script decoder"; e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for FromHexError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match *self {
+ Self::Hex(ref e) => Some(e),
+ Self::Decoder(ref e) => Some(e),
+ }
+ }
+}
+
+impl From<hex::DecodeVariableLengthBytesError> for FromHexError {
+ fn from(e: hex::DecodeVariableLengthBytesError) -> Self { Self::Hex(e) }
+}
+
+impl From<encoding::DecodeError<ScriptBufDecoderError>> for FromHexError {
+ fn from(e: encoding::DecodeError<ScriptBufDecoderError>) -> Self { Self::Decoder(e) }
+}
Why this scored 27/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.