Add hex Display formatting for taproot::Signature
What changed, and why it matters
This commit adds a human-readable hex display format for Taproot signatures in the rust-bitcoin library, matching a similar feature already available for ECDSA signatures. It is a straightforward feature addition with tests and does not fix any security bug.
No security action required; review as a normal feature addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch implements fmt::Display for taproot::Signature, serializing the signature as lowercase hex. When the sighash type is the default, it omits the trailing sighash byte; otherwise it includes it. This makes Display round-trip with the existing FromStr implementation. Unit tests verify round-tripping for several sighash types and that an explicit sighash byte of 0x00 is rejected as invalid. No unsafe code, cryptographic changes, or bug fixes are present.
Changed components
bitcoin/src/crypto/taproot.rsInspect captured patch +42 / −1
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index 77ac2bef..24e02d8a 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -93,6 +93,17 @@ impl Signature {
}
}
+impl fmt::Display for Signature {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.sighash_type == TapSighashType::Default {
+ // default sighash type, don't add extra sighash byte
+ hex_unstable::fmt_hex_exact!(f, 64, self.serialize(), hex_unstable::Case::Lower)
+ } else {
+ hex_unstable::fmt_hex_exact!(f, 65, self.serialize(), hex_unstable::Case::Lower)
+ }
+ }
+}
+
impl FromStr for Signature {
type Err = ParseSignatureError;
@@ -466,7 +477,9 @@ impl<'a> Arbitrary<'a> for Signature {
#[cfg(test)]
mod tests {
- use super::{SerializedSignature, MAX_LEN};
+ use alloc::string::ToString;
+
+ use super::*;
#[test]
fn iterator_ops_are_homomorphic() {
@@ -498,4 +511,32 @@ mod tests {
assert_eq!(iter1.as_slice(), iter2.as_slice());
}
}
+
+ #[test]
+ fn signature_hex_roundtrip() {
+ let sig_strings = [
+ // default sighash type
+ "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab",
+ // various sighash types
+ "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab01",
+ "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab02",
+ "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab03",
+ "7777777777777777abababababababababababababababababababababababababababababababababababababababababababababababababababababababab81",
+ ];
+ for want in sig_strings {
+ let sig = want.parse::<Signature>().unwrap();
+ let got = sig.to_string();
+ assert_eq!(got, want);
+ }
+ }
+
+ #[test]
+ fn signature_hex_default_error() {
+ let sig_hex = "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab00";
+ let parse_err = sig_hex.parse::<Signature>().unwrap_err();
+ assert!(matches!(
+ parse_err,
+ ParseSignatureError::Decode(SigFromSliceError::SighashType(InvalidSighashTypeError(0))),
+ ));
+ }
}
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.