|
14 | 14 |
|
15 | 15 | use prelude::*;
|
16 | 16 | use io;
|
| 17 | +use core::fmt; |
| 18 | +use core::str::FromStr; |
17 | 19 |
|
18 | 20 | use secp256k1;
|
19 | 21 | use blockdata::script::Script;
|
20 | 22 | use blockdata::witness::Witness;
|
21 |
| -use blockdata::transaction::{Transaction, TxOut, NonStandardSigHashType}; |
| 23 | +use blockdata::transaction::{Transaction, TxOut, NonStandardSigHashType, SigHashTypeParseError}; |
22 | 24 | use consensus::encode;
|
23 | 25 | use hashes::{self, hash160, ripemd160, sha256, sha256d};
|
24 | 26 | use secp256k1::XOnlyPublicKey;
|
@@ -155,6 +157,40 @@ pub struct PsbtSigHashType {
|
155 | 157 | pub (in ::util::psbt) inner: u32,
|
156 | 158 | }
|
157 | 159 |
|
| 160 | +impl fmt::Display for PsbtSigHashType { |
| 161 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 162 | + match self.schnorr_hash_ty() { |
| 163 | + Ok(SchnorrSigHashType::Reserved) | Err(_) => write!(f, "{:#x}", self.inner), |
| 164 | + Ok(schnorr_hash_ty) => fmt::Display::fmt(&schnorr_hash_ty, f), |
| 165 | + } |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +impl FromStr for PsbtSigHashType { |
| 170 | + type Err = SigHashTypeParseError; |
| 171 | + |
| 172 | + #[inline] |
| 173 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 174 | + // We accept strings of form: "SIGHASH_ALL" etc. |
| 175 | + // |
| 176 | + // NB: some of Schnorr sighash types are non-standard for pre-taproot |
| 177 | + // inputs. We also do not support SIGHASH_RESERVED in verbatim form |
| 178 | + // ("0xFF" string should be used instead). |
| 179 | + match SchnorrSigHashType::from_str(s) { |
| 180 | + Ok(SchnorrSigHashType::Reserved) => return Err(SigHashTypeParseError{ unrecognized: s.to_owned() }), |
| 181 | + Ok(ty) => return Ok(ty.into()), |
| 182 | + Err(_) => {} |
| 183 | + } |
| 184 | + |
| 185 | + // We accept non-standard sighash values. |
| 186 | + // TODO: Swap `trim_left_matches` for `trim_start_matches` once MSRV >= 1.30. |
| 187 | + if let Ok(inner) = u32::from_str_radix(s.trim_left_matches("0x"), 16) { |
| 188 | + return Ok(PsbtSigHashType { inner }); |
| 189 | + } |
| 190 | + |
| 191 | + Err(SigHashTypeParseError{ unrecognized: s.to_owned() }) |
| 192 | + } |
| 193 | +} |
158 | 194 | impl From<EcdsaSigHashType> for PsbtSigHashType {
|
159 | 195 | fn from(ecdsa_hash_ty: EcdsaSigHashType) -> Self {
|
160 | 196 | PsbtSigHashType { inner: ecdsa_hash_ty as u32 }
|
@@ -500,3 +536,71 @@ where
|
500 | 536 | btree_map::Entry::Occupied(_) => Err(psbt::Error::DuplicateKey(raw_key).into()),
|
501 | 537 | }
|
502 | 538 | }
|
| 539 | + |
| 540 | +#[cfg(test)] |
| 541 | +mod test { |
| 542 | + use super::*; |
| 543 | + |
| 544 | + #[test] |
| 545 | + fn psbt_sighash_type_ecdsa() { |
| 546 | + for ecdsa in &[ |
| 547 | + EcdsaSigHashType::All, |
| 548 | + EcdsaSigHashType::None, |
| 549 | + EcdsaSigHashType::Single, |
| 550 | + EcdsaSigHashType::AllPlusAnyoneCanPay, |
| 551 | + EcdsaSigHashType::NonePlusAnyoneCanPay, |
| 552 | + EcdsaSigHashType::SinglePlusAnyoneCanPay, |
| 553 | + ] { |
| 554 | + let sighash = PsbtSigHashType::from(*ecdsa); |
| 555 | + let s = format!("{}", sighash); |
| 556 | + let back = PsbtSigHashType::from_str(&s).unwrap(); |
| 557 | + assert_eq!(back, sighash); |
| 558 | + assert_eq!(back.ecdsa_hash_ty().unwrap(), *ecdsa); |
| 559 | + } |
| 560 | + } |
| 561 | + |
| 562 | + #[test] |
| 563 | + fn psbt_sighash_type_schnorr() { |
| 564 | + for schnorr in &[ |
| 565 | + SchnorrSigHashType::Default, |
| 566 | + SchnorrSigHashType::All, |
| 567 | + SchnorrSigHashType::None, |
| 568 | + SchnorrSigHashType::Single, |
| 569 | + SchnorrSigHashType::AllPlusAnyoneCanPay, |
| 570 | + SchnorrSigHashType::NonePlusAnyoneCanPay, |
| 571 | + SchnorrSigHashType::SinglePlusAnyoneCanPay, |
| 572 | + ] { |
| 573 | + let sighash = PsbtSigHashType::from(*schnorr); |
| 574 | + let s = format!("{}", sighash); |
| 575 | + let back = PsbtSigHashType::from_str(&s).unwrap(); |
| 576 | + assert_eq!(back, sighash); |
| 577 | + assert_eq!(back.schnorr_hash_ty().unwrap(), *schnorr); |
| 578 | + } |
| 579 | + } |
| 580 | + |
| 581 | + #[test] |
| 582 | + fn psbt_sighash_type_schnorr_notstd() { |
| 583 | + for (schnorr, schnorr_str) in &[ |
| 584 | + (SchnorrSigHashType::Reserved, "0xff"), |
| 585 | + ] { |
| 586 | + let sighash = PsbtSigHashType::from(*schnorr); |
| 587 | + let s = format!("{}", sighash); |
| 588 | + assert_eq!(&s, schnorr_str); |
| 589 | + let back = PsbtSigHashType::from_str(&s).unwrap(); |
| 590 | + assert_eq!(back, sighash); |
| 591 | + assert_eq!(back.schnorr_hash_ty().unwrap(), *schnorr); |
| 592 | + } |
| 593 | + } |
| 594 | + |
| 595 | + #[test] |
| 596 | + fn psbt_sighash_type_notstd() { |
| 597 | + let nonstd = 0xdddddddd; |
| 598 | + let sighash = PsbtSigHashType { inner: nonstd }; |
| 599 | + let s = format!("{}", sighash); |
| 600 | + let back = PsbtSigHashType::from_str(&s).unwrap(); |
| 601 | + |
| 602 | + assert_eq!(back, sighash); |
| 603 | + assert_eq!(back.ecdsa_hash_ty(), Err(NonStandardSigHashType(nonstd))); |
| 604 | + assert_eq!(back.schnorr_hash_ty(), Err(sighash::Error::InvalidSigHashType(nonstd))); |
| 605 | + } |
| 606 | +} |
0 commit comments