Merge rust-bitcoin/rust-bitcoin#779: Introduce PsbtSigHashType

ebdeed086e Cleanup imports (sanket1729)
382c8f9e4f Introduce PsbtSigHashType (sanket1729)

Pull request description:

  We cannot really use `Psbt` for taproot because the sighash type is currently EcdsaSigHashType. We could introduce an enum with two options but then deser is not really clear, so I chose the approach in the current PR. Feedback or other ways to do this welcome :)

  This is NOT related to #776

ACKs for top commit:
  apoelstra:
    ACK ebdeed086e
  dr-orlovsky:
    ACK ebdeed086e

Tree-SHA512: f9424cf3db09098d73f0d431a45ff86a47f11f7d40785bf95e58991fd4d16f0db0a9a3a63f898628b29c95bbd2ca901312a6a44ac6d8aec73a6a34710f6354a2
This commit is contained in:
Dr. Maxim Orlovsky 2022-01-14 11:14:18 +02:00
commit a2da9f58fa
No known key found for this signature in database
GPG Key ID: AF91255DEA466640
5 changed files with 64 additions and 24 deletions

View File

@ -13,13 +13,12 @@
// //
use prelude::*; use prelude::*;
use io;
use ::{EcdsaSig, io};
use secp256k1; use secp256k1;
use blockdata::script::Script; use blockdata::script::Script;
use blockdata::witness::Witness; use blockdata::witness::Witness;
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut}; use blockdata::transaction::{Transaction, TxOut, NonStandardSigHashType};
use consensus::encode; use consensus::encode;
use hashes::{self, hash160, ripemd160, sha256, sha256d}; use hashes::{self, hash160, ripemd160, sha256, sha256d};
use secp256k1::XOnlyPublicKey; use secp256k1::XOnlyPublicKey;
@ -30,8 +29,9 @@ use util::psbt::raw;
use util::psbt::serialize::Deserialize; use util::psbt::serialize::Deserialize;
use util::psbt::{Error, error}; use util::psbt::{Error, error};
use ::{SchnorrSig};
use util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash}; use util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash};
use util::sighash;
use {EcdsaSigHashType, SchnorrSigHashType, EcdsaSig, SchnorrSig};
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00 /// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00; const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00;
@ -92,7 +92,7 @@ pub struct Input {
pub partial_sigs: BTreeMap<secp256k1::PublicKey, EcdsaSig>, pub partial_sigs: BTreeMap<secp256k1::PublicKey, EcdsaSig>,
/// The sighash type to be used for this input. Signatures for this input /// The sighash type to be used for this input. Signatures for this input
/// must use the sighash type. /// must use the sighash type.
pub sighash_type: Option<EcdsaSigHashType>, pub sighash_type: Option<PsbtSigHashType>,
/// The redeem script for this input. /// The redeem script for this input.
pub redeem_script: Option<Script>, pub redeem_script: Option<Script>,
/// The witness script for this input. /// The witness script for this input.
@ -143,6 +143,52 @@ pub struct Input {
pub unknown: BTreeMap<raw::Key, Vec<u8>>, pub unknown: BTreeMap<raw::Key, Vec<u8>>,
} }
/// A Signature hash type for the corresponding input. As of taproot upgrade, the signature hash
/// type can be either [`EcdsaSigHashType`] or [`SchnorrSigHashType`] but it is not possible to know
/// directly which signature hash type the user is dealing with. Therefore, the user is responsible
/// for converting to/from [`PsbtSigHashType`] from/to the desired signature hash type they need.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PsbtSigHashType {
pub (in ::util::psbt) inner: u32,
}
impl From<EcdsaSigHashType> for PsbtSigHashType {
fn from(ecdsa_hash_ty: EcdsaSigHashType) -> Self {
PsbtSigHashType {inner: ecdsa_hash_ty as u32}
}
}
impl From<SchnorrSigHashType> for PsbtSigHashType {
fn from(schnorr_hash_ty: SchnorrSigHashType) -> Self {
PsbtSigHashType {inner: schnorr_hash_ty as u32}
}
}
impl PsbtSigHashType {
/// Returns the [`EcdsaSigHashType`] if the [`PsbtSigHashType`] can be
/// converted to one.
pub fn ecdsa_hash_ty(self) -> Result<EcdsaSigHashType, NonStandardSigHashType> {
EcdsaSigHashType::from_u32_standard(self.inner)
}
/// Returns the [`SchnorrSigHashType`] if the [`PsbtSigHashType`] can be
/// converted to one.
pub fn schnorr_hash_ty(self) -> Result<SchnorrSigHashType, sighash::Error> {
if self.inner > 0xffu32 {
Err(sighash::Error::InvalidSigHashType(self.inner))
} else {
SchnorrSigHashType::from_u8(self.inner as u8)
}
}
/// Obtains the inner sighash byte from this [`PsbtSigHashType`].
pub fn inner(self) -> u32 {
self.inner
}
}
impl Map for Input { impl Map for Input {
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> { fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
let raw::Pair { let raw::Pair {
@ -168,7 +214,7 @@ impl Map for Input {
} }
PSBT_IN_SIGHASH_TYPE => { PSBT_IN_SIGHASH_TYPE => {
impl_psbt_insert_pair! { impl_psbt_insert_pair! {
self.sighash_type <= <raw_key: _>|<raw_value: EcdsaSigHashType> self.sighash_type <= <raw_key: _>|<raw_value: PsbtSigHashType>
} }
} }
PSBT_IN_REDEEM_SCRIPT => { PSBT_IN_REDEEM_SCRIPT => {

View File

@ -53,6 +53,6 @@ mod global;
mod input; mod input;
mod output; mod output;
pub use self::input::Input; pub use self::input::{Input, PsbtSigHashType};
pub use self::output::Output; pub use self::output::Output;
pub use self::output::TapTree; pub use self::output::TapTree;

View File

@ -406,6 +406,7 @@ mod tests {
//! Create a full PSBT value with various fields filled and make sure it can be JSONized. //! Create a full PSBT value with various fields filled and make sure it can be JSONized.
use hashes::sha256d; use hashes::sha256d;
use util::psbt::map::Input; use util::psbt::map::Input;
use EcdsaSigHashType;
// create some values to use in the PSBT // create some values to use in the PSBT
let tx = Transaction { let tx = Transaction {
@ -469,7 +470,7 @@ mod tests {
value: 190303501938, value: 190303501938,
script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"), script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"),
}), }),
sighash_type: Some("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY".parse().unwrap()), sighash_type: Some("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY".parse::<EcdsaSigHashType>().unwrap().into()),
redeem_script: Some(vec![0x51].into()), redeem_script: Some(vec![0x51].into()),
witness_script: None, witness_script: None,
partial_sigs: vec![( partial_sigs: vec![(
@ -732,8 +733,8 @@ mod tests {
.is_p2pkh() .is_p2pkh()
); );
assert_eq!( assert_eq!(
(&psbt.inputs[0].sighash_type).as_ref().unwrap(), (&psbt.inputs[0].sighash_type).as_ref().unwrap().ecdsa_hash_ty().unwrap(),
&EcdsaSigHashType::All EcdsaSigHashType::All
); );
} }

View File

@ -30,10 +30,9 @@ use secp256k1::{self, XOnlyPublicKey};
use util::bip32::{ChildNumber, Fingerprint, KeySource}; use util::bip32::{ChildNumber, Fingerprint, KeySource};
use hashes::{hash160, ripemd160, sha256, sha256d, Hash}; use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use util::ecdsa::EcdsaSig; use util::ecdsa::EcdsaSig;
use util::psbt;
use util::taproot::{TapBranchHash, TapLeafHash, ControlBlock, LeafVersion}; use util::taproot::{TapBranchHash, TapLeafHash, ControlBlock, LeafVersion};
use schnorr; use schnorr;
use super::map::TapTree; use super::map::{TapTree, PsbtSigHashType};
use util::taproot::TaprootBuilder; use util::taproot::TaprootBuilder;
use util::sighash::SchnorrSigHashType; use util::sighash::SchnorrSigHashType;
@ -160,22 +159,16 @@ impl Deserialize for Vec<u8> {
} }
} }
impl Serialize for EcdsaSigHashType { impl Serialize for PsbtSigHashType {
fn serialize(&self) -> Vec<u8> { fn serialize(&self) -> Vec<u8> {
serialize(&self.as_u32()) serialize(&self.inner())
} }
} }
impl Deserialize for EcdsaSigHashType { impl Deserialize for PsbtSigHashType {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> { fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
let raw: u32 = encode::deserialize(bytes)?; let raw: u32 = encode::deserialize(bytes)?;
let rv: EcdsaSigHashType = EcdsaSigHashType::from_u32_consensus(raw); Ok(PsbtSigHashType { inner: raw })
if rv.as_u32() == raw {
Ok(rv)
} else {
Err(psbt::Error::NonStandardSigHashType(raw).into())
}
} }
} }

View File

@ -167,7 +167,7 @@ pub enum Error {
WrongAnnex, WrongAnnex,
/// Invalid Sighash type /// Invalid Sighash type
InvalidSigHashType(u8), InvalidSigHashType(u32),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -286,7 +286,7 @@ impl SchnorrSigHashType {
0x81 => Ok(SchnorrSigHashType::AllPlusAnyoneCanPay), 0x81 => Ok(SchnorrSigHashType::AllPlusAnyoneCanPay),
0x82 => Ok(SchnorrSigHashType::NonePlusAnyoneCanPay), 0x82 => Ok(SchnorrSigHashType::NonePlusAnyoneCanPay),
0x83 => Ok(SchnorrSigHashType::SinglePlusAnyoneCanPay), 0x83 => Ok(SchnorrSigHashType::SinglePlusAnyoneCanPay),
x => Err(Error::InvalidSigHashType(x)), x => Err(Error::InvalidSigHashType(x as u32)),
} }
} }
} }