From 40c246743bab8a91be903ea6d48aa309953c21e8 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 7 Feb 2023 13:47:08 +1100 Subject: [PATCH] Split Sighash into LegacySighash and SegwitV0Sighash Currently we have `TapSighash` that is used for taproot sighashes but for non-taproot sighashes we use `hash_types::Sighash`. We can improve the API by creating a `LegacySighash`, and `SegwitV0Sighash`. Copy the original `Sighash` macro calls to create the two new types in the `sighash` module. While we are at it, put the `TapSighash` and `TapSighashTag` into the `sighash` module also. --- bitcoin/src/blockdata/transaction.rs | 10 ++-- bitcoin/src/crypto/sighash.rs | 72 ++++++++++++++++++++-------- bitcoin/src/hash_types.rs | 7 --- bitcoin/src/psbt/mod.rs | 25 +++++----- bitcoin/src/taproot.rs | 21 ++------ 5 files changed, 75 insertions(+), 60 deletions(-) diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index 3d8308d9..104ef4c7 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -30,7 +30,8 @@ use crate::blockdata::witness::Witness; use crate::blockdata::locktime::absolute::{self, Height, Time}; use crate::blockdata::locktime::relative; use crate::consensus::{encode, Decodable, Encodable}; -use crate::hash_types::{Sighash, Txid, Wtxid}; +use crate::crypto::sighash::LegacySighash; +use crate::hash_types::{Txid, Wtxid}; use crate::VarInt; use crate::internal_macros::impl_consensus_encoding; use crate::parse::impl_parse_str_through_int; @@ -589,10 +590,9 @@ impl EncodeSigningDataResult { /// ```rust /// # use bitcoin::consensus::deserialize; /// # use bitcoin::Transaction; - /// # use bitcoin::crypto::sighash::SighashCache; + /// # use bitcoin::crypto::sighash::{LegacySighash, SighashCache}; /// # use bitcoin_hashes::{Hash, hex::FromHex}; - /// # use bitcoin::hash_types::Sighash; - /// # let mut writer = Sighash::engine(); + /// # let mut writer = LegacySighash::engine(); /// # let input_index = 0; /// # let script_pubkey = bitcoin::ScriptBuf::new(); /// # let sighash_u32 = 0u32; @@ -837,7 +837,7 @@ impl Transaction { input_index: usize, script_pubkey: &Script, sighash_u32: u32 - ) -> Sighash { + ) -> LegacySighash { assert!(input_index < self.input.len()); // Panic on OOB, enables expect below. let cache = crate::sighash::SighashCache::new(self); diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index 1db59f3b..11c70b23 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -19,10 +19,9 @@ use crate::blockdata::transaction::EncodeSigningDataResult; use crate::blockdata::witness::Witness; use crate::consensus::{encode, Encodable}; use crate::error::impl_std_error; -use crate::hashes::{sha256, sha256d, Hash}; -use crate::hash_types::Sighash; +use crate::hashes::{hash_newtype, sha256, sha256t_hash_newtype, sha256d, Hash}; use crate::prelude::*; -use crate::taproot::{LeafVersion, TapLeafHash, TapSighash, TAPROOT_ANNEX_PREFIX}; +use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX}; /// Used for signature hash for invalid use of SIGHASH_SINGLE. #[rustfmt::skip] @@ -33,6 +32,39 @@ pub(crate) const UINT256_ONE: [u8; 32] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]; +/// The SHA-256 midstate value for the [`TapSighash`]. +pub(crate) const MIDSTATE_TAPSIGHASH: [u8; 32] = [ + 245, 4, 164, 37, 215, 248, 120, 59, 19, 99, 134, 138, 227, 229, 86, 88, 110, 238, 148, 93, 188, + 120, 136, 221, 2, 166, 226, 195, 24, 115, 254, 159, +]; +// f504a425d7f8783b1363868ae3e556586eee945dbc7888dd02a6e2c31873fe9f + +macro_rules! impl_thirty_two_byte_hash { + ($ty:ident) => { + impl secp256k1::ThirtyTwoByteHash for $ty { + fn into_32(self) -> [u8; 32] { self.into_inner() } + } + } +} + +#[rustfmt::skip] +hash_newtype!(LegacySighash, sha256d::Hash, 32, + doc="Hash of a transaction according to the legacy signature algorithm", false); +impl_thirty_two_byte_hash!(LegacySighash); + +#[rustfmt::skip] +hash_newtype!(SegwitV0Sighash, sha256d::Hash, 32, + doc="Hash of a transaction according to the segwit version 0 signature algorithm", false); +impl_thirty_two_byte_hash!(SegwitV0Sighash); + +#[rustfmt::skip] +sha256t_hash_newtype!(TapSighash, TapSighashTag, MIDSTATE_TAPSIGHASH, 64, + doc="Taproot-tagged hash with tag \"TapSighash\". + +This hash type is used for computing taproot signature hash.", false +); +impl_thirty_two_byte_hash!(TapSighash); + /// Efficiently calculates signature hash message for legacy, segwit and taproot inputs. #[derive(Debug)] pub struct SighashCache> { @@ -741,9 +773,9 @@ impl> SighashCache { if sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None { self.segwit_cache().outputs.consensus_encode(&mut writer)?; } else if sighash == EcdsaSighashType::Single && input_index < self.tx.borrow().output.len() { - let mut single_enc = Sighash::engine(); + let mut single_enc = LegacySighash::engine(); self.tx.borrow().output[input_index].consensus_encode(&mut single_enc)?; - let hash = Sighash::from_engine(single_enc); + let hash = LegacySighash::from_engine(single_enc); writer.write_all(&hash[..])?; } else { writer.write_all(&zero_hash[..])?; @@ -761,8 +793,8 @@ impl> SighashCache { script_code: &Script, value: u64, sighash_type: EcdsaSighashType, - ) -> Result { - let mut enc = Sighash::engine(); + ) -> Result { + let mut enc = SegwitV0Sighash::engine(); self.segwit_encode_signing_data_to( &mut enc, input_index, @@ -770,7 +802,7 @@ impl> SighashCache { value, sighash_type, )?; - Ok(Sighash::from_engine(enc)) + Ok(SegwitV0Sighash::from_engine(enc)) } /// Encodes the legacy signing data from which a signature hash for a given input index with a @@ -930,15 +962,15 @@ impl> SighashCache { input_index: usize, script_pubkey: &Script, sighash_type: u32, - ) -> Result { - let mut enc = Sighash::engine(); + ) -> Result { + let mut enc = LegacySighash::engine(); if self .legacy_encode_signing_data_to(&mut enc, input_index, script_pubkey, sighash_type) .is_sighash_single_bug()? { - Ok(Sighash::from_inner(UINT256_ONE)) + Ok(LegacySighash::from_inner(UINT256_ONE)) } else { - Ok(Sighash::from_engine(enc)) + Ok(LegacySighash::from_engine(enc)) } } @@ -1068,12 +1100,12 @@ mod tests { use crate::blockdata::locktime::absolute; use crate::consensus::deserialize; use crate::crypto::key::PublicKey; - use crate::hash_types::Sighash; + use crate::crypto::sighash::{LegacySighash, TapSighash}; use crate::hashes::hex::FromHex; - use crate::hashes::{Hash, HashEngine}; + use crate::hashes::HashEngine; use crate::internal_macros::hex; use crate::network::constants::Network; - use crate::taproot::{TapLeafHash, TapSighash}; + use crate::taproot::TapLeafHash; extern crate serde_json; @@ -1092,7 +1124,7 @@ mod tests { let cache = SighashCache::new(&tx); let got = cache.legacy_signature_hash(1, &script, SIGHASH_SINGLE).expect("sighash"); - let want = Sighash::from_slice(&UINT256_ONE).unwrap(); + let want = LegacySighash::from_slice(&UINT256_ONE).unwrap(); assert_eq!(got, want) } @@ -1115,7 +1147,7 @@ mod tests { let script = ScriptBuf::from(Vec::from_hex(script).unwrap()); let mut raw_expected = Vec::from_hex(expected_result).unwrap(); raw_expected.reverse(); - let want = Sighash::from_slice(&raw_expected[..]).unwrap(); + let want = LegacySighash::from_slice(&raw_expected[..]).unwrap(); let cache = SighashCache::new(&tx); let got = cache.legacy_signature_hash(input_index, &script, hash_type as u32).unwrap(); @@ -1635,7 +1667,7 @@ mod tests { let mut cache = SighashCache::new(&tx); assert_eq!( cache.segwit_signature_hash(1, &witness_script, value, EcdsaSighashType::All).unwrap(), - "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670".parse::().unwrap(), + "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670".parse::().unwrap(), ); let cache = cache.segwit_cache(); @@ -1671,7 +1703,7 @@ mod tests { let mut cache = SighashCache::new(&tx); assert_eq!( cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(), - "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6".parse::().unwrap(), + "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6".parse::().unwrap(), ); let cache = cache.segwit_cache(); @@ -1712,7 +1744,7 @@ mod tests { let mut cache = SighashCache::new(&tx); assert_eq!( cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(), - "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c".parse::().unwrap(), + "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c".parse::().unwrap(), ); let cache = cache.segwit_cache(); diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs index 78c0342d..31b7cb84 100644 --- a/bitcoin/src/hash_types.rs +++ b/bitcoin/src/hash_types.rs @@ -66,13 +66,6 @@ See [`hashes::Hash::DISPLAY_BACKWARD`] for more details. "); hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID."); hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash."); - hash_newtype!(Sighash, sha256d::Hash, 32, doc="Hash of the transaction according to the signature algorithm", false); - impl secp256k1::ThirtyTwoByteHash for Sighash { - fn into_32(self) -> [u8; 32] { - use hashes::Hash; - *self.as_inner() - } - } hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key."); hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode."); diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 73b6c541..62d756e9 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -326,34 +326,37 @@ impl PartiallySignedTransaction { let hash_ty = input.ecdsa_hash_ty() .map_err(|_| SignError::InvalidSighashType)?; // Only support standard sighash types. - let sighash = match self.output_type(input_index)? { + match self.output_type(input_index)? { Bare => { - cache.legacy_signature_hash(input_index, spk, hash_ty.to_u32())? + let sighash = cache.legacy_signature_hash(input_index, spk, hash_ty.to_u32())?; + Ok((Message::from(sighash), hash_ty)) }, Sh => { let script_code = input.redeem_script.as_ref().ok_or(SignError::MissingRedeemScript)?; - cache.legacy_signature_hash(input_index, script_code, hash_ty.to_u32())? + let sighash = cache.legacy_signature_hash(input_index, script_code, hash_ty.to_u32())?; + Ok((Message::from(sighash), hash_ty)) }, Wpkh => { let script_code = ScriptBuf::p2wpkh_script_code(spk).ok_or(SignError::NotWpkh)?; - cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)? - } + let sighash = cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?; + Ok((Message::from(sighash), hash_ty)) + }, ShWpkh => { let script_code = ScriptBuf::p2wpkh_script_code(input.redeem_script.as_ref().expect("checked above")) .ok_or(SignError::NotWpkh)?; - cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)? + let sighash = cache.segwit_signature_hash(input_index, &script_code, utxo.value, hash_ty)?; + Ok((Message::from(sighash), hash_ty)) }, Wsh | ShWsh => { let script_code = input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?; - cache.segwit_signature_hash(input_index, script_code, utxo.value, hash_ty)? + let sighash = cache.segwit_signature_hash(input_index, script_code, utxo.value, hash_ty)?; + Ok((Message::from(sighash), hash_ty)) }, Tr => { // This PSBT signing API is WIP, taproot to come shortly. - return Err(SignError::Unsupported); + Err(SignError::Unsupported) } - }; - - Ok((Message::from(sighash), hash_ty)) + } } /// Returns the spending utxo for this PSBT's input at `input_index`. diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs index 34ff3af8..06bfe9e0 100644 --- a/bitcoin/src/taproot.rs +++ b/bitcoin/src/taproot.rs @@ -18,6 +18,8 @@ use crate::hashes::{sha256t_hash_newtype, Hash, HashEngine}; use crate::prelude::*; use crate::{io, Script, ScriptBuf}; +pub use crate::crypto::sighash::{TapSighash, TapSighashTag}; + /// The SHA-256 midstate value for the TapLeaf hash. const MIDSTATE_TAPLEAF: [u8; 32] = [ 156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147, 108, @@ -39,13 +41,6 @@ const MIDSTATE_TAPTWEAK: [u8; 32] = [ ]; // d129a2f3701c655d6583b6c3b941972795f4e23294fd54f4a2ae8d8547ca590b -/// The SHA-256 midstate value for the [`TapSighash`]. -const MIDSTATE_TAPSIGHASH: [u8; 32] = [ - 245, 4, 164, 37, 215, 248, 120, 59, 19, 99, 134, 138, 227, 229, 86, 88, 110, 238, 148, 93, 188, - 120, 136, 221, 2, 166, 226, 195, 24, 115, 254, 159, -]; -// f504a425d7f8783b1363868ae3e556586eee945dbc7888dd02a6e2c31873fe9f - // Taproot test vectors from BIP-341 state the hashes without any reversing #[rustfmt::skip] sha256t_hash_newtype!(TapLeafHash, TapLeafTag, MIDSTATE_TAPLEAF, 64, @@ -62,16 +57,6 @@ sha256t_hash_newtype!(TapTweakHash, TapTweakTag, MIDSTATE_TAPTWEAK, 64, doc="Taproot-tagged hash with tag \"TapTweak\". This hash type is used while computing the tweaked public key", false ); -#[rustfmt::skip] -sha256t_hash_newtype!(TapSighash, TapSighashTag, MIDSTATE_TAPSIGHASH, 64, - doc="Taproot-tagged hash with tag \"TapSighash\". - -This hash type is used for computing taproot signature hash.", false -); - -impl secp256k1::ThirtyTwoByteHash for TapSighash { - fn into_32(self) -> [u8; 32] { self.into_inner() } -} impl TapTweakHash { /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where @@ -1164,6 +1149,8 @@ mod test { #[test] fn test_midstates() { + use crate::crypto::sighash::MIDSTATE_TAPSIGHASH; + // check midstate against hard-coded values assert_eq!(MIDSTATE_TAPLEAF, tag_engine("TapLeaf").midstate().into_inner()); assert_eq!(MIDSTATE_TAPBRANCH, tag_engine("TapBranch").midstate().into_inner());