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:
parent
e38d843536
commit
40c246743b
|
@ -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<E> EncodeSigningDataResult<E> {
|
|||
/// ```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);
|
||||
|
|
|
@ -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<T: Borrow<Transaction>> {
|
||||
|
@ -741,9 +773,9 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
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<R: Borrow<Transaction>> SighashCache<R> {
|
|||
script_code: &Script,
|
||||
value: u64,
|
||||
sighash_type: EcdsaSighashType,
|
||||
) -> Result<Sighash, Error> {
|
||||
let mut enc = Sighash::engine();
|
||||
) -> Result<SegwitV0Sighash, Error> {
|
||||
let mut enc = SegwitV0Sighash::engine();
|
||||
self.segwit_encode_signing_data_to(
|
||||
&mut enc,
|
||||
input_index,
|
||||
|
@ -770,7 +802,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
|||
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<R: Borrow<Transaction>> SighashCache<R> {
|
|||
input_index: usize,
|
||||
script_pubkey: &Script,
|
||||
sighash_type: u32,
|
||||
) -> Result<Sighash, Error> {
|
||||
let mut enc = Sighash::engine();
|
||||
) -> Result<LegacySighash, Error> {
|
||||
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::<Sighash>().unwrap(),
|
||||
"c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670".parse::<SegwitV0Sighash>().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::<Sighash>().unwrap(),
|
||||
"64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6".parse::<SegwitV0Sighash>().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::<Sighash>().unwrap(),
|
||||
"185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c".parse::<SegwitV0Sighash>().unwrap(),
|
||||
);
|
||||
|
||||
let cache = cache.segwit_cache();
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue