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.
This commit is contained in:
Tobin C. Harding 2023-02-07 13:47:08 +11:00
parent e38d843536
commit 40c246743b
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
5 changed files with 75 additions and 60 deletions

View File

@ -30,7 +30,8 @@ use crate::blockdata::witness::Witness;
use crate::blockdata::locktime::absolute::{self, Height, Time}; use crate::blockdata::locktime::absolute::{self, Height, Time};
use crate::blockdata::locktime::relative; use crate::blockdata::locktime::relative;
use crate::consensus::{encode, Decodable, Encodable}; 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::VarInt;
use crate::internal_macros::impl_consensus_encoding; use crate::internal_macros::impl_consensus_encoding;
use crate::parse::impl_parse_str_through_int; use crate::parse::impl_parse_str_through_int;
@ -589,10 +590,9 @@ impl<E> EncodeSigningDataResult<E> {
/// ```rust /// ```rust
/// # use bitcoin::consensus::deserialize; /// # use bitcoin::consensus::deserialize;
/// # use bitcoin::Transaction; /// # use bitcoin::Transaction;
/// # use bitcoin::crypto::sighash::SighashCache; /// # use bitcoin::crypto::sighash::{LegacySighash, SighashCache};
/// # use bitcoin_hashes::{Hash, hex::FromHex}; /// # use bitcoin_hashes::{Hash, hex::FromHex};
/// # use bitcoin::hash_types::Sighash; /// # let mut writer = LegacySighash::engine();
/// # let mut writer = Sighash::engine();
/// # let input_index = 0; /// # let input_index = 0;
/// # let script_pubkey = bitcoin::ScriptBuf::new(); /// # let script_pubkey = bitcoin::ScriptBuf::new();
/// # let sighash_u32 = 0u32; /// # let sighash_u32 = 0u32;
@ -837,7 +837,7 @@ impl Transaction {
input_index: usize, input_index: usize,
script_pubkey: &Script, script_pubkey: &Script,
sighash_u32: u32 sighash_u32: u32
) -> Sighash { ) -> LegacySighash {
assert!(input_index < self.input.len()); // Panic on OOB, enables expect below. assert!(input_index < self.input.len()); // Panic on OOB, enables expect below.
let cache = crate::sighash::SighashCache::new(self); let cache = crate::sighash::SighashCache::new(self);

View File

@ -19,10 +19,9 @@ use crate::blockdata::transaction::EncodeSigningDataResult;
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::consensus::{encode, Encodable}; use crate::consensus::{encode, Encodable};
use crate::error::impl_std_error; use crate::error::impl_std_error;
use crate::hashes::{sha256, sha256d, Hash}; use crate::hashes::{hash_newtype, sha256, sha256t_hash_newtype, sha256d, Hash};
use crate::hash_types::Sighash;
use crate::prelude::*; 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. /// Used for signature hash for invalid use of SIGHASH_SINGLE.
#[rustfmt::skip] #[rustfmt::skip]
@ -33,6 +32,39 @@ pub(crate) const UINT256_ONE: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0 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. /// Efficiently calculates signature hash message for legacy, segwit and taproot inputs.
#[derive(Debug)] #[derive(Debug)]
pub struct SighashCache<T: Borrow<Transaction>> { pub struct SighashCache<T: Borrow<Transaction>> {
@ -741,9 +773,9 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
if sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None { if sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None {
self.segwit_cache().outputs.consensus_encode(&mut writer)?; self.segwit_cache().outputs.consensus_encode(&mut writer)?;
} else if sighash == EcdsaSighashType::Single && input_index < self.tx.borrow().output.len() { } 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)?; 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[..])?; writer.write_all(&hash[..])?;
} else { } else {
writer.write_all(&zero_hash[..])?; writer.write_all(&zero_hash[..])?;
@ -761,8 +793,8 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
script_code: &Script, script_code: &Script,
value: u64, value: u64,
sighash_type: EcdsaSighashType, sighash_type: EcdsaSighashType,
) -> Result<Sighash, Error> { ) -> Result<SegwitV0Sighash, Error> {
let mut enc = Sighash::engine(); let mut enc = SegwitV0Sighash::engine();
self.segwit_encode_signing_data_to( self.segwit_encode_signing_data_to(
&mut enc, &mut enc,
input_index, input_index,
@ -770,7 +802,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
value, value,
sighash_type, 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 /// Encodes the legacy signing data from which a signature hash for a given input index with a
@ -930,15 +962,15 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
input_index: usize, input_index: usize,
script_pubkey: &Script, script_pubkey: &Script,
sighash_type: u32, sighash_type: u32,
) -> Result<Sighash, Error> { ) -> Result<LegacySighash, Error> {
let mut enc = Sighash::engine(); let mut enc = LegacySighash::engine();
if self if self
.legacy_encode_signing_data_to(&mut enc, input_index, script_pubkey, sighash_type) .legacy_encode_signing_data_to(&mut enc, input_index, script_pubkey, sighash_type)
.is_sighash_single_bug()? .is_sighash_single_bug()?
{ {
Ok(Sighash::from_inner(UINT256_ONE)) Ok(LegacySighash::from_inner(UINT256_ONE))
} else { } else {
Ok(Sighash::from_engine(enc)) Ok(LegacySighash::from_engine(enc))
} }
} }
@ -1068,12 +1100,12 @@ mod tests {
use crate::blockdata::locktime::absolute; use crate::blockdata::locktime::absolute;
use crate::consensus::deserialize; use crate::consensus::deserialize;
use crate::crypto::key::PublicKey; use crate::crypto::key::PublicKey;
use crate::hash_types::Sighash; use crate::crypto::sighash::{LegacySighash, TapSighash};
use crate::hashes::hex::FromHex; use crate::hashes::hex::FromHex;
use crate::hashes::{Hash, HashEngine}; use crate::hashes::HashEngine;
use crate::internal_macros::hex; use crate::internal_macros::hex;
use crate::network::constants::Network; use crate::network::constants::Network;
use crate::taproot::{TapLeafHash, TapSighash}; use crate::taproot::TapLeafHash;
extern crate serde_json; extern crate serde_json;
@ -1092,7 +1124,7 @@ mod tests {
let cache = SighashCache::new(&tx); let cache = SighashCache::new(&tx);
let got = cache.legacy_signature_hash(1, &script, SIGHASH_SINGLE).expect("sighash"); 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) assert_eq!(got, want)
} }
@ -1115,7 +1147,7 @@ mod tests {
let script = ScriptBuf::from(Vec::from_hex(script).unwrap()); let script = ScriptBuf::from(Vec::from_hex(script).unwrap());
let mut raw_expected = Vec::from_hex(expected_result).unwrap(); let mut raw_expected = Vec::from_hex(expected_result).unwrap();
raw_expected.reverse(); 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 cache = SighashCache::new(&tx);
let got = cache.legacy_signature_hash(input_index, &script, hash_type as u32).unwrap(); 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); let mut cache = SighashCache::new(&tx);
assert_eq!( assert_eq!(
cache.segwit_signature_hash(1, &witness_script, value, EcdsaSighashType::All).unwrap(), cache.segwit_signature_hash(1, &witness_script, value, EcdsaSighashType::All).unwrap(),
"c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670".parse::<Sighash>().unwrap(), "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670".parse::<SegwitV0Sighash>().unwrap(),
); );
let cache = cache.segwit_cache(); let cache = cache.segwit_cache();
@ -1671,7 +1703,7 @@ mod tests {
let mut cache = SighashCache::new(&tx); let mut cache = SighashCache::new(&tx);
assert_eq!( assert_eq!(
cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(), cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(),
"64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6".parse::<Sighash>().unwrap(), "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6".parse::<SegwitV0Sighash>().unwrap(),
); );
let cache = cache.segwit_cache(); let cache = cache.segwit_cache();
@ -1712,7 +1744,7 @@ mod tests {
let mut cache = SighashCache::new(&tx); let mut cache = SighashCache::new(&tx);
assert_eq!( assert_eq!(
cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(), cache.segwit_signature_hash(0, &witness_script, value, EcdsaSighashType::All).unwrap(),
"185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c".parse::<Sighash>().unwrap(), "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c".parse::<SegwitV0Sighash>().unwrap(),
); );
let cache = cache.segwit_cache(); let cache = cache.segwit_cache();

View File

@ -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!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID.");
hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash."); 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!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key.");
hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode."); hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode.");

View File

@ -326,34 +326,37 @@ impl PartiallySignedTransaction {
let hash_ty = input.ecdsa_hash_ty() let hash_ty = input.ecdsa_hash_ty()
.map_err(|_| SignError::InvalidSighashType)?; // Only support standard sighash types. .map_err(|_| SignError::InvalidSighashType)?; // Only support standard sighash types.
let sighash = match self.output_type(input_index)? { match self.output_type(input_index)? {
Bare => { 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 => { Sh => {
let script_code = input.redeem_script.as_ref().ok_or(SignError::MissingRedeemScript)?; 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 => { Wpkh => {
let script_code = ScriptBuf::p2wpkh_script_code(spk).ok_or(SignError::NotWpkh)?; 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 => { ShWpkh => {
let script_code = ScriptBuf::p2wpkh_script_code(input.redeem_script.as_ref().expect("checked above")) let script_code = ScriptBuf::p2wpkh_script_code(input.redeem_script.as_ref().expect("checked above"))
.ok_or(SignError::NotWpkh)?; .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 => { Wsh | ShWsh => {
let script_code = input.witness_script.as_ref().ok_or(SignError::MissingWitnessScript)?; 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 => { Tr => {
// This PSBT signing API is WIP, taproot to come shortly. // 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`. /// Returns the spending utxo for this PSBT's input at `input_index`.

View File

@ -18,6 +18,8 @@ use crate::hashes::{sha256t_hash_newtype, Hash, HashEngine};
use crate::prelude::*; use crate::prelude::*;
use crate::{io, Script, ScriptBuf}; use crate::{io, Script, ScriptBuf};
pub use crate::crypto::sighash::{TapSighash, TapSighashTag};
/// The SHA-256 midstate value for the TapLeaf hash. /// The SHA-256 midstate value for the TapLeaf hash.
const MIDSTATE_TAPLEAF: [u8; 32] = [ 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, 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 // 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 // Taproot test vectors from BIP-341 state the hashes without any reversing
#[rustfmt::skip] #[rustfmt::skip]
sha256t_hash_newtype!(TapLeafHash, TapLeafTag, MIDSTATE_TAPLEAF, 64, 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\". doc="Taproot-tagged hash with tag \"TapTweak\".
This hash type is used while computing the tweaked public key", false 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 { impl TapTweakHash {
/// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where /// Creates a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where
@ -1164,6 +1149,8 @@ mod test {
#[test] #[test]
fn test_midstates() { fn test_midstates() {
use crate::crypto::sighash::MIDSTATE_TAPSIGHASH;
// check midstate against hard-coded values // check midstate against hard-coded values
assert_eq!(MIDSTATE_TAPLEAF, tag_engine("TapLeaf").midstate().into_inner()); assert_eq!(MIDSTATE_TAPLEAF, tag_engine("TapLeaf").midstate().into_inner());
assert_eq!(MIDSTATE_TAPBRANCH, tag_engine("TapBranch").midstate().into_inner()); assert_eq!(MIDSTATE_TAPBRANCH, tag_engine("TapBranch").midstate().into_inner());