From 7cdcdaad6c4232e15e03cab4bdfc16ba5af4ee85 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 27 Mar 2022 15:01:08 +0200 Subject: [PATCH 1/3] Support SIGHASH_RESERVED in SchnorrSigHashType::from_u8 --- src/util/sighash.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/sighash.rs b/src/util/sighash.rs index 33cb5bb0..7e6936b2 100644 --- a/src/util/sighash.rs +++ b/src/util/sighash.rs @@ -326,6 +326,7 @@ impl SchnorrSigHashType { 0x81 => Ok(SchnorrSigHashType::AllPlusAnyoneCanPay), 0x82 => Ok(SchnorrSigHashType::NonePlusAnyoneCanPay), 0x83 => Ok(SchnorrSigHashType::SinglePlusAnyoneCanPay), + 0xFF => Ok(SchnorrSigHashType::Reserved), x => Err(Error::InvalidSigHashType(x as u32)), } } From 5be1cdb8c75f0927ecceef02416f063f593e4168 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 24 Mar 2022 07:22:28 +0100 Subject: [PATCH 2/3] PsbtSigHashType Display and FromStr implementation --- src/blockdata/transaction.rs | 2 +- src/util/psbt/map/input.rs | 38 +++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index f625d7e1..6bcfd5c0 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -758,7 +758,7 @@ impl str::FromStr for EcdsaSigHashType { "SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(EcdsaSigHashType::AllPlusAnyoneCanPay), "SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(EcdsaSigHashType::NonePlusAnyoneCanPay), "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(EcdsaSigHashType::SinglePlusAnyoneCanPay), - _ => Err(SigHashTypeParseError{ unrecognized: s.to_owned() }), + _ => Err(SigHashTypeParseError { unrecognized: s.to_owned() }), } } } diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs index e89fa9c9..3bcbea1f 100644 --- a/src/util/psbt/map/input.rs +++ b/src/util/psbt/map/input.rs @@ -14,11 +14,13 @@ use prelude::*; use io; +use core::fmt; +use core::str::FromStr; use secp256k1; use blockdata::script::Script; use blockdata::witness::Witness; -use blockdata::transaction::{Transaction, TxOut, NonStandardSigHashType}; +use blockdata::transaction::{Transaction, TxOut, NonStandardSigHashType, SigHashTypeParseError}; use consensus::encode; use hashes::{self, hash160, ripemd160, sha256, sha256d}; use secp256k1::XOnlyPublicKey; @@ -155,6 +157,40 @@ pub struct PsbtSigHashType { pub (in ::util::psbt) inner: u32, } +impl fmt::Display for PsbtSigHashType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.schnorr_hash_ty() { + Ok(SchnorrSigHashType::Reserved) | Err(_) => write!(f, "{:#x}", self.inner), + Ok(schnorr_hash_ty) => fmt::Display::fmt(&schnorr_hash_ty, f), + } + } +} + +impl FromStr for PsbtSigHashType { + type Err = SigHashTypeParseError; + + #[inline] + fn from_str(s: &str) -> Result { + // We accept strings of form: "SIGHASH_ALL" etc. + // + // NB: some of Schnorr sighash types are non-standard for pre-taproot + // inputs. We also do not support SIGHASH_RESERVED in verbatim form + // ("0xFF" string should be used instead). + match SchnorrSigHashType::from_str(s) { + Ok(SchnorrSigHashType::Reserved) => return Err(SigHashTypeParseError{ unrecognized: s.to_owned() }), + Ok(ty) => return Ok(ty.into()), + Err(_) => {} + } + + // We accept non-standard sighash values. + // TODO: Swap `trim_left_matches` for `trim_start_matches` once MSRV >= 1.30. + if let Ok(inner) = u32::from_str_radix(s.trim_left_matches("0x"), 16) { + return Ok(PsbtSigHashType { inner }); + } + + Err(SigHashTypeParseError{ unrecognized: s.to_owned() }) + } +} impl From for PsbtSigHashType { fn from(ecdsa_hash_ty: EcdsaSigHashType) -> Self { PsbtSigHashType { inner: ecdsa_hash_ty as u32 } From 992857ad0a8bf8a0c6c5ad3d0259404792dabb24 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 26 Mar 2022 02:37:30 +0100 Subject: [PATCH 3/3] PsbtSighashType unit tests --- src/util/psbt/map/input.rs | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs index 3bcbea1f..6d90d559 100644 --- a/src/util/psbt/map/input.rs +++ b/src/util/psbt/map/input.rs @@ -525,3 +525,71 @@ where btree_map::Entry::Occupied(_) => Err(psbt::Error::DuplicateKey(raw_key).into()), } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn psbt_sighash_type_ecdsa() { + for ecdsa in &[ + EcdsaSigHashType::All, + EcdsaSigHashType::None, + EcdsaSigHashType::Single, + EcdsaSigHashType::AllPlusAnyoneCanPay, + EcdsaSigHashType::NonePlusAnyoneCanPay, + EcdsaSigHashType::SinglePlusAnyoneCanPay, + ] { + let sighash = PsbtSigHashType::from(*ecdsa); + let s = format!("{}", sighash); + let back = PsbtSigHashType::from_str(&s).unwrap(); + assert_eq!(back, sighash); + assert_eq!(back.ecdsa_hash_ty().unwrap(), *ecdsa); + } + } + + #[test] + fn psbt_sighash_type_schnorr() { + for schnorr in &[ + SchnorrSigHashType::Default, + SchnorrSigHashType::All, + SchnorrSigHashType::None, + SchnorrSigHashType::Single, + SchnorrSigHashType::AllPlusAnyoneCanPay, + SchnorrSigHashType::NonePlusAnyoneCanPay, + SchnorrSigHashType::SinglePlusAnyoneCanPay, + ] { + let sighash = PsbtSigHashType::from(*schnorr); + let s = format!("{}", sighash); + let back = PsbtSigHashType::from_str(&s).unwrap(); + assert_eq!(back, sighash); + assert_eq!(back.schnorr_hash_ty().unwrap(), *schnorr); + } + } + + #[test] + fn psbt_sighash_type_schnorr_notstd() { + for (schnorr, schnorr_str) in &[ + (SchnorrSigHashType::Reserved, "0xff"), + ] { + let sighash = PsbtSigHashType::from(*schnorr); + let s = format!("{}", sighash); + assert_eq!(&s, schnorr_str); + let back = PsbtSigHashType::from_str(&s).unwrap(); + assert_eq!(back, sighash); + assert_eq!(back.schnorr_hash_ty().unwrap(), *schnorr); + } + } + + #[test] + fn psbt_sighash_type_notstd() { + let nonstd = 0xdddddddd; + let sighash = PsbtSigHashType { inner: nonstd }; + let s = format!("{}", sighash); + let back = PsbtSigHashType::from_str(&s).unwrap(); + + assert_eq!(back, sighash); + assert_eq!(back.ecdsa_hash_ty(), Err(NonStandardSigHashType(nonstd))); + assert_eq!(back.schnorr_hash_ty(), Err(sighash::Error::InvalidSigHashType(nonstd))); + } +}