Add hex string parsing for taproot::Signature
What changed, and why it matters
This commit adds a new feature: the ability to parse a Taproot cryptographic signature from a hexadecimal text string. Previously only ECDSA signatures could be parsed this way. The change is purely additive and does not fix any known bug or vulnerability. It simply exposes a new parsing function to users of the library.
No security action required. Review as a normal feature addition; ensure the new error type and FromStr implementation meet project API conventions.
Security signals we found
No security-relevant signals present in the commit or supplied references.
Change is a feature addition for API parity with ecdsa::Signature.
Evidence from the diff
The patch implements FromStr for taproot::Signature in bitcoin/src/crypto/taproot.rs. It decodes an input string as hex bytes via hex::decode_to_vec, then deserializes the bytes with the existing Signature::from_slice. A new public error enum ParseSignatureError wraps hex-decoding and slice-decoding failures. No existing behavior is modified; no unsafe code, memory handling, or cryptographic validation logic is changed beyond reusing the already-present from_slice decoder.
Changed components
bitcoin/src/crypto/taproot.rsInspect captured patch +46 / −0
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index 19623e26..77ac2bef 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -8,6 +8,7 @@ use core::borrow::Borrow;
use core::convert::Infallible;
use core::fmt;
use core::ops::Deref;
+use core::str::FromStr;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
@@ -16,6 +17,7 @@ use internals::{impl_to_hex_from_lower_hex, write_err};
use io::Write;
pub use self::into_iter::IntoIter;
+use crate::hex;
use crate::prelude::{DisplayHex, Vec};
use crate::sighash::{InvalidSighashTypeError, TapSighashType};
@@ -91,6 +93,17 @@ impl Signature {
}
}
+impl FromStr for Signature {
+ type Err = ParseSignatureError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let bytes = hex::decode_to_vec(s)
+ .map_err(ParseSignatureError::Hex)?;
+ Self::from_slice(&bytes)
+ .map_err(ParseSignatureError::Decode)
+ }
+}
+
/// A serialized Taproot Signature
///
/// Serialized Taproot signatures have the issue that they can have different lengths.
@@ -406,6 +419,39 @@ impl From<InvalidSighashTypeError> for SigFromSliceError {
fn from(err: InvalidSighashTypeError) -> Self { Self::SighashType(err) }
}
+/// Error encountered while parsing a Taproot signature from a string.
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum ParseSignatureError {
+ /// Hex string decoding error.
+ Hex(hex::DecodeVariableLengthBytesError),
+ /// Signature byte slice decoding error.
+ Decode(SigFromSliceError),
+}
+
+impl From<Infallible> for ParseSignatureError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for ParseSignatureError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Hex(ref e) => write_err!(f, "signature hex decoding error"; e),
+ Self::Decode(ref e) => write_err!(f, "signature byte slice decoding error"; e),
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for ParseSignatureError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::Hex(ref e) => Some(e),
+ Self::Decode(ref e) => Some(e),
+ }
+ }
+}
+
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Signature {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Why this scored 19/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.