Merge rust-bitcoin/rust-bitcoin#1597: Move sighash types around
be7b3754a9
Rename schnorr module to taproot (Tobin C. Harding)9f39e872bc
Rename SchnorrSighashType to TapSighashType (Tobin C. Harding)f5c26693c5
Make match arms more terse (Tobin C. Harding)40c246743b
Split Sighash into LegacySighash and SegwitV0Sighash (Tobin C. Harding)e38d843536
Do not use deprecated function in rustdoc example (Tobin C. Harding)98130f49f1
Rename TapSighashHash to TapSighash (Tobin C. Harding)7e4da3c0ab
Move taproot keys to the keys module (Tobin C. Harding)c5fe315a93
Move sighash to crypto module (Tobin C. Harding) Pull request description: This PR is now part 1 of great sighash clean up. It does not attempt to split ecdsa into two parts as discussed below, that is WIP and will be done in a separate PR after this upcoming release. Does however create `LegacySighash` and `SegwitV0Sighash` types. This PR moves us along the road by doing: - Move `sighash` to the `crypto` module, where I should have put it ages ago :) - Move the taproot keys to the `keys` module - Rename the `schnorr` module -> `taproot` - Rename `TapSighashHash` -> `TapSighash` - Rename `SchnorrSighashType` -> `TapSighashType` - Fix a bunch of other schnorr usage (including pub/priv methods). This leaves us in a situation where we have taproot sig and sighash type, and non-taproot ones prefixed with "ecdsa". Its not uniform but it kind of makes sense. Fix: #1542 Fix: #1550 ACKs for top commit: Kixunil: ACKbe7b3754a9
apoelstra: ACKbe7b3754a9
Tree-SHA512: 4c52bea9ba71713afa181d625153d73d3c5b7c6f5786248960958982332a6a79501cd26e13a66bfb9700d9b8f59717446abb795ecbfbd8d60d9d59a38c1d718b
This commit is contained in:
commit
090c376fdf
|
@ -81,18 +81,18 @@ use std::str::FromStr;
|
||||||
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
|
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
|
||||||
use bitcoin::consensus::encode;
|
use bitcoin::consensus::encode;
|
||||||
use bitcoin::constants::COIN_VALUE;
|
use bitcoin::constants::COIN_VALUE;
|
||||||
|
use bitcoin::crypto::taproot;
|
||||||
use bitcoin::hashes::Hash;
|
use bitcoin::hashes::Hash;
|
||||||
use bitcoin::key::XOnlyPublicKey;
|
use bitcoin::key::{TapTweak, XOnlyPublicKey};
|
||||||
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
|
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
|
||||||
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
|
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
|
||||||
use bitcoin::schnorr::{self, TapTweak};
|
|
||||||
use bitcoin::secp256k1::{Message, Secp256k1};
|
use bitcoin::secp256k1::{Message, Secp256k1};
|
||||||
use bitcoin::sighash::{self, SchnorrSighashType, SighashCache};
|
use bitcoin::sighash::{self, TapSighashType, SighashCache};
|
||||||
use bitcoin::taproot::{
|
use bitcoin::taproot::{
|
||||||
LeafVersion, TapLeafHash, TapSighashHash, TaprootBuilder, TaprootSpendInfo,
|
LeafVersion, TapLeafHash, TapSighash, TaprootBuilder, TaprootSpendInfo,
|
||||||
};
|
};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
absolute, script, Address, Amount, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Witness,
|
absolute, script, Address, Amount, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Witness,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
@ -283,8 +283,8 @@ fn generate_bip86_key_spend_tx(
|
||||||
|(vout, input)| {
|
|(vout, input)| {
|
||||||
let hash_ty = input
|
let hash_ty = input
|
||||||
.sighash_type
|
.sighash_type
|
||||||
.and_then(|psbt_sighash_type| psbt_sighash_type.schnorr_hash_ty().ok())
|
.and_then(|psbt_sighash_type| psbt_sighash_type.taproot_hash_ty().ok())
|
||||||
.unwrap_or(SchnorrSighashType::All);
|
.unwrap_or(TapSighashType::All);
|
||||||
let hash = SighashCache::new(&unsigned_tx).taproot_key_spend_signature_hash(
|
let hash = SighashCache::new(&unsigned_tx).taproot_key_spend_signature_hash(
|
||||||
vout,
|
vout,
|
||||||
&sighash::Prevouts::All(&[TxOut {
|
&sighash::Prevouts::All(&[TxOut {
|
||||||
|
@ -300,7 +300,7 @@ fn generate_bip86_key_spend_tx(
|
||||||
.ok_or("Missing taproot key origin")?;
|
.ok_or("Missing taproot key origin")?;
|
||||||
|
|
||||||
let secret_key = master_xpriv.derive_priv(secp, &derivation_path)?.to_priv().inner;
|
let secret_key = master_xpriv.derive_priv(secp, &derivation_path)?.to_priv().inner;
|
||||||
sign_psbt_schnorr(
|
sign_psbt_taproot(
|
||||||
&secret_key,
|
&secret_key,
|
||||||
input.tap_internal_key.unwrap(),
|
input.tap_internal_key.unwrap(),
|
||||||
None,
|
None,
|
||||||
|
@ -518,8 +518,8 @@ impl BenefactorWallet {
|
||||||
|
|
||||||
let hash_ty = input
|
let hash_ty = input
|
||||||
.sighash_type
|
.sighash_type
|
||||||
.and_then(|psbt_sighash_type| psbt_sighash_type.schnorr_hash_ty().ok())
|
.and_then(|psbt_sighash_type| psbt_sighash_type.taproot_hash_ty().ok())
|
||||||
.unwrap_or(SchnorrSighashType::All);
|
.unwrap_or(TapSighashType::All);
|
||||||
let hash = SighashCache::new(&psbt.unsigned_tx).taproot_key_spend_signature_hash(
|
let hash = SighashCache::new(&psbt.unsigned_tx).taproot_key_spend_signature_hash(
|
||||||
0,
|
0,
|
||||||
&sighash::Prevouts::All(&[TxOut {
|
&sighash::Prevouts::All(&[TxOut {
|
||||||
|
@ -536,7 +536,7 @@ impl BenefactorWallet {
|
||||||
.ok_or("Missing taproot key origin")?;
|
.ok_or("Missing taproot key origin")?;
|
||||||
let secret_key =
|
let secret_key =
|
||||||
self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
|
self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
|
||||||
sign_psbt_schnorr(
|
sign_psbt_taproot(
|
||||||
&secret_key,
|
&secret_key,
|
||||||
spend_info.internal_key(),
|
spend_info.internal_key(),
|
||||||
None,
|
None,
|
||||||
|
@ -660,7 +660,7 @@ impl BeneficiaryWallet {
|
||||||
let secret_key =
|
let secret_key =
|
||||||
self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
|
self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
|
||||||
for lh in leaf_hashes {
|
for lh in leaf_hashes {
|
||||||
let hash_ty = SchnorrSighashType::All;
|
let hash_ty = TapSighashType::All;
|
||||||
let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(
|
let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(
|
||||||
0,
|
0,
|
||||||
&sighash::Prevouts::All(&[TxOut {
|
&sighash::Prevouts::All(&[TxOut {
|
||||||
|
@ -670,7 +670,7 @@ impl BeneficiaryWallet {
|
||||||
*lh,
|
*lh,
|
||||||
hash_ty,
|
hash_ty,
|
||||||
)?;
|
)?;
|
||||||
sign_psbt_schnorr(
|
sign_psbt_taproot(
|
||||||
&secret_key,
|
&secret_key,
|
||||||
*x_only_pubkey,
|
*x_only_pubkey,
|
||||||
Some(*lh),
|
Some(*lh),
|
||||||
|
@ -730,13 +730,13 @@ impl BeneficiaryWallet {
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
// Calling this with `leaf_hash` = `None` will sign for key-spend
|
// Calling this with `leaf_hash` = `None` will sign for key-spend
|
||||||
fn sign_psbt_schnorr(
|
fn sign_psbt_taproot(
|
||||||
secret_key: &secp256k1::SecretKey,
|
secret_key: &secp256k1::SecretKey,
|
||||||
pubkey: XOnlyPublicKey,
|
pubkey: XOnlyPublicKey,
|
||||||
leaf_hash: Option<TapLeafHash>,
|
leaf_hash: Option<TapLeafHash>,
|
||||||
psbt_input: &mut psbt::Input,
|
psbt_input: &mut psbt::Input,
|
||||||
hash: TapSighashHash,
|
hash: TapSighash,
|
||||||
hash_ty: SchnorrSighashType,
|
hash_ty: TapSighashType,
|
||||||
secp: &Secp256k1<secp256k1::All>,
|
secp: &Secp256k1<secp256k1::All>,
|
||||||
) {
|
) {
|
||||||
let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
|
let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
|
||||||
|
@ -747,7 +747,7 @@ fn sign_psbt_schnorr(
|
||||||
|
|
||||||
let sig = secp.sign_schnorr(&Message::from_slice(&hash.into_inner()[..]).unwrap(), &keypair);
|
let sig = secp.sign_schnorr(&Message::from_slice(&hash.into_inner()[..]).unwrap(), &keypair);
|
||||||
|
|
||||||
let final_signature = schnorr::Signature { sig, hash_ty };
|
let final_signature = taproot::Signature { sig, hash_ty };
|
||||||
|
|
||||||
if let Some(lh) = leaf_hash {
|
if let Some(lh) = leaf_hash {
|
||||||
psbt_input.tap_script_sigs.insert((pubkey, lh), final_signature);
|
psbt_input.tap_script_sigs.insert((pubkey, lh), final_signature);
|
||||||
|
|
|
@ -44,8 +44,7 @@ use crate::blockdata::constants::{
|
||||||
use crate::blockdata::opcodes;
|
use crate::blockdata::opcodes;
|
||||||
use crate::blockdata::opcodes::all::*;
|
use crate::blockdata::opcodes::all::*;
|
||||||
use crate::blockdata::script::{self, Instruction, Script, ScriptBuf, PushBytes, PushBytesBuf, PushBytesErrorReport};
|
use crate::blockdata::script::{self, Instruction, Script, ScriptBuf, PushBytes, PushBytesBuf, PushBytesErrorReport};
|
||||||
use crate::crypto::key::PublicKey;
|
use crate::crypto::key::{PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
||||||
use crate::crypto::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
|
||||||
use crate::error::ParseIntError;
|
use crate::error::ParseIntError;
|
||||||
use crate::hash_types::{PubkeyHash, ScriptHash};
|
use crate::hash_types::{PubkeyHash, ScriptHash};
|
||||||
use crate::hashes::{sha256, Hash, HashEngine};
|
use crate::hashes::{sha256, Hash, HashEngine};
|
||||||
|
|
|
@ -17,10 +17,9 @@ use crate::blockdata::script::Error;
|
||||||
use crate::consensus::Encodable;
|
use crate::consensus::Encodable;
|
||||||
use crate::hash_types::{ScriptHash, WScriptHash};
|
use crate::hash_types::{ScriptHash, WScriptHash};
|
||||||
use crate::hashes::Hash;
|
use crate::hashes::Hash;
|
||||||
use crate::key::PublicKey;
|
use crate::key::{PublicKey, UntweakedPublicKey};
|
||||||
use crate::policy::DUST_RELAY_TX_FEE;
|
use crate::policy::DUST_RELAY_TX_FEE;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::schnorr::UntweakedPublicKey;
|
|
||||||
use crate::taproot::{LeafVersion, TapNodeHash, TapLeafHash};
|
use crate::taproot::{LeafVersion, TapNodeHash, TapLeafHash};
|
||||||
|
|
||||||
/// Bitcoin script slice.
|
/// Bitcoin script slice.
|
||||||
|
|
|
@ -11,9 +11,8 @@ use crate::blockdata::opcodes::{self, all::*};
|
||||||
use crate::blockdata::script::{opcode_to_verify, Builder, Instruction, Script, PushBytes};
|
use crate::blockdata::script::{opcode_to_verify, Builder, Instruction, Script, PushBytes};
|
||||||
use crate::hashes::hex;
|
use crate::hashes::hex;
|
||||||
use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
use crate::hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
||||||
use crate::key::PublicKey;
|
use crate::key::{PublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};
|
|
||||||
use crate::taproot::TapNodeHash;
|
use crate::taproot::TapNodeHash;
|
||||||
|
|
||||||
/// An owned, growable script.
|
/// An owned, growable script.
|
||||||
|
|
|
@ -30,14 +30,15 @@ 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;
|
||||||
use super::Weight;
|
use super::Weight;
|
||||||
|
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use crate::sighash::{EcdsaSighashType, SchnorrSighashType};
|
use crate::sighash::{EcdsaSighashType, TapSighashType};
|
||||||
|
|
||||||
/// A reference to a transaction output.
|
/// A reference to a transaction output.
|
||||||
///
|
///
|
||||||
|
@ -589,16 +590,17 @@ impl<E> EncodeSigningDataResult<E> {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use bitcoin::consensus::deserialize;
|
/// # use bitcoin::consensus::deserialize;
|
||||||
/// # use bitcoin::Transaction;
|
/// # use bitcoin::Transaction;
|
||||||
/// # use bitcoin::hash_types::Sighash;
|
/// # use bitcoin::crypto::sighash::{LegacySighash, SighashCache};
|
||||||
/// # use bitcoin_hashes::{Hash, hex::FromHex};
|
/// # use bitcoin_hashes::{Hash, hex::FromHex};
|
||||||
/// # let mut writer = Sighash::engine();
|
/// # let mut writer = LegacySighash::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;
|
||||||
/// # const SOME_TX: &'static str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
|
/// # const SOME_TX: &'static str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
|
||||||
/// # let raw_tx = Vec::from_hex(SOME_TX).unwrap();
|
/// # let raw_tx = Vec::from_hex(SOME_TX).unwrap();
|
||||||
/// # let tx: Transaction = deserialize(&raw_tx).unwrap();
|
/// # let tx: Transaction = deserialize(&raw_tx).unwrap();
|
||||||
/// if tx.encode_signing_data_to(&mut writer, input_index, &script_pubkey, sighash_u32)
|
/// # let cache = SighashCache::new(&tx);
|
||||||
|
/// if cache.legacy_encode_signing_data_to(&mut writer, input_index, &script_pubkey, sighash_u32)
|
||||||
/// .is_sighash_single_bug()
|
/// .is_sighash_single_bug()
|
||||||
/// .expect("writer can't fail") {
|
/// .expect("writer can't fail") {
|
||||||
/// // use a hash value of "1", instead of computing the actual hash due to SIGHASH_SINGLE bug
|
/// // use a hash value of "1", instead of computing the actual hash due to SIGHASH_SINGLE bug
|
||||||
|
@ -835,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);
|
||||||
|
|
|
@ -12,12 +12,14 @@ use core::{ops, str::FromStr};
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
use bitcoin_internals::write_err;
|
use bitcoin_internals::write_err;
|
||||||
pub use secp256k1::{self, Secp256k1, XOnlyPublicKey, KeyPair};
|
|
||||||
|
pub use secp256k1::{self, constants, Secp256k1, KeyPair, XOnlyPublicKey, Verification, Parity};
|
||||||
|
|
||||||
use crate::{base58, io};
|
use crate::{base58, io};
|
||||||
use crate::network::constants::Network;
|
use crate::network::constants::Network;
|
||||||
use crate::hashes::{Hash, hash160, hex, hex::FromHex};
|
use crate::hashes::{Hash, hash160, hex, hex::FromHex};
|
||||||
use crate::hash_types::{PubkeyHash, WPubkeyHash};
|
use crate::hash_types::{PubkeyHash, WPubkeyHash};
|
||||||
|
use crate::taproot::{TapNodeHash, TapTweakHash};
|
||||||
|
|
||||||
/// A key-related error.
|
/// A key-related error.
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
@ -546,6 +548,215 @@ impl<'de> serde::Deserialize<'de> for PublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Untweaked BIP-340 X-coord-only public key
|
||||||
|
pub type UntweakedPublicKey = XOnlyPublicKey;
|
||||||
|
|
||||||
|
/// Tweaked BIP-340 X-coord-only public key
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
|
pub struct TweakedPublicKey(XOnlyPublicKey);
|
||||||
|
|
||||||
|
impl fmt::LowerHex for TweakedPublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::LowerHex::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TweakedPublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Untweaked BIP-340 key pair
|
||||||
|
pub type UntweakedKeyPair = KeyPair;
|
||||||
|
|
||||||
|
/// Tweaked BIP-340 key pair
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// # #[cfg(feature = "rand-std")] {
|
||||||
|
/// # use bitcoin::crypto::key::{KeyPair, TweakedKeyPair, TweakedPublicKey};
|
||||||
|
/// # use bitcoin::secp256k1::{rand, Secp256k1};
|
||||||
|
/// # let secp = Secp256k1::new();
|
||||||
|
/// # let keypair = TweakedKeyPair::dangerous_assume_tweaked(KeyPair::new(&secp, &mut rand::thread_rng()));
|
||||||
|
/// // There are various conversion methods available to get a tweaked pubkey from a tweaked keypair.
|
||||||
|
/// let (_pk, _parity) = keypair.public_parts();
|
||||||
|
/// let _pk = TweakedPublicKey::from_keypair(keypair);
|
||||||
|
/// let _pk = TweakedPublicKey::from(keypair);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
|
pub struct TweakedKeyPair(KeyPair);
|
||||||
|
|
||||||
|
/// A trait for tweaking BIP340 key types (x-only public keys and key pairs).
|
||||||
|
pub trait TapTweak {
|
||||||
|
/// Tweaked key type with optional auxiliary information
|
||||||
|
type TweakedAux;
|
||||||
|
/// Tweaked key type
|
||||||
|
type TweakedKey;
|
||||||
|
|
||||||
|
/// Tweaks an untweaked key with corresponding public key value and optional script tree merkle
|
||||||
|
/// root. For the [`KeyPair`] type this also tweaks the private key in the pair.
|
||||||
|
///
|
||||||
|
/// This is done by using the equation Q = P + H(P|c)G, where
|
||||||
|
/// * Q is the tweaked public key
|
||||||
|
/// * P is the internal public key
|
||||||
|
/// * H is the hash function
|
||||||
|
/// * c is the commitment data
|
||||||
|
/// * G is the generator point
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The tweaked key and its parity.
|
||||||
|
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapNodeHash>) -> Self::TweakedAux;
|
||||||
|
|
||||||
|
/// Directly converts an [`UntweakedPublicKey`] to a [`TweakedPublicKey`]
|
||||||
|
///
|
||||||
|
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
||||||
|
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
||||||
|
fn dangerous_assume_tweaked(self) -> Self::TweakedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TapTweak for UntweakedPublicKey {
|
||||||
|
type TweakedAux = (TweakedPublicKey, Parity);
|
||||||
|
type TweakedKey = TweakedPublicKey;
|
||||||
|
|
||||||
|
/// Tweaks an untweaked public key with corresponding public key value and optional script tree
|
||||||
|
/// merkle root.
|
||||||
|
///
|
||||||
|
/// This is done by using the equation Q = P + H(P|c)G, where
|
||||||
|
/// * Q is the tweaked public key
|
||||||
|
/// * P is the internal public key
|
||||||
|
/// * H is the hash function
|
||||||
|
/// * c is the commitment data
|
||||||
|
/// * G is the generator point
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The tweaked key and its parity.
|
||||||
|
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapNodeHash>) -> (TweakedPublicKey, Parity) {
|
||||||
|
let tweak = TapTweakHash::from_key_and_tweak(self, merkle_root).to_scalar();
|
||||||
|
let (output_key, parity) = self.add_tweak(secp, &tweak).expect("Tap tweak failed");
|
||||||
|
|
||||||
|
debug_assert!(self.tweak_add_check(secp, &output_key, parity, tweak));
|
||||||
|
(TweakedPublicKey(output_key), parity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dangerous_assume_tweaked(self) -> TweakedPublicKey {
|
||||||
|
TweakedPublicKey(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TapTweak for UntweakedKeyPair {
|
||||||
|
type TweakedAux = TweakedKeyPair;
|
||||||
|
type TweakedKey = TweakedKeyPair;
|
||||||
|
|
||||||
|
/// Tweaks private and public keys within an untweaked [`KeyPair`] with corresponding public key
|
||||||
|
/// value and optional script tree merkle root.
|
||||||
|
///
|
||||||
|
/// This is done by tweaking private key within the pair using the equation q = p + H(P|c), where
|
||||||
|
/// * q is the tweaked private key
|
||||||
|
/// * p is the internal private key
|
||||||
|
/// * H is the hash function
|
||||||
|
/// * c is the commitment data
|
||||||
|
/// The public key is generated from a private key by multiplying with generator point, Q = qG.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The tweaked key and its parity.
|
||||||
|
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapNodeHash>) -> TweakedKeyPair {
|
||||||
|
let (pubkey, _parity) = XOnlyPublicKey::from_keypair(&self);
|
||||||
|
let tweak = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).to_scalar();
|
||||||
|
let tweaked = self.add_xonly_tweak(secp, &tweak).expect("Tap tweak failed");
|
||||||
|
TweakedKeyPair(tweaked)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dangerous_assume_tweaked(self) -> TweakedKeyPair {
|
||||||
|
TweakedKeyPair(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TweakedPublicKey {
|
||||||
|
/// Returns the [`TweakedPublicKey`] for `keypair`.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_keypair(keypair: TweakedKeyPair) -> Self {
|
||||||
|
let (xonly, _parity) = keypair.0.x_only_public_key();
|
||||||
|
TweakedPublicKey(xonly)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`TweakedPublicKey`] from a [`XOnlyPublicKey`]. No tweak is applied, consider
|
||||||
|
/// calling `tap_tweak` on an [`UntweakedPublicKey`] instead of using this constructor.
|
||||||
|
///
|
||||||
|
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
||||||
|
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
||||||
|
#[inline]
|
||||||
|
pub fn dangerous_assume_tweaked(key: XOnlyPublicKey) -> TweakedPublicKey {
|
||||||
|
TweakedPublicKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the underlying public key.
|
||||||
|
pub fn to_inner(self) -> XOnlyPublicKey {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the key as a byte-encoded pair of values. In compressed form
|
||||||
|
/// the y-coordinate is represented by only a single bit, as x determines
|
||||||
|
/// it up to one bit.
|
||||||
|
#[inline]
|
||||||
|
pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
|
||||||
|
self.0.serialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TweakedKeyPair {
|
||||||
|
/// Creates a new [`TweakedKeyPair`] from a [`KeyPair`]. No tweak is applied, consider
|
||||||
|
/// calling `tap_tweak` on an [`UntweakedKeyPair`] instead of using this constructor.
|
||||||
|
///
|
||||||
|
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
||||||
|
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
||||||
|
#[inline]
|
||||||
|
pub fn dangerous_assume_tweaked(pair: KeyPair) -> TweakedKeyPair {
|
||||||
|
TweakedKeyPair(pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the underlying key pair.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_inner(self) -> KeyPair {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [`TweakedPublicKey`] and its [`Parity`] for this [`TweakedKeyPair`].
|
||||||
|
#[inline]
|
||||||
|
pub fn public_parts(&self) -> (TweakedPublicKey, Parity) {
|
||||||
|
let (xonly, parity) = self.0.x_only_public_key();
|
||||||
|
(TweakedPublicKey(xonly), parity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TweakedPublicKey> for XOnlyPublicKey {
|
||||||
|
#[inline]
|
||||||
|
fn from(pair: TweakedPublicKey) -> Self {
|
||||||
|
pair.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TweakedKeyPair> for KeyPair {
|
||||||
|
#[inline]
|
||||||
|
fn from(pair: TweakedKeyPair) -> Self {
|
||||||
|
pair.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TweakedKeyPair> for TweakedPublicKey {
|
||||||
|
#[inline]
|
||||||
|
fn from(pair: TweakedKeyPair) -> Self {
|
||||||
|
TweakedPublicKey::from_keypair(pair)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -8,4 +8,5 @@
|
||||||
|
|
||||||
pub mod ecdsa;
|
pub mod ecdsa;
|
||||||
pub mod key;
|
pub mod key;
|
||||||
pub mod schnorr;
|
pub mod sighash;
|
||||||
|
pub mod taproot;
|
||||||
|
|
|
@ -1,323 +0,0 @@
|
||||||
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
|
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
|
||||||
|
|
||||||
//! Schnorr Bitcoin keys.
|
|
||||||
//!
|
|
||||||
//! This module provides Schnorr keys used in Bitcoin, reexporting Secp256k1
|
|
||||||
//! Schnorr key types.
|
|
||||||
//!
|
|
||||||
|
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
use bitcoin_internals::write_err;
|
|
||||||
|
|
||||||
pub use secp256k1::{self, constants, Secp256k1, KeyPair, XOnlyPublicKey, Verification, Parity};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use crate::taproot::{TapNodeHash, TapTweakHash};
|
|
||||||
use crate::sighash::SchnorrSighashType;
|
|
||||||
|
|
||||||
/// Untweaked BIP-340 X-coord-only public key
|
|
||||||
pub type UntweakedPublicKey = XOnlyPublicKey;
|
|
||||||
|
|
||||||
/// Tweaked BIP-340 X-coord-only public key
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
|
||||||
pub struct TweakedPublicKey(XOnlyPublicKey);
|
|
||||||
|
|
||||||
impl fmt::LowerHex for TweakedPublicKey {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::LowerHex::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for TweakedPublicKey {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Untweaked BIP-340 key pair
|
|
||||||
pub type UntweakedKeyPair = KeyPair;
|
|
||||||
|
|
||||||
/// Tweaked BIP-340 key pair
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// # #[cfg(feature = "rand-std")] {
|
|
||||||
/// # use bitcoin::schnorr::{KeyPair, TweakedKeyPair, TweakedPublicKey};
|
|
||||||
/// # use bitcoin::secp256k1::{rand, Secp256k1};
|
|
||||||
/// # let secp = Secp256k1::new();
|
|
||||||
/// # let keypair = TweakedKeyPair::dangerous_assume_tweaked(KeyPair::new(&secp, &mut rand::thread_rng()));
|
|
||||||
/// // There are various conversion methods available to get a tweaked pubkey from a tweaked keypair.
|
|
||||||
/// let (_pk, _parity) = keypair.public_parts();
|
|
||||||
/// let _pk = TweakedPublicKey::from_keypair(keypair);
|
|
||||||
/// let _pk = TweakedPublicKey::from(keypair);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
|
||||||
pub struct TweakedKeyPair(KeyPair);
|
|
||||||
|
|
||||||
/// A trait for tweaking BIP340 key types (x-only public keys and key pairs).
|
|
||||||
pub trait TapTweak {
|
|
||||||
/// Tweaked key type with optional auxiliary information
|
|
||||||
type TweakedAux;
|
|
||||||
/// Tweaked key type
|
|
||||||
type TweakedKey;
|
|
||||||
|
|
||||||
/// Tweaks an untweaked key with corresponding public key value and optional script tree merkle
|
|
||||||
/// root. For the [`KeyPair`] type this also tweaks the private key in the pair.
|
|
||||||
///
|
|
||||||
/// This is done by using the equation Q = P + H(P|c)G, where
|
|
||||||
/// * Q is the tweaked public key
|
|
||||||
/// * P is the internal public key
|
|
||||||
/// * H is the hash function
|
|
||||||
/// * c is the commitment data
|
|
||||||
/// * G is the generator point
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The tweaked key and its parity.
|
|
||||||
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapNodeHash>) -> Self::TweakedAux;
|
|
||||||
|
|
||||||
/// Directly converts an [`UntweakedPublicKey`] to a [`TweakedPublicKey`]
|
|
||||||
///
|
|
||||||
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
|
||||||
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
|
||||||
fn dangerous_assume_tweaked(self) -> Self::TweakedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TapTweak for UntweakedPublicKey {
|
|
||||||
type TweakedAux = (TweakedPublicKey, Parity);
|
|
||||||
type TweakedKey = TweakedPublicKey;
|
|
||||||
|
|
||||||
/// Tweaks an untweaked public key with corresponding public key value and optional script tree
|
|
||||||
/// merkle root.
|
|
||||||
///
|
|
||||||
/// This is done by using the equation Q = P + H(P|c)G, where
|
|
||||||
/// * Q is the tweaked public key
|
|
||||||
/// * P is the internal public key
|
|
||||||
/// * H is the hash function
|
|
||||||
/// * c is the commitment data
|
|
||||||
/// * G is the generator point
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The tweaked key and its parity.
|
|
||||||
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapNodeHash>) -> (TweakedPublicKey, Parity) {
|
|
||||||
let tweak = TapTweakHash::from_key_and_tweak(self, merkle_root).to_scalar();
|
|
||||||
let (output_key, parity) = self.add_tweak(secp, &tweak).expect("Tap tweak failed");
|
|
||||||
|
|
||||||
debug_assert!(self.tweak_add_check(secp, &output_key, parity, tweak));
|
|
||||||
(TweakedPublicKey(output_key), parity)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dangerous_assume_tweaked(self) -> TweakedPublicKey {
|
|
||||||
TweakedPublicKey(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TapTweak for UntweakedKeyPair {
|
|
||||||
type TweakedAux = TweakedKeyPair;
|
|
||||||
type TweakedKey = TweakedKeyPair;
|
|
||||||
|
|
||||||
/// Tweaks private and public keys within an untweaked [`KeyPair`] with corresponding public key
|
|
||||||
/// value and optional script tree merkle root.
|
|
||||||
///
|
|
||||||
/// This is done by tweaking private key within the pair using the equation q = p + H(P|c), where
|
|
||||||
/// * q is the tweaked private key
|
|
||||||
/// * p is the internal private key
|
|
||||||
/// * H is the hash function
|
|
||||||
/// * c is the commitment data
|
|
||||||
/// The public key is generated from a private key by multiplying with generator point, Q = qG.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The tweaked key and its parity.
|
|
||||||
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapNodeHash>) -> TweakedKeyPair {
|
|
||||||
let (pubkey, _parity) = XOnlyPublicKey::from_keypair(&self);
|
|
||||||
let tweak = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).to_scalar();
|
|
||||||
let tweaked = self.add_xonly_tweak(secp, &tweak).expect("Tap tweak failed");
|
|
||||||
TweakedKeyPair(tweaked)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dangerous_assume_tweaked(self) -> TweakedKeyPair {
|
|
||||||
TweakedKeyPair(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TweakedPublicKey {
|
|
||||||
/// Returns the [`TweakedPublicKey`] for `keypair`.
|
|
||||||
#[inline]
|
|
||||||
pub fn from_keypair(keypair: TweakedKeyPair) -> Self {
|
|
||||||
let (xonly, _parity) = keypair.0.x_only_public_key();
|
|
||||||
TweakedPublicKey(xonly)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`TweakedPublicKey`] from a [`XOnlyPublicKey`]. No tweak is applied, consider
|
|
||||||
/// calling `tap_tweak` on an [`UntweakedPublicKey`] instead of using this constructor.
|
|
||||||
///
|
|
||||||
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
|
||||||
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
|
||||||
#[inline]
|
|
||||||
pub fn dangerous_assume_tweaked(key: XOnlyPublicKey) -> TweakedPublicKey {
|
|
||||||
TweakedPublicKey(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the underlying public key.
|
|
||||||
pub fn to_inner(self) -> XOnlyPublicKey {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize the key as a byte-encoded pair of values. In compressed form
|
|
||||||
/// the y-coordinate is represented by only a single bit, as x determines
|
|
||||||
/// it up to one bit.
|
|
||||||
#[inline]
|
|
||||||
pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
|
|
||||||
self.0.serialize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TweakedKeyPair {
|
|
||||||
/// Creates a new [`TweakedKeyPair`] from a [`KeyPair`]. No tweak is applied, consider
|
|
||||||
/// calling `tap_tweak` on an [`UntweakedKeyPair`] instead of using this constructor.
|
|
||||||
///
|
|
||||||
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
|
||||||
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
|
||||||
#[inline]
|
|
||||||
pub fn dangerous_assume_tweaked(pair: KeyPair) -> TweakedKeyPair {
|
|
||||||
TweakedKeyPair(pair)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the underlying key pair.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_inner(self) -> KeyPair {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [`TweakedPublicKey`] and its [`Parity`] for this [`TweakedKeyPair`].
|
|
||||||
#[inline]
|
|
||||||
pub fn public_parts(&self) -> (TweakedPublicKey, Parity) {
|
|
||||||
let (xonly, parity) = self.0.x_only_public_key();
|
|
||||||
(TweakedPublicKey(xonly), parity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TweakedPublicKey> for XOnlyPublicKey {
|
|
||||||
#[inline]
|
|
||||||
fn from(pair: TweakedPublicKey) -> Self {
|
|
||||||
pair.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TweakedKeyPair> for KeyPair {
|
|
||||||
#[inline]
|
|
||||||
fn from(pair: TweakedKeyPair) -> Self {
|
|
||||||
pair.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TweakedKeyPair> for TweakedPublicKey {
|
|
||||||
#[inline]
|
|
||||||
fn from(pair: TweakedKeyPair) -> Self {
|
|
||||||
TweakedPublicKey::from_keypair(pair)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A BIP340-341 serialized schnorr signature with the corresponding hash type.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
|
||||||
pub struct Signature {
|
|
||||||
/// The underlying schnorr signature
|
|
||||||
pub sig: secp256k1::schnorr::Signature,
|
|
||||||
/// The corresponding hash type
|
|
||||||
pub hash_ty: SchnorrSighashType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signature {
|
|
||||||
/// Deserialize from slice
|
|
||||||
pub fn from_slice(sl: &[u8]) -> Result<Self, Error> {
|
|
||||||
match sl.len() {
|
|
||||||
64 => {
|
|
||||||
// default type
|
|
||||||
let sig = secp256k1::schnorr::Signature::from_slice(sl)
|
|
||||||
.map_err(Error::Secp256k1)?;
|
|
||||||
Ok(Signature { sig, hash_ty: SchnorrSighashType::Default })
|
|
||||||
},
|
|
||||||
65 => {
|
|
||||||
let (hash_ty, sig) = sl.split_last().expect("Slice len checked == 65");
|
|
||||||
let hash_ty = SchnorrSighashType::from_consensus_u8(*hash_ty)
|
|
||||||
.map_err(|_| Error::InvalidSighashType(*hash_ty))?;
|
|
||||||
let sig = secp256k1::schnorr::Signature::from_slice(sig)
|
|
||||||
.map_err(Error::Secp256k1)?;
|
|
||||||
Ok(Signature { sig, hash_ty })
|
|
||||||
}
|
|
||||||
len => {
|
|
||||||
Err(Error::InvalidSignatureSize(len))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize Signature
|
|
||||||
pub fn to_vec(self) -> Vec<u8> {
|
|
||||||
// TODO: add support to serialize to a writer to SerializedSig
|
|
||||||
let mut ser_sig = self.sig.as_ref().to_vec();
|
|
||||||
if self.hash_ty == SchnorrSighashType::Default {
|
|
||||||
// default sighash type, don't add extra sighash byte
|
|
||||||
} else {
|
|
||||||
ser_sig.push(self.hash_ty as u8);
|
|
||||||
}
|
|
||||||
ser_sig
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A schnorr sig related error.
|
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum Error {
|
|
||||||
/// Base58 encoding error
|
|
||||||
InvalidSighashType(u8),
|
|
||||||
/// Signature has valid size but does not parse correctly
|
|
||||||
Secp256k1(secp256k1::Error),
|
|
||||||
/// Invalid schnorr signature size
|
|
||||||
InvalidSignatureSize(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Error::InvalidSighashType(hash_ty) =>
|
|
||||||
write!(f, "Invalid signature hash type {}", hash_ty),
|
|
||||||
Error::Secp256k1(ref e) =>
|
|
||||||
write_err!(f, "Schnorr signature has correct len but is malformed"; e),
|
|
||||||
Error::InvalidSignatureSize(sz) =>
|
|
||||||
write!(f, "Invalid Schnorr signature size: {}", sz),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
|
||||||
impl std::error::Error for Error {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
use self::Error::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Secp256k1(e) => Some(e),
|
|
||||||
InvalidSighashType(_) | InvalidSignatureSize(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<secp256k1::Error> for Error {
|
|
||||||
|
|
||||||
fn from(e: secp256k1::Error) -> Error {
|
|
||||||
Error::Secp256k1(e)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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, TapSighashHash, 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>> {
|
||||||
|
@ -77,7 +109,7 @@ struct TaprootCache {
|
||||||
script_pubkeys: sha256::Hash,
|
script_pubkeys: sha256::Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains outputs of previous transactions. In the case [`SchnorrSighashType`] variant is
|
/// Contains outputs of previous transactions. In the case [`TapSighashType`] variant is
|
||||||
/// `SIGHASH_ANYONECANPAY`, [`Prevouts::One`] may be used.
|
/// `SIGHASH_ANYONECANPAY`, [`Prevouts::One`] may be used.
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum Prevouts<'u, T>
|
pub enum Prevouts<'u, T>
|
||||||
|
@ -107,8 +139,8 @@ pub struct ScriptPath<'s> {
|
||||||
/// Hashtype of an input's signature, encoded in the last byte of the signature.
|
/// Hashtype of an input's signature, encoded in the last byte of the signature.
|
||||||
/// Fixed values so they can be cast as integer types for encoding.
|
/// Fixed values so they can be cast as integer types for encoding.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum SchnorrSighashType {
|
pub enum TapSighashType {
|
||||||
/// 0x0: Used when not explicitly specified, defaults to [`SchnorrSighashType::All`]
|
/// 0x0: Used when not explicitly specified, defaults to [`TapSighashType::All`]
|
||||||
Default = 0x00,
|
Default = 0x00,
|
||||||
/// 0x1: Sign all outputs.
|
/// 0x1: Sign all outputs.
|
||||||
All = 0x01,
|
All = 0x01,
|
||||||
|
@ -127,35 +159,39 @@ pub enum SchnorrSighashType {
|
||||||
SinglePlusAnyoneCanPay = 0x83,
|
SinglePlusAnyoneCanPay = 0x83,
|
||||||
}
|
}
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
crate::serde_utils::serde_string_impl!(SchnorrSighashType, "a SchnorrSighashType data");
|
crate::serde_utils::serde_string_impl!(TapSighashType, "a TapSighashType data");
|
||||||
|
|
||||||
impl fmt::Display for SchnorrSighashType {
|
impl fmt::Display for TapSighashType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use TapSighashType::*;
|
||||||
|
|
||||||
let s = match self {
|
let s = match self {
|
||||||
SchnorrSighashType::Default => "SIGHASH_DEFAULT",
|
Default => "SIGHASH_DEFAULT",
|
||||||
SchnorrSighashType::All => "SIGHASH_ALL",
|
All => "SIGHASH_ALL",
|
||||||
SchnorrSighashType::None => "SIGHASH_NONE",
|
None => "SIGHASH_NONE",
|
||||||
SchnorrSighashType::Single => "SIGHASH_SINGLE",
|
Single => "SIGHASH_SINGLE",
|
||||||
SchnorrSighashType::AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
|
AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
|
||||||
SchnorrSighashType::NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
|
NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
|
||||||
SchnorrSighashType::SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
|
SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
|
||||||
};
|
};
|
||||||
f.write_str(s)
|
f.write_str(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl str::FromStr for SchnorrSighashType {
|
impl str::FromStr for TapSighashType {
|
||||||
type Err = SighashTypeParseError;
|
type Err = SighashTypeParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
use TapSighashType::*;
|
||||||
|
|
||||||
match s {
|
match s {
|
||||||
"SIGHASH_DEFAULT" => Ok(SchnorrSighashType::Default),
|
"SIGHASH_DEFAULT" => Ok(Default),
|
||||||
"SIGHASH_ALL" => Ok(SchnorrSighashType::All),
|
"SIGHASH_ALL" => Ok(All),
|
||||||
"SIGHASH_NONE" => Ok(SchnorrSighashType::None),
|
"SIGHASH_NONE" => Ok(None),
|
||||||
"SIGHASH_SINGLE" => Ok(SchnorrSighashType::Single),
|
"SIGHASH_SINGLE" => Ok(Single),
|
||||||
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::AllPlusAnyoneCanPay),
|
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(AllPlusAnyoneCanPay),
|
||||||
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::NonePlusAnyoneCanPay),
|
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(NonePlusAnyoneCanPay),
|
||||||
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SchnorrSighashType::SinglePlusAnyoneCanPay),
|
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SinglePlusAnyoneCanPay),
|
||||||
_ => Err(SighashTypeParseError { unrecognized: s.to_owned() }),
|
_ => Err(SighashTypeParseError { unrecognized: s.to_owned() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,15 +243,17 @@ pub enum Error {
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Error::Io(error_kind) => write!(f, "writer errored: {:?}", error_kind),
|
Io(error_kind) => write!(f, "writer errored: {:?}", error_kind),
|
||||||
Error::IndexOutOfInputsBounds { index, inputs_size } => write!(f, "Requested index ({}) is greater or equal than the number of transaction inputs ({})", index, inputs_size),
|
IndexOutOfInputsBounds { index, inputs_size } => write!(f, "Requested index ({}) is greater or equal than the number of transaction inputs ({})", index, inputs_size),
|
||||||
Error::SingleWithoutCorrespondingOutput { index, outputs_size } => write!(f, "SIGHASH_SINGLE for input ({}) haven't a corresponding output (#outputs:{})", index, outputs_size),
|
SingleWithoutCorrespondingOutput { index, outputs_size } => write!(f, "SIGHASH_SINGLE for input ({}) haven't a corresponding output (#outputs:{})", index, outputs_size),
|
||||||
Error::PrevoutsSize => write!(f, "Number of supplied prevouts differs from the number of inputs in transaction"),
|
PrevoutsSize => write!(f, "Number of supplied prevouts differs from the number of inputs in transaction"),
|
||||||
Error::PrevoutIndex => write!(f, "The index requested is greater than available prevouts or different from the provided [Provided::Anyone] index"),
|
PrevoutIndex => write!(f, "The index requested is greater than available prevouts or different from the provided [Provided::Anyone] index"),
|
||||||
Error::PrevoutKind => write!(f, "A single prevout has been provided but all prevouts are needed without `ANYONECANPAY`"),
|
PrevoutKind => write!(f, "A single prevout has been provided but all prevouts are needed without `ANYONECANPAY`"),
|
||||||
Error::WrongAnnex => write!(f, "Annex must be at least one byte long and the first bytes must be `0x50`"),
|
WrongAnnex => write!(f, "Annex must be at least one byte long and the first bytes must be `0x50`"),
|
||||||
Error::InvalidSighashType(hash_ty) => write!(f, "Invalid schnorr Signature hash type : {} ", hash_ty),
|
InvalidSighashType(hash_ty) => write!(f, "Invalid taproot signature hash type : {} ", hash_ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,7 +262,7 @@ impl fmt::Display for Error {
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
impl std::error::Error for Error {
|
impl std::error::Error for Error {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
use self::Error::*;
|
use Error::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Io(_)
|
Io(_)
|
||||||
|
@ -301,7 +339,7 @@ impl<'s> From<ScriptPath<'s>> for TapLeafHash {
|
||||||
/// Hashtype of an input's signature, encoded in the last byte of the signature.
|
/// Hashtype of an input's signature, encoded in the last byte of the signature.
|
||||||
///
|
///
|
||||||
/// Fixed values so they can be cast as integer types for encoding (see also
|
/// Fixed values so they can be cast as integer types for encoding (see also
|
||||||
/// [`SchnorrSighashType`]).
|
/// [`TapSighashType`]).
|
||||||
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
|
||||||
pub enum EcdsaSighashType {
|
pub enum EcdsaSighashType {
|
||||||
/// 0x1: Sign all outputs.
|
/// 0x1: Sign all outputs.
|
||||||
|
@ -325,13 +363,15 @@ crate::serde_utils::serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType dat
|
||||||
|
|
||||||
impl fmt::Display for EcdsaSighashType {
|
impl fmt::Display for EcdsaSighashType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use EcdsaSighashType::*;
|
||||||
|
|
||||||
let s = match self {
|
let s = match self {
|
||||||
EcdsaSighashType::All => "SIGHASH_ALL",
|
All => "SIGHASH_ALL",
|
||||||
EcdsaSighashType::None => "SIGHASH_NONE",
|
None => "SIGHASH_NONE",
|
||||||
EcdsaSighashType::Single => "SIGHASH_SINGLE",
|
Single => "SIGHASH_SINGLE",
|
||||||
EcdsaSighashType::AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
|
AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
|
||||||
EcdsaSighashType::NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
|
NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
|
||||||
EcdsaSighashType::SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
|
SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
|
||||||
};
|
};
|
||||||
f.write_str(s)
|
f.write_str(s)
|
||||||
}
|
}
|
||||||
|
@ -341,13 +381,15 @@ impl str::FromStr for EcdsaSighashType {
|
||||||
type Err = SighashTypeParseError;
|
type Err = SighashTypeParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
use EcdsaSighashType::*;
|
||||||
|
|
||||||
match s {
|
match s {
|
||||||
"SIGHASH_ALL" => Ok(EcdsaSighashType::All),
|
"SIGHASH_ALL" => Ok(All),
|
||||||
"SIGHASH_NONE" => Ok(EcdsaSighashType::None),
|
"SIGHASH_NONE" => Ok(None),
|
||||||
"SIGHASH_SINGLE" => Ok(EcdsaSighashType::Single),
|
"SIGHASH_SINGLE" => Ok(Single),
|
||||||
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(EcdsaSighashType::AllPlusAnyoneCanPay),
|
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(AllPlusAnyoneCanPay),
|
||||||
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(EcdsaSighashType::NonePlusAnyoneCanPay),
|
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(NonePlusAnyoneCanPay),
|
||||||
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay),
|
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SinglePlusAnyoneCanPay),
|
||||||
_ => Err(SighashTypeParseError { unrecognized: s.to_owned() }),
|
_ => Err(SighashTypeParseError { unrecognized: s.to_owned() }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,13 +398,15 @@ impl str::FromStr for EcdsaSighashType {
|
||||||
impl EcdsaSighashType {
|
impl EcdsaSighashType {
|
||||||
/// Splits the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean.
|
/// Splits the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean.
|
||||||
pub(crate) fn split_anyonecanpay_flag(self) -> (EcdsaSighashType, bool) {
|
pub(crate) fn split_anyonecanpay_flag(self) -> (EcdsaSighashType, bool) {
|
||||||
|
use EcdsaSighashType::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
EcdsaSighashType::All => (EcdsaSighashType::All, false),
|
All => (All, false),
|
||||||
EcdsaSighashType::None => (EcdsaSighashType::None, false),
|
None => (None, false),
|
||||||
EcdsaSighashType::Single => (EcdsaSighashType::Single, false),
|
Single => (Single, false),
|
||||||
EcdsaSighashType::AllPlusAnyoneCanPay => (EcdsaSighashType::All, true),
|
AllPlusAnyoneCanPay => (All, true),
|
||||||
EcdsaSighashType::NonePlusAnyoneCanPay => (EcdsaSighashType::None, true),
|
NonePlusAnyoneCanPay => (None, true),
|
||||||
EcdsaSighashType::SinglePlusAnyoneCanPay => (EcdsaSighashType::Single, true),
|
SinglePlusAnyoneCanPay => (Single, true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,6 +420,8 @@ impl EcdsaSighashType {
|
||||||
/// verifying signatures, the user should retain the `n` and use it compute the signature hash
|
/// verifying signatures, the user should retain the `n` and use it compute the signature hash
|
||||||
/// message.
|
/// message.
|
||||||
pub fn from_consensus(n: u32) -> EcdsaSighashType {
|
pub fn from_consensus(n: u32) -> EcdsaSighashType {
|
||||||
|
use EcdsaSighashType::*;
|
||||||
|
|
||||||
// In Bitcoin Core, the SignatureHash function will mask the (int32) value with
|
// In Bitcoin Core, the SignatureHash function will mask the (int32) value with
|
||||||
// 0x1f to (apparently) deactivate ACP when checking for SINGLE and NONE bits.
|
// 0x1f to (apparently) deactivate ACP when checking for SINGLE and NONE bits.
|
||||||
// We however want to be matching also against on ACP-masked ALL, SINGLE, and NONE.
|
// We however want to be matching also against on ACP-masked ALL, SINGLE, and NONE.
|
||||||
|
@ -383,15 +429,15 @@ impl EcdsaSighashType {
|
||||||
let mask = 0x1f | 0x80;
|
let mask = 0x1f | 0x80;
|
||||||
match n & mask {
|
match n & mask {
|
||||||
// "real" sighashes
|
// "real" sighashes
|
||||||
0x01 => EcdsaSighashType::All,
|
0x01 => All,
|
||||||
0x02 => EcdsaSighashType::None,
|
0x02 => None,
|
||||||
0x03 => EcdsaSighashType::Single,
|
0x03 => Single,
|
||||||
0x81 => EcdsaSighashType::AllPlusAnyoneCanPay,
|
0x81 => AllPlusAnyoneCanPay,
|
||||||
0x82 => EcdsaSighashType::NonePlusAnyoneCanPay,
|
0x82 => NonePlusAnyoneCanPay,
|
||||||
0x83 => EcdsaSighashType::SinglePlusAnyoneCanPay,
|
0x83 => SinglePlusAnyoneCanPay,
|
||||||
// catchalls
|
// catchalls
|
||||||
x if x & 0x80 == 0x80 => EcdsaSighashType::AllPlusAnyoneCanPay,
|
x if x & 0x80 == 0x80 => AllPlusAnyoneCanPay,
|
||||||
_ => EcdsaSighashType::All,
|
_ => All,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,14 +447,16 @@ impl EcdsaSighashType {
|
||||||
///
|
///
|
||||||
/// If `n` is a non-standard sighash value.
|
/// If `n` is a non-standard sighash value.
|
||||||
pub fn from_standard(n: u32) -> Result<EcdsaSighashType, NonStandardSighashType> {
|
pub fn from_standard(n: u32) -> Result<EcdsaSighashType, NonStandardSighashType> {
|
||||||
|
use EcdsaSighashType::*;
|
||||||
|
|
||||||
match n {
|
match n {
|
||||||
// Standard sighashes, see https://github.com/bitcoin/bitcoin/blob/b805dbb0b9c90dadef0424e5b3bf86ac308e103e/src/script/interpreter.cpp#L189-L198
|
// Standard sighashes, see https://github.com/bitcoin/bitcoin/blob/b805dbb0b9c90dadef0424e5b3bf86ac308e103e/src/script/interpreter.cpp#L189-L198
|
||||||
0x01 => Ok(EcdsaSighashType::All),
|
0x01 => Ok(All),
|
||||||
0x02 => Ok(EcdsaSighashType::None),
|
0x02 => Ok(None),
|
||||||
0x03 => Ok(EcdsaSighashType::Single),
|
0x03 => Ok(Single),
|
||||||
0x81 => Ok(EcdsaSighashType::AllPlusAnyoneCanPay),
|
0x81 => Ok(AllPlusAnyoneCanPay),
|
||||||
0x82 => Ok(EcdsaSighashType::NonePlusAnyoneCanPay),
|
0x82 => Ok(NonePlusAnyoneCanPay),
|
||||||
0x83 => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay),
|
0x83 => Ok(SinglePlusAnyoneCanPay),
|
||||||
non_standard => Err(NonStandardSighashType(non_standard)),
|
non_standard => Err(NonStandardSighashType(non_standard)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,36 +467,40 @@ impl EcdsaSighashType {
|
||||||
pub fn to_u32(self) -> u32 { self as u32 }
|
pub fn to_u32(self) -> u32 { self as u32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EcdsaSighashType> for SchnorrSighashType {
|
impl From<EcdsaSighashType> for TapSighashType {
|
||||||
fn from(s: EcdsaSighashType) -> Self {
|
fn from(s: EcdsaSighashType) -> Self {
|
||||||
|
use TapSighashType::*;
|
||||||
|
|
||||||
match s {
|
match s {
|
||||||
EcdsaSighashType::All => SchnorrSighashType::All,
|
EcdsaSighashType::All => All,
|
||||||
EcdsaSighashType::None => SchnorrSighashType::None,
|
EcdsaSighashType::None => None,
|
||||||
EcdsaSighashType::Single => SchnorrSighashType::Single,
|
EcdsaSighashType::Single => Single,
|
||||||
EcdsaSighashType::AllPlusAnyoneCanPay => SchnorrSighashType::AllPlusAnyoneCanPay,
|
EcdsaSighashType::AllPlusAnyoneCanPay => AllPlusAnyoneCanPay,
|
||||||
EcdsaSighashType::NonePlusAnyoneCanPay => SchnorrSighashType::NonePlusAnyoneCanPay,
|
EcdsaSighashType::NonePlusAnyoneCanPay => NonePlusAnyoneCanPay,
|
||||||
EcdsaSighashType::SinglePlusAnyoneCanPay => SchnorrSighashType::SinglePlusAnyoneCanPay,
|
EcdsaSighashType::SinglePlusAnyoneCanPay => SinglePlusAnyoneCanPay,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchnorrSighashType {
|
impl TapSighashType {
|
||||||
/// Breaks the sighash flag into the "real" sighash flag and the `SIGHASH_ANYONECANPAY` boolean.
|
/// Breaks the sighash flag into the "real" sighash flag and the `SIGHASH_ANYONECANPAY` boolean.
|
||||||
pub(crate) fn split_anyonecanpay_flag(self) -> (SchnorrSighashType, bool) {
|
pub(crate) fn split_anyonecanpay_flag(self) -> (TapSighashType, bool) {
|
||||||
|
use TapSighashType::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
SchnorrSighashType::Default => (SchnorrSighashType::Default, false),
|
Default => (Default, false),
|
||||||
SchnorrSighashType::All => (SchnorrSighashType::All, false),
|
All => (All, false),
|
||||||
SchnorrSighashType::None => (SchnorrSighashType::None, false),
|
None => (None, false),
|
||||||
SchnorrSighashType::Single => (SchnorrSighashType::Single, false),
|
Single => (Single, false),
|
||||||
SchnorrSighashType::AllPlusAnyoneCanPay => (SchnorrSighashType::All, true),
|
AllPlusAnyoneCanPay => (All, true),
|
||||||
SchnorrSighashType::NonePlusAnyoneCanPay => (SchnorrSighashType::None, true),
|
NonePlusAnyoneCanPay => (None, true),
|
||||||
SchnorrSighashType::SinglePlusAnyoneCanPay => (SchnorrSighashType::Single, true),
|
SinglePlusAnyoneCanPay => (Single, true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a [`SchnorrSighashType`] from a raw `u8`.
|
/// Constructs a [`TapSighashType`] from a raw `u8`.
|
||||||
pub fn from_consensus_u8(hash_ty: u8) -> Result<Self, Error> {
|
pub fn from_consensus_u8(hash_ty: u8) -> Result<Self, Error> {
|
||||||
use SchnorrSighashType::*;
|
use TapSighashType::*;
|
||||||
|
|
||||||
Ok(match hash_ty {
|
Ok(match hash_ty {
|
||||||
0x00 => Default,
|
0x00 => Default,
|
||||||
|
@ -522,7 +574,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
prevouts: &Prevouts<T>,
|
prevouts: &Prevouts<T>,
|
||||||
annex: Option<Annex>,
|
annex: Option<Annex>,
|
||||||
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
|
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
|
||||||
sighash_type: SchnorrSighashType,
|
sighash_type: TapSighashType,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
prevouts.check_all(self.tx.borrow())?;
|
prevouts.check_all(self.tx.borrow())?;
|
||||||
|
|
||||||
|
@ -556,7 +608,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
|
|
||||||
// If hash_type & 3 does not equal SIGHASH_NONE or SIGHASH_SINGLE:
|
// If hash_type & 3 does not equal SIGHASH_NONE or SIGHASH_SINGLE:
|
||||||
// sha_outputs (32): the SHA256 of the serialization of all outputs in CTxOut format.
|
// sha_outputs (32): the SHA256 of the serialization of all outputs in CTxOut format.
|
||||||
if sighash != SchnorrSighashType::None && sighash != SchnorrSighashType::Single {
|
if sighash != TapSighashType::None && sighash != TapSighashType::Single {
|
||||||
self.common_cache().outputs.consensus_encode(&mut writer)?;
|
self.common_cache().outputs.consensus_encode(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,7 +656,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
// * Data about this output:
|
// * Data about this output:
|
||||||
// If hash_type & 3 equals SIGHASH_SINGLE:
|
// If hash_type & 3 equals SIGHASH_SINGLE:
|
||||||
// sha_single_output (32): the SHA256 of the corresponding output in CTxOut format.
|
// sha_single_output (32): the SHA256 of the corresponding output in CTxOut format.
|
||||||
if sighash == SchnorrSighashType::Single {
|
if sighash == TapSighashType::Single {
|
||||||
let mut enc = sha256::Hash::engine();
|
let mut enc = sha256::Hash::engine();
|
||||||
self.tx
|
self.tx
|
||||||
.borrow()
|
.borrow()
|
||||||
|
@ -639,9 +691,9 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
prevouts: &Prevouts<T>,
|
prevouts: &Prevouts<T>,
|
||||||
annex: Option<Annex>,
|
annex: Option<Annex>,
|
||||||
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
|
leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
|
||||||
sighash_type: SchnorrSighashType,
|
sighash_type: TapSighashType,
|
||||||
) -> Result<TapSighashHash, Error> {
|
) -> Result<TapSighash, Error> {
|
||||||
let mut enc = TapSighashHash::engine();
|
let mut enc = TapSighash::engine();
|
||||||
self.taproot_encode_signing_data_to(
|
self.taproot_encode_signing_data_to(
|
||||||
&mut enc,
|
&mut enc,
|
||||||
input_index,
|
input_index,
|
||||||
|
@ -650,7 +702,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
leaf_hash_code_separator,
|
leaf_hash_code_separator,
|
||||||
sighash_type,
|
sighash_type,
|
||||||
)?;
|
)?;
|
||||||
Ok(TapSighashHash::from_engine(enc))
|
Ok(TapSighash::from_engine(enc))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the BIP341 sighash for a key spend.
|
/// Computes the BIP341 sighash for a key spend.
|
||||||
|
@ -658,9 +710,9 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
&mut self,
|
&mut self,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
prevouts: &Prevouts<T>,
|
prevouts: &Prevouts<T>,
|
||||||
sighash_type: SchnorrSighashType,
|
sighash_type: TapSighashType,
|
||||||
) -> Result<TapSighashHash, Error> {
|
) -> Result<TapSighash, Error> {
|
||||||
let mut enc = TapSighashHash::engine();
|
let mut enc = TapSighash::engine();
|
||||||
self.taproot_encode_signing_data_to(
|
self.taproot_encode_signing_data_to(
|
||||||
&mut enc,
|
&mut enc,
|
||||||
input_index,
|
input_index,
|
||||||
|
@ -669,7 +721,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
None,
|
None,
|
||||||
sighash_type,
|
sighash_type,
|
||||||
)?;
|
)?;
|
||||||
Ok(TapSighashHash::from_engine(enc))
|
Ok(TapSighash::from_engine(enc))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the BIP341 sighash for a script spend.
|
/// Computes the BIP341 sighash for a script spend.
|
||||||
|
@ -681,9 +733,9 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
prevouts: &Prevouts<T>,
|
prevouts: &Prevouts<T>,
|
||||||
leaf_hash: S,
|
leaf_hash: S,
|
||||||
sighash_type: SchnorrSighashType,
|
sighash_type: TapSighashType,
|
||||||
) -> Result<TapSighashHash, Error> {
|
) -> Result<TapSighash, Error> {
|
||||||
let mut enc = TapSighashHash::engine();
|
let mut enc = TapSighash::engine();
|
||||||
self.taproot_encode_signing_data_to(
|
self.taproot_encode_signing_data_to(
|
||||||
&mut enc,
|
&mut enc,
|
||||||
input_index,
|
input_index,
|
||||||
|
@ -692,7 +744,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
|
||||||
Some((leaf_hash.into(), 0xFFFFFFFF)),
|
Some((leaf_hash.into(), 0xFFFFFFFF)),
|
||||||
sighash_type,
|
sighash_type,
|
||||||
)?;
|
)?;
|
||||||
Ok(TapSighashHash::from_engine(enc))
|
Ok(TapSighash::from_engine(enc))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encodes the BIP143 signing data for any flag type into a given object implementing a
|
/// Encodes the BIP143 signing data for any flag type into a given object implementing a
|
||||||
|
@ -741,9 +793,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 +813,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 +822,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 +982,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 +1120,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, TapSighashHash};
|
use crate::taproot::TapLeafHash;
|
||||||
|
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
|
@ -1092,7 +1144,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 +1167,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();
|
||||||
|
@ -1126,7 +1178,7 @@ mod tests {
|
||||||
// These test vectors were stolen from libbtc, which is Copyright 2014 Jonas Schnelli MIT
|
// These test vectors were stolen from libbtc, which is Copyright 2014 Jonas Schnelli MIT
|
||||||
// They were transformed by replacing {...} with run_test_sighash(...), then the ones containing
|
// They were transformed by replacing {...} with run_test_sighash(...), then the ones containing
|
||||||
// OP_CODESEPARATOR in their pubkeys were removed
|
// OP_CODESEPARATOR in their pubkeys were removed
|
||||||
let data = include_str!("../tests/data/legacy_sighash.json");
|
let data = include_str!("../../tests/data/legacy_sighash.json");
|
||||||
|
|
||||||
let testdata = serde_json::from_str::<Value>(data).unwrap().as_array().unwrap().clone();
|
let testdata = serde_json::from_str::<Value>(data).unwrap().as_array().unwrap().clone();
|
||||||
for t in testdata.iter().skip(1) {
|
for t in testdata.iter().skip(1) {
|
||||||
|
@ -1144,9 +1196,9 @@ mod tests {
|
||||||
let bytes = hex!("00011b96877db45ffa23b307e9f0ac87b80ef9a80b4c5f0db3fbe734422453e83cc5576f3d542c5d4898fb2b696c15d43332534a7c1d1255fda38993545882df92c3e353ff6d36fbfadc4d168452afd8467f02fe53d71714fcea5dfe2ea759bd00185c4cb02bc76d42620393ca358a1a713f4997f9fc222911890afb3fe56c6a19b202df7bffdcfad08003821294279043746631b00e2dc5e52a111e213bbfe6ef09a19428d418dab0d50000000000");
|
let bytes = hex!("00011b96877db45ffa23b307e9f0ac87b80ef9a80b4c5f0db3fbe734422453e83cc5576f3d542c5d4898fb2b696c15d43332534a7c1d1255fda38993545882df92c3e353ff6d36fbfadc4d168452afd8467f02fe53d71714fcea5dfe2ea759bd00185c4cb02bc76d42620393ca358a1a713f4997f9fc222911890afb3fe56c6a19b202df7bffdcfad08003821294279043746631b00e2dc5e52a111e213bbfe6ef09a19428d418dab0d50000000000");
|
||||||
let expected =
|
let expected =
|
||||||
hex!("04e808aad07a40b3767a1442fead79af6ef7e7c9316d82dec409bb31e77699b0");
|
hex!("04e808aad07a40b3767a1442fead79af6ef7e7c9316d82dec409bb31e77699b0");
|
||||||
let mut enc = TapSighashHash::engine();
|
let mut enc = TapSighash::engine();
|
||||||
enc.input(&bytes);
|
enc.input(&bytes);
|
||||||
let hash = TapSighashHash::from_engine(enc);
|
let hash = TapSighash::from_engine(enc);
|
||||||
assert_eq!(expected, hash.into_inner());
|
assert_eq!(expected, hash.into_inner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1159,7 +1211,7 @@ mod tests {
|
||||||
"01365724000000000023542156b39dab4f8f3508e0432cfb41fab110170acaa2d4c42539cb90a4dc7c093bc500",
|
"01365724000000000023542156b39dab4f8f3508e0432cfb41fab110170acaa2d4c42539cb90a4dc7c093bc500",
|
||||||
0,
|
0,
|
||||||
"33ca0ebfb4a945eeee9569fc0f5040221275f88690b7f8592ada88ce3bdf6703",
|
"33ca0ebfb4a945eeee9569fc0f5040221275f88690b7f8592ada88ce3bdf6703",
|
||||||
SchnorrSighashType::Default, None, None, None
|
TapSighashType::Default, None, None, None
|
||||||
);
|
);
|
||||||
|
|
||||||
test_taproot_sighash(
|
test_taproot_sighash(
|
||||||
|
@ -1167,7 +1219,7 @@ mod tests {
|
||||||
"02591f220000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece48fb310000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece",
|
"02591f220000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece48fb310000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece",
|
||||||
1,
|
1,
|
||||||
"626ab955d58c9a8a600a0c580549d06dc7da4e802eb2a531f62a588e430967a8",
|
"626ab955d58c9a8a600a0c580549d06dc7da4e802eb2a531f62a588e430967a8",
|
||||||
SchnorrSighashType::All, None, None, None
|
TapSighashType::All, None, None, None
|
||||||
);
|
);
|
||||||
|
|
||||||
test_taproot_sighash(
|
test_taproot_sighash(
|
||||||
|
@ -1175,7 +1227,7 @@ mod tests {
|
||||||
"01c4811000000000002251201bf9297d0a2968ae6693aadd0fa514717afefd218087a239afb7418e2d22e65c",
|
"01c4811000000000002251201bf9297d0a2968ae6693aadd0fa514717afefd218087a239afb7418e2d22e65c",
|
||||||
0,
|
0,
|
||||||
"dfa9437f9c9a1d1f9af271f79f2f5482f287cdb0d2e03fa92c8a9b216cc6061c",
|
"dfa9437f9c9a1d1f9af271f79f2f5482f287cdb0d2e03fa92c8a9b216cc6061c",
|
||||||
SchnorrSighashType::AllPlusAnyoneCanPay, None, None, None
|
TapSighashType::AllPlusAnyoneCanPay, None, None, None
|
||||||
);
|
);
|
||||||
|
|
||||||
test_taproot_sighash(
|
test_taproot_sighash(
|
||||||
|
@ -1183,7 +1235,7 @@ mod tests {
|
||||||
"0144c84d0000000000225120e3f2107989c88e67296ab2faca930efa2e3a5bd3ff0904835a11c9e807458621",
|
"0144c84d0000000000225120e3f2107989c88e67296ab2faca930efa2e3a5bd3ff0904835a11c9e807458621",
|
||||||
0,
|
0,
|
||||||
"3129de36a5d05fff97ffca31eb75fcccbbbc27b3147a7a36a9e4b45d8b625067",
|
"3129de36a5d05fff97ffca31eb75fcccbbbc27b3147a7a36a9e4b45d8b625067",
|
||||||
SchnorrSighashType::None, None, None, None
|
TapSighashType::None, None, None, None
|
||||||
);
|
);
|
||||||
|
|
||||||
test_taproot_sighash(
|
test_taproot_sighash(
|
||||||
|
@ -1191,7 +1243,7 @@ mod tests {
|
||||||
"013fed110000000000225120eb536ae8c33580290630fc495046e998086a64f8f33b93b07967d9029b265c55",
|
"013fed110000000000225120eb536ae8c33580290630fc495046e998086a64f8f33b93b07967d9029b265c55",
|
||||||
0,
|
0,
|
||||||
"2441e8b0e063a2083ee790f14f2045022f07258ddde5ee01de543c9e789d80ae",
|
"2441e8b0e063a2083ee790f14f2045022f07258ddde5ee01de543c9e789d80ae",
|
||||||
SchnorrSighashType::NonePlusAnyoneCanPay, None, None, None
|
TapSighashType::NonePlusAnyoneCanPay, None, None, None
|
||||||
);
|
);
|
||||||
|
|
||||||
test_taproot_sighash(
|
test_taproot_sighash(
|
||||||
|
@ -1199,7 +1251,7 @@ mod tests {
|
||||||
"01efa558000000000022512007071ea3dc7e331b0687d0193d1e6d6ed10e645ef36f10ef8831d5e522ac9e80",
|
"01efa558000000000022512007071ea3dc7e331b0687d0193d1e6d6ed10e645ef36f10ef8831d5e522ac9e80",
|
||||||
0,
|
0,
|
||||||
"30239345177cadd0e3ea413d49803580abb6cb27971b481b7788a78d35117a88",
|
"30239345177cadd0e3ea413d49803580abb6cb27971b481b7788a78d35117a88",
|
||||||
SchnorrSighashType::Single, None, None, None
|
TapSighashType::Single, None, None, None
|
||||||
);
|
);
|
||||||
|
|
||||||
test_taproot_sighash(
|
test_taproot_sighash(
|
||||||
|
@ -1207,7 +1259,7 @@ mod tests {
|
||||||
"0107af4e00000000002251202c36d243dfc06cb56a248e62df27ecba7417307511a81ae61aa41c597a929c69",
|
"0107af4e00000000002251202c36d243dfc06cb56a248e62df27ecba7417307511a81ae61aa41c597a929c69",
|
||||||
0,
|
0,
|
||||||
"bf9c83f26c6dd16449e4921f813f551c4218e86f2ec906ca8611175b41b566df",
|
"bf9c83f26c6dd16449e4921f813f551c4218e86f2ec906ca8611175b41b566df",
|
||||||
SchnorrSighashType::SinglePlusAnyoneCanPay, None, None, None
|
TapSighashType::SinglePlusAnyoneCanPay, None, None, None
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1218,7 +1270,7 @@ mod tests {
|
||||||
"01ea49260000000000225120ab5e9800806bf18cb246edcf5fe63441208fe955a4b5a35bbff65f5db622a010",
|
"01ea49260000000000225120ab5e9800806bf18cb246edcf5fe63441208fe955a4b5a35bbff65f5db622a010",
|
||||||
0,
|
0,
|
||||||
"3b003000add359a364a156e73e02846782a59d0d95ca8c4638aaad99f2ef915c",
|
"3b003000add359a364a156e73e02846782a59d0d95ca8c4638aaad99f2ef915c",
|
||||||
SchnorrSighashType::SinglePlusAnyoneCanPay,
|
TapSighashType::SinglePlusAnyoneCanPay,
|
||||||
Some("507b979802e62d397acb29f56743a791894b99372872fc5af06a4f6e8d242d0615cda53062bb20e6ec79756fe39183f0c128adfe85559a8fa042b042c018aa8010143799e44f0893c40e1e"),
|
Some("507b979802e62d397acb29f56743a791894b99372872fc5af06a4f6e8d242d0615cda53062bb20e6ec79756fe39183f0c128adfe85559a8fa042b042c018aa8010143799e44f0893c40e1e"),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
@ -1232,7 +1284,7 @@ mod tests {
|
||||||
"011bec34000000000022512028055142ea437db73382e991861446040b61dd2185c4891d7daf6893d79f7182",
|
"011bec34000000000022512028055142ea437db73382e991861446040b61dd2185c4891d7daf6893d79f7182",
|
||||||
0,
|
0,
|
||||||
"d66de5274a60400c7b08c86ba6b7f198f40660079edf53aca89d2a9501317f2e",
|
"d66de5274a60400c7b08c86ba6b7f198f40660079edf53aca89d2a9501317f2e",
|
||||||
SchnorrSighashType::All,
|
TapSighashType::All,
|
||||||
None,
|
None,
|
||||||
Some("20cc4e1107aea1d170c5ff5b6817e1303010049724fb3caa7941792ea9d29b3e2bacab"),
|
Some("20cc4e1107aea1d170c5ff5b6817e1303010049724fb3caa7941792ea9d29b3e2bacab"),
|
||||||
None,
|
None,
|
||||||
|
@ -1246,7 +1298,7 @@ mod tests {
|
||||||
"011bec34000000000022512028055142ea437db73382e991861446040b61dd2185c4891d7daf6893d79f7182",
|
"011bec34000000000022512028055142ea437db73382e991861446040b61dd2185c4891d7daf6893d79f7182",
|
||||||
0,
|
0,
|
||||||
"d66de5274a60400c7b08c86ba6b7f198f40660079edf53aca89d2a9501317f2e",
|
"d66de5274a60400c7b08c86ba6b7f198f40660079edf53aca89d2a9501317f2e",
|
||||||
SchnorrSighashType::All,
|
TapSighashType::All,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Some("15a2530514e399f8b5cf0b3d3112cf5b289eaa3e308ba2071b58392fdc6da68a"),
|
Some("15a2530514e399f8b5cf0b3d3112cf5b289eaa3e308ba2071b58392fdc6da68a"),
|
||||||
|
@ -1260,7 +1312,7 @@ mod tests {
|
||||||
"011458360000000000225120a7baec3fb9f84614e3899fcc010c638f80f13539344120e1f4d8b68a9a011a13",
|
"011458360000000000225120a7baec3fb9f84614e3899fcc010c638f80f13539344120e1f4d8b68a9a011a13",
|
||||||
0,
|
0,
|
||||||
"a0042aa434f9a75904b64043f2a283f8b4c143c7f4f7f49a6cbe5b9f745f4c15",
|
"a0042aa434f9a75904b64043f2a283f8b4c143c7f4f7f49a6cbe5b9f745f4c15",
|
||||||
SchnorrSighashType::All,
|
TapSighashType::All,
|
||||||
Some("50a6272b470e1460e3332ade7bb14b81671c564fb6245761bd5bd531394b28860e0b3808ab229fb51791fb6ae6fa82d915b2efb8f6df83ae1f5ab3db13e30928875e2a22b749d89358de481f19286cd4caa792ce27f9559082d227a731c5486882cc707f83da361c51b7aadd9a0cf68fe7480c410fa137b454482d9a1ebf0f96d760b4d61426fc109c6e8e99a508372c45caa7b000a41f8251305da3f206c1849985ba03f3d9592832b4053afbd23ab25d0465df0bc25a36c223aacf8e04ec736a418c72dc319e4da3e972e349713ca600965e7c665f2090d5a70e241ac164115a1f5639f28b1773327715ca307ace64a2de7f0e3df70a2ffee3857689f909c0dad46d8a20fa373a4cc6eed6d4c9806bf146f0d76baae1"),
|
Some("50a6272b470e1460e3332ade7bb14b81671c564fb6245761bd5bd531394b28860e0b3808ab229fb51791fb6ae6fa82d915b2efb8f6df83ae1f5ab3db13e30928875e2a22b749d89358de481f19286cd4caa792ce27f9559082d227a731c5486882cc707f83da361c51b7aadd9a0cf68fe7480c410fa137b454482d9a1ebf0f96d760b4d61426fc109c6e8e99a508372c45caa7b000a41f8251305da3f206c1849985ba03f3d9592832b4053afbd23ab25d0465df0bc25a36c223aacf8e04ec736a418c72dc319e4da3e972e349713ca600965e7c665f2090d5a70e241ac164115a1f5639f28b1773327715ca307ace64a2de7f0e3df70a2ffee3857689f909c0dad46d8a20fa373a4cc6eed6d4c9806bf146f0d76baae1"),
|
||||||
Some("7520ab9160dd8299dc1367659be3e8f66781fe440d52940c7f8d314a89b9f2698d406ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6eadac"),
|
Some("7520ab9160dd8299dc1367659be3e8f66781fe440d52940c7f8d314a89b9f2698d406ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6eadac"),
|
||||||
None,
|
None,
|
||||||
|
@ -1282,27 +1334,27 @@ mod tests {
|
||||||
let empty_vec = vec![];
|
let empty_vec = vec![];
|
||||||
let empty_prevouts : Prevouts<TxOut> = Prevouts::All(&empty_vec);
|
let empty_prevouts : Prevouts<TxOut> = Prevouts::All(&empty_vec);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.taproot_signature_hash(0, &empty_prevouts, None, None, SchnorrSighashType::All),
|
c.taproot_signature_hash(0, &empty_prevouts, None, None, TapSighashType::All),
|
||||||
Err(Error::PrevoutsSize)
|
Err(Error::PrevoutsSize)
|
||||||
);
|
);
|
||||||
let two = vec![TxOut::default(), TxOut::default()];
|
let two = vec![TxOut::default(), TxOut::default()];
|
||||||
let too_many_prevouts = Prevouts::All(&two);
|
let too_many_prevouts = Prevouts::All(&two);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.taproot_signature_hash(0, &too_many_prevouts, None, None, SchnorrSighashType::All),
|
c.taproot_signature_hash(0, &too_many_prevouts, None, None, TapSighashType::All),
|
||||||
Err(Error::PrevoutsSize)
|
Err(Error::PrevoutsSize)
|
||||||
);
|
);
|
||||||
let tx_out = TxOut::default();
|
let tx_out = TxOut::default();
|
||||||
let prevout = Prevouts::One(1, &tx_out);
|
let prevout = Prevouts::One(1, &tx_out);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.taproot_signature_hash(0, &prevout, None, None, SchnorrSighashType::All),
|
c.taproot_signature_hash(0, &prevout, None, None, TapSighashType::All),
|
||||||
Err(Error::PrevoutKind)
|
Err(Error::PrevoutKind)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.taproot_signature_hash(0, &prevout, None, None, SchnorrSighashType::AllPlusAnyoneCanPay),
|
c.taproot_signature_hash(0, &prevout, None, None, TapSighashType::AllPlusAnyoneCanPay),
|
||||||
Err(Error::PrevoutIndex)
|
Err(Error::PrevoutIndex)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.taproot_signature_hash(10, &prevout, None, None, SchnorrSighashType::AllPlusAnyoneCanPay),
|
c.taproot_signature_hash(10, &prevout, None, None, TapSighashType::AllPlusAnyoneCanPay),
|
||||||
Err(Error::IndexOutOfInputsBounds {
|
Err(Error::IndexOutOfInputsBounds {
|
||||||
index: 10,
|
index: 10,
|
||||||
inputs_size: 1
|
inputs_size: 1
|
||||||
|
@ -1310,7 +1362,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
let prevout = Prevouts::One(0, &tx_out);
|
let prevout = Prevouts::One(0, &tx_out);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
c.taproot_signature_hash(0, &prevout, None, None, SchnorrSighashType::SinglePlusAnyoneCanPay),
|
c.taproot_signature_hash(0, &prevout, None, None, TapSighashType::SinglePlusAnyoneCanPay),
|
||||||
Err(Error::SingleWithoutCorrespondingOutput {
|
Err(Error::SingleWithoutCorrespondingOutput {
|
||||||
index: 0,
|
index: 0,
|
||||||
outputs_size: 0
|
outputs_size: 0
|
||||||
|
@ -1338,7 +1390,7 @@ mod tests {
|
||||||
prevout_hex: &str,
|
prevout_hex: &str,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
expected_hash: &str,
|
expected_hash: &str,
|
||||||
sighash_type: SchnorrSighashType,
|
sighash_type: TapSighashType,
|
||||||
annex_hex: Option<&str>,
|
annex_hex: Option<&str>,
|
||||||
script_hex: Option<&str>,
|
script_hex: Option<&str>,
|
||||||
script_leaf_hash: Option<&str>,
|
script_leaf_hash: Option<&str>,
|
||||||
|
@ -1387,14 +1439,14 @@ mod tests {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
#[test]
|
#[test]
|
||||||
fn bip_341_sighash_tests() {
|
fn bip_341_sighash_tests() {
|
||||||
fn sighash_deser_numeric<'de, D>(deserializer: D) -> Result<SchnorrSighashType, D::Error>
|
fn sighash_deser_numeric<'de, D>(deserializer: D) -> Result<TapSighashType, D::Error>
|
||||||
where
|
where
|
||||||
D: actual_serde::Deserializer<'de>,
|
D: actual_serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
use actual_serde::de::{Deserialize, Error, Unexpected};
|
use actual_serde::de::{Deserialize, Error, Unexpected};
|
||||||
|
|
||||||
let raw = u8::deserialize(deserializer)?;
|
let raw = u8::deserialize(deserializer)?;
|
||||||
SchnorrSighashType::from_consensus_u8(raw).map_err(|_| {
|
TapSighashType::from_consensus_u8(raw).map_err(|_| {
|
||||||
D::Error::invalid_value(
|
D::Error::invalid_value(
|
||||||
Unexpected::Unsigned(raw.into()),
|
Unexpected::Unsigned(raw.into()),
|
||||||
&"number in range 0-3 or 0x81-0x83",
|
&"number in range 0-3 or 0x81-0x83",
|
||||||
|
@ -1444,7 +1496,7 @@ mod tests {
|
||||||
internal_privkey: SecretKey,
|
internal_privkey: SecretKey,
|
||||||
merkle_root: Option<TapNodeHash>,
|
merkle_root: Option<TapNodeHash>,
|
||||||
#[serde(deserialize_with = "sighash_deser_numeric")]
|
#[serde(deserialize_with = "sighash_deser_numeric")]
|
||||||
hash_type: SchnorrSighashType,
|
hash_type: TapSighashType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
@ -1456,7 +1508,7 @@ mod tests {
|
||||||
tweaked_privkey: SecretKey,
|
tweaked_privkey: SecretKey,
|
||||||
sig_msg: String,
|
sig_msg: String,
|
||||||
//precomputed_used: Vec<String>, // unused
|
//precomputed_used: Vec<String>, // unused
|
||||||
sig_hash: TapSighashHash,
|
sig_hash: TapSighash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
@ -1494,7 +1546,7 @@ mod tests {
|
||||||
//script_pubkey: Vec<ScriptPubKey>, // unused
|
//script_pubkey: Vec<ScriptPubKey>, // unused
|
||||||
}
|
}
|
||||||
|
|
||||||
let json_str = include_str!("../tests/data/bip341_tests.json");
|
let json_str = include_str!("../../tests/data/bip341_tests.json");
|
||||||
let mut data =
|
let mut data =
|
||||||
serde_json::from_str::<TestData>(json_str).expect("JSON was not well-formatted");
|
serde_json::from_str::<TestData>(json_str).expect("JSON was not well-formatted");
|
||||||
|
|
||||||
|
@ -1532,11 +1584,11 @@ mod tests {
|
||||||
let (expected_key_spend_sig, expected_hash_ty) = if sig_str.len() == 128 {
|
let (expected_key_spend_sig, expected_hash_ty) = if sig_str.len() == 128 {
|
||||||
(
|
(
|
||||||
secp256k1::schnorr::Signature::from_str(&sig_str).unwrap(),
|
secp256k1::schnorr::Signature::from_str(&sig_str).unwrap(),
|
||||||
SchnorrSighashType::Default,
|
TapSighashType::Default,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let hash_ty = u8::from_str_radix(&sig_str[128..130], 16).unwrap();
|
let hash_ty = u8::from_str_radix(&sig_str[128..130], 16).unwrap();
|
||||||
let hash_ty = SchnorrSighashType::from_consensus_u8(hash_ty).unwrap();
|
let hash_ty = TapSighashType::from_consensus_u8(hash_ty).unwrap();
|
||||||
(secp256k1::schnorr::Signature::from_str(&sig_str[..128]).unwrap(), hash_ty)
|
(secp256k1::schnorr::Signature::from_str(&sig_str[..128]).unwrap(), hash_ty)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1578,17 +1630,17 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn sighashtype_fromstr_display() {
|
fn sighashtype_fromstr_display() {
|
||||||
let sighashtypes = vec![
|
let sighashtypes = vec![
|
||||||
("SIGHASH_DEFAULT", SchnorrSighashType::Default),
|
("SIGHASH_DEFAULT", TapSighashType::Default),
|
||||||
("SIGHASH_ALL", SchnorrSighashType::All),
|
("SIGHASH_ALL", TapSighashType::All),
|
||||||
("SIGHASH_NONE", SchnorrSighashType::None),
|
("SIGHASH_NONE", TapSighashType::None),
|
||||||
("SIGHASH_SINGLE", SchnorrSighashType::Single),
|
("SIGHASH_SINGLE", TapSighashType::Single),
|
||||||
("SIGHASH_ALL|SIGHASH_ANYONECANPAY", SchnorrSighashType::AllPlusAnyoneCanPay),
|
("SIGHASH_ALL|SIGHASH_ANYONECANPAY", TapSighashType::AllPlusAnyoneCanPay),
|
||||||
("SIGHASH_NONE|SIGHASH_ANYONECANPAY", SchnorrSighashType::NonePlusAnyoneCanPay),
|
("SIGHASH_NONE|SIGHASH_ANYONECANPAY", TapSighashType::NonePlusAnyoneCanPay),
|
||||||
("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", SchnorrSighashType::SinglePlusAnyoneCanPay),
|
("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", TapSighashType::SinglePlusAnyoneCanPay),
|
||||||
];
|
];
|
||||||
for (s, sht) in sighashtypes {
|
for (s, sht) in sighashtypes {
|
||||||
assert_eq!(sht.to_string(), s);
|
assert_eq!(sht.to_string(), s);
|
||||||
assert_eq!(SchnorrSighashType::from_str(s).unwrap(), sht);
|
assert_eq!(TapSighashType::from_str(s).unwrap(), sht);
|
||||||
}
|
}
|
||||||
let sht_mistakes = vec![
|
let sht_mistakes = vec![
|
||||||
"SIGHASH_ALL | SIGHASH_ANYONECANPAY",
|
"SIGHASH_ALL | SIGHASH_ANYONECANPAY",
|
||||||
|
@ -1606,7 +1658,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
for s in sht_mistakes {
|
for s in sht_mistakes {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
SchnorrSighashType::from_str(s).unwrap_err().to_string(),
|
TapSighashType::from_str(s).unwrap_err().to_string(),
|
||||||
format!("Unrecognized SIGHASH string '{}'", s)
|
format!("Unrecognized SIGHASH string '{}'", s)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1635,7 +1687,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 +1723,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 +1764,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();
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
//! Bitcoin taproot keys.
|
||||||
|
//!
|
||||||
|
//! This module provides taproot keys used in Bitcoin (including reexporting secp256k1 keys).
|
||||||
|
//!
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use bitcoin_internals::write_err;
|
||||||
|
|
||||||
|
pub use secp256k1::{self, constants, Secp256k1, KeyPair, XOnlyPublicKey, Verification, Parity};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use crate::sighash::TapSighashType;
|
||||||
|
|
||||||
|
/// A BIP340-341 serialized taproot signature with the corresponding hash type.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
|
||||||
|
pub struct Signature {
|
||||||
|
/// The underlying schnorr signature
|
||||||
|
pub sig: secp256k1::schnorr::Signature,
|
||||||
|
/// The corresponding hash type
|
||||||
|
pub hash_ty: TapSighashType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
/// Deserialize from slice
|
||||||
|
pub fn from_slice(sl: &[u8]) -> Result<Self, Error> {
|
||||||
|
match sl.len() {
|
||||||
|
64 => {
|
||||||
|
// default type
|
||||||
|
let sig = secp256k1::schnorr::Signature::from_slice(sl)
|
||||||
|
.map_err(Error::Secp256k1)?;
|
||||||
|
Ok(Signature { sig, hash_ty: TapSighashType::Default })
|
||||||
|
},
|
||||||
|
65 => {
|
||||||
|
let (hash_ty, sig) = sl.split_last().expect("Slice len checked == 65");
|
||||||
|
let hash_ty = TapSighashType::from_consensus_u8(*hash_ty)
|
||||||
|
.map_err(|_| Error::InvalidSighashType(*hash_ty))?;
|
||||||
|
let sig = secp256k1::schnorr::Signature::from_slice(sig)
|
||||||
|
.map_err(Error::Secp256k1)?;
|
||||||
|
Ok(Signature { sig, hash_ty })
|
||||||
|
}
|
||||||
|
len => {
|
||||||
|
Err(Error::InvalidSignatureSize(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize Signature
|
||||||
|
pub fn to_vec(self) -> Vec<u8> {
|
||||||
|
// TODO: add support to serialize to a writer to SerializedSig
|
||||||
|
let mut ser_sig = self.sig.as_ref().to_vec();
|
||||||
|
if self.hash_ty == TapSighashType::Default {
|
||||||
|
// default sighash type, don't add extra sighash byte
|
||||||
|
} else {
|
||||||
|
ser_sig.push(self.hash_ty as u8);
|
||||||
|
}
|
||||||
|
ser_sig
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A taproot sig related error.
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
|
/// Base58 encoding error
|
||||||
|
InvalidSighashType(u8),
|
||||||
|
/// Signature has valid size but does not parse correctly
|
||||||
|
Secp256k1(secp256k1::Error),
|
||||||
|
/// Invalid taproot signature size
|
||||||
|
InvalidSignatureSize(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Error::InvalidSighashType(hash_ty) =>
|
||||||
|
write!(f, "invalid signature hash type {}", hash_ty),
|
||||||
|
Error::Secp256k1(ref e) =>
|
||||||
|
write_err!(f, "taproot signature has correct len but is malformed"; e),
|
||||||
|
Error::InvalidSignatureSize(sz) =>
|
||||||
|
write!(f, "invalid taproot signature size: {}", sz),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
use self::Error::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Secp256k1(e) => Some(e),
|
||||||
|
InvalidSighashType(_) | InvalidSignatureSize(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<secp256k1::Error> for Error {
|
||||||
|
|
||||||
|
fn from(e: secp256k1::Error) -> Error {
|
||||||
|
Error::Secp256k1(e)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.");
|
||||||
|
|
|
@ -99,7 +99,6 @@ pub mod merkle_tree;
|
||||||
pub mod policy;
|
pub mod policy;
|
||||||
pub mod pow;
|
pub mod pow;
|
||||||
pub mod psbt;
|
pub mod psbt;
|
||||||
pub mod sighash;
|
|
||||||
pub mod sign_message;
|
pub mod sign_message;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
pub mod taproot;
|
pub mod taproot;
|
||||||
|
@ -128,7 +127,7 @@ pub use crate::blockdata::witness::{self, Witness};
|
||||||
pub use crate::blockdata::{constants, opcodes};
|
pub use crate::blockdata::{constants, opcodes};
|
||||||
pub use crate::consensus::encode::VarInt;
|
pub use crate::consensus::encode::VarInt;
|
||||||
pub use crate::crypto::key::{self, PrivateKey, PublicKey};
|
pub use crate::crypto::key::{self, PrivateKey, PublicKey};
|
||||||
pub use crate::crypto::{ecdsa, schnorr};
|
pub use crate::crypto::sighash;
|
||||||
pub use crate::error::Error;
|
pub use crate::error::Error;
|
||||||
pub use crate::hash_types::{Txid, Wtxid, BlockHash, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash};
|
pub use crate::hash_types::{Txid, Wtxid, BlockHash, PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash};
|
||||||
pub use crate::merkle_tree::MerkleBlock;
|
pub use crate::merkle_tree::MerkleBlock;
|
||||||
|
|
|
@ -87,13 +87,13 @@ pub enum Error {
|
||||||
InvalidXOnlyPublicKey,
|
InvalidXOnlyPublicKey,
|
||||||
/// Parsing error indicating invalid ECDSA signatures
|
/// Parsing error indicating invalid ECDSA signatures
|
||||||
InvalidEcdsaSignature(crate::crypto::ecdsa::Error),
|
InvalidEcdsaSignature(crate::crypto::ecdsa::Error),
|
||||||
/// Parsing error indicating invalid Schnorr signatures
|
/// Parsing error indicating invalid taproot signatures
|
||||||
InvalidSchnorrSignature(crate::crypto::schnorr::Error),
|
InvalidTaprootSignature(crate::crypto::taproot::Error),
|
||||||
/// Parsing error indicating invalid control block
|
/// Parsing error indicating invalid control block
|
||||||
InvalidControlBlock,
|
InvalidControlBlock,
|
||||||
/// Parsing error indicating invalid leaf version
|
/// Parsing error indicating invalid leaf version
|
||||||
InvalidLeafVersion,
|
InvalidLeafVersion,
|
||||||
/// Parsing error indicating a Taproot error
|
/// Parsing error indicating a taproot error
|
||||||
Taproot(&'static str),
|
Taproot(&'static str),
|
||||||
/// Error related to an xpub key
|
/// Error related to an xpub key
|
||||||
XPubKey(&'static str),
|
XPubKey(&'static str),
|
||||||
|
@ -136,7 +136,7 @@ impl fmt::Display for Error {
|
||||||
Error::InvalidSecp256k1PublicKey(ref e) => write_err!(f, "invalid secp256k1 public key"; e),
|
Error::InvalidSecp256k1PublicKey(ref e) => write_err!(f, "invalid secp256k1 public key"; e),
|
||||||
Error::InvalidXOnlyPublicKey => f.write_str("invalid xonly public key"),
|
Error::InvalidXOnlyPublicKey => f.write_str("invalid xonly public key"),
|
||||||
Error::InvalidEcdsaSignature(ref e) => write_err!(f, "invalid ECDSA signature"; e),
|
Error::InvalidEcdsaSignature(ref e) => write_err!(f, "invalid ECDSA signature"; e),
|
||||||
Error::InvalidSchnorrSignature(ref e) => write_err!(f, "invalid Schnorr signature"; e),
|
Error::InvalidTaprootSignature(ref e) => write_err!(f, "invalid taproot signature"; e),
|
||||||
Error::InvalidControlBlock => f.write_str("invalid control block"),
|
Error::InvalidControlBlock => f.write_str("invalid control block"),
|
||||||
Error::InvalidLeafVersion => f.write_str("invalid leaf version"),
|
Error::InvalidLeafVersion => f.write_str("invalid leaf version"),
|
||||||
Error::Taproot(s) => write!(f, "taproot error - {}", s),
|
Error::Taproot(s) => write!(f, "taproot error - {}", s),
|
||||||
|
@ -179,7 +179,7 @@ impl std::error::Error for Error {
|
||||||
| InvalidSecp256k1PublicKey(_)
|
| InvalidSecp256k1PublicKey(_)
|
||||||
| InvalidXOnlyPublicKey
|
| InvalidXOnlyPublicKey
|
||||||
| InvalidEcdsaSignature(_)
|
| InvalidEcdsaSignature(_)
|
||||||
| InvalidSchnorrSignature(_)
|
| InvalidTaprootSignature(_)
|
||||||
| InvalidControlBlock
|
| InvalidControlBlock
|
||||||
| InvalidLeafVersion
|
| InvalidLeafVersion
|
||||||
| Taproot(_)
|
| Taproot(_)
|
||||||
|
|
|
@ -11,14 +11,14 @@ use secp256k1::XOnlyPublicKey;
|
||||||
use crate::blockdata::script::ScriptBuf;
|
use crate::blockdata::script::ScriptBuf;
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
use crate::blockdata::transaction::{Transaction, TxOut};
|
use crate::blockdata::transaction::{Transaction, TxOut};
|
||||||
use crate::crypto::{ecdsa, schnorr};
|
use crate::crypto::{ecdsa, taproot};
|
||||||
use crate::crypto::key::PublicKey;
|
use crate::crypto::key::PublicKey;
|
||||||
use crate::hashes::{self, hash160, ripemd160, sha256, sha256d};
|
use crate::hashes::{self, hash160, ripemd160, sha256, sha256d};
|
||||||
use crate::bip32::KeySource;
|
use crate::bip32::KeySource;
|
||||||
use crate::psbt::map::Map;
|
use crate::psbt::map::Map;
|
||||||
use crate::psbt::serialize::Deserialize;
|
use crate::psbt::serialize::Deserialize;
|
||||||
use crate::psbt::{self, error, raw, Error};
|
use crate::psbt::{self, error, raw, Error};
|
||||||
use crate::sighash::{self, NonStandardSighashType, SighashTypeParseError, EcdsaSighashType, SchnorrSighashType};
|
use crate::sighash::{self, NonStandardSighashType, SighashTypeParseError, EcdsaSighashType, TapSighashType};
|
||||||
use crate::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapNodeHash};
|
use crate::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapNodeHash};
|
||||||
|
|
||||||
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
|
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
|
||||||
|
@ -47,9 +47,9 @@ const PSBT_IN_SHA256: u8 = 0x0b;
|
||||||
const PSBT_IN_HASH160: u8 = 0x0c;
|
const PSBT_IN_HASH160: u8 = 0x0c;
|
||||||
/// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d
|
/// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d
|
||||||
const PSBT_IN_HASH256: u8 = 0x0d;
|
const PSBT_IN_HASH256: u8 = 0x0d;
|
||||||
/// Type: Schnorr Signature in Key Spend PSBT_IN_TAP_KEY_SIG = 0x13
|
/// Type: Taproot Signature in Key Spend PSBT_IN_TAP_KEY_SIG = 0x13
|
||||||
const PSBT_IN_TAP_KEY_SIG: u8 = 0x13;
|
const PSBT_IN_TAP_KEY_SIG: u8 = 0x13;
|
||||||
/// Type: Schnorr Signature in Script Spend PSBT_IN_TAP_SCRIPT_SIG = 0x14
|
/// Type: Taproot Signature in Script Spend PSBT_IN_TAP_SCRIPT_SIG = 0x14
|
||||||
const PSBT_IN_TAP_SCRIPT_SIG: u8 = 0x14;
|
const PSBT_IN_TAP_SCRIPT_SIG: u8 = 0x14;
|
||||||
/// Type: Taproot Leaf Script PSBT_IN_TAP_LEAF_SCRIPT = 0x14
|
/// Type: Taproot Leaf Script PSBT_IN_TAP_LEAF_SCRIPT = 0x14
|
||||||
const PSBT_IN_TAP_LEAF_SCRIPT: u8 = 0x15;
|
const PSBT_IN_TAP_LEAF_SCRIPT: u8 = 0x15;
|
||||||
|
@ -109,11 +109,11 @@ pub struct Input {
|
||||||
/// HAS256 hash to preimage map.
|
/// HAS256 hash to preimage map.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_byte_values"))]
|
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_byte_values"))]
|
||||||
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
|
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
|
||||||
/// Serialized schnorr signature with sighash type for key spend.
|
/// Serialized taproot signature with sighash type for key spend.
|
||||||
pub tap_key_sig: Option<schnorr::Signature>,
|
pub tap_key_sig: Option<taproot::Signature>,
|
||||||
/// Map of `<xonlypubkey>|<leafhash>` with signature.
|
/// Map of `<xonlypubkey>|<leafhash>` with signature.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
|
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
|
||||||
pub tap_script_sigs: BTreeMap<(XOnlyPublicKey, TapLeafHash), schnorr::Signature>,
|
pub tap_script_sigs: BTreeMap<(XOnlyPublicKey, TapLeafHash), taproot::Signature>,
|
||||||
/// Map of Control blocks to Script version pair.
|
/// Map of Control blocks to Script version pair.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
|
#[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::btreemap_as_seq"))]
|
||||||
pub tap_scripts: BTreeMap<ControlBlock, (ScriptBuf, LeafVersion)>,
|
pub tap_scripts: BTreeMap<ControlBlock, (ScriptBuf, LeafVersion)>,
|
||||||
|
@ -134,7 +134,7 @@ pub struct Input {
|
||||||
|
|
||||||
|
|
||||||
/// A Signature hash type for the corresponding input. As of taproot upgrade, the signature hash
|
/// 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
|
/// type can be either [`EcdsaSighashType`] or [`TapSighashType`] but it is not possible to know
|
||||||
/// directly which signature hash type the user is dealing with. Therefore, the user is responsible
|
/// 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.
|
/// for converting to/from [`PsbtSighashType`] from/to the desired signature hash type they need.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -146,9 +146,9 @@ pub struct PsbtSighashType {
|
||||||
|
|
||||||
impl fmt::Display for PsbtSighashType {
|
impl fmt::Display for PsbtSighashType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.schnorr_hash_ty() {
|
match self.taproot_hash_ty() {
|
||||||
Err(_) => write!(f, "{:#x}", self.inner),
|
Err(_) => write!(f, "{:#x}", self.inner),
|
||||||
Ok(schnorr_hash_ty) => fmt::Display::fmt(&schnorr_hash_ty, f),
|
Ok(taproot_hash_ty) => fmt::Display::fmt(&taproot_hash_ty, f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,10 +160,10 @@ impl FromStr for PsbtSighashType {
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
// We accept strings of form: "SIGHASH_ALL" etc.
|
// We accept strings of form: "SIGHASH_ALL" etc.
|
||||||
//
|
//
|
||||||
// NB: some of Schnorr sighash types are non-standard for pre-taproot
|
// NB: some of Taproot sighash types are non-standard for pre-taproot
|
||||||
// inputs. We also do not support SIGHASH_RESERVED in verbatim form
|
// inputs. We also do not support SIGHASH_RESERVED in verbatim form
|
||||||
// ("0xFF" string should be used instead).
|
// ("0xFF" string should be used instead).
|
||||||
if let Ok(ty) = SchnorrSighashType::from_str(s) {
|
if let Ok(ty) = TapSighashType::from_str(s) {
|
||||||
return Ok(ty.into());
|
return Ok(ty.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,9 +181,9 @@ impl From<EcdsaSighashType> for PsbtSighashType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SchnorrSighashType> for PsbtSighashType {
|
impl From<TapSighashType> for PsbtSighashType {
|
||||||
fn from(schnorr_hash_ty: SchnorrSighashType) -> Self {
|
fn from(taproot_hash_ty: TapSighashType) -> Self {
|
||||||
PsbtSighashType { inner: schnorr_hash_ty as u32 }
|
PsbtSighashType { inner: taproot_hash_ty as u32 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,20 +194,20 @@ impl PsbtSighashType {
|
||||||
EcdsaSighashType::from_standard(self.inner)
|
EcdsaSighashType::from_standard(self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`SchnorrSighashType`] if the [`PsbtSighashType`] can be
|
/// Returns the [`TapSighashType`] if the [`PsbtSighashType`] can be
|
||||||
/// converted to one.
|
/// converted to one.
|
||||||
pub fn schnorr_hash_ty(self) -> Result<SchnorrSighashType, sighash::Error> {
|
pub fn taproot_hash_ty(self) -> Result<TapSighashType, sighash::Error> {
|
||||||
if self.inner > 0xffu32 {
|
if self.inner > 0xffu32 {
|
||||||
Err(sighash::Error::InvalidSighashType(self.inner))
|
Err(sighash::Error::InvalidSighashType(self.inner))
|
||||||
} else {
|
} else {
|
||||||
SchnorrSighashType::from_consensus_u8(self.inner as u8)
|
TapSighashType::from_consensus_u8(self.inner as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`PsbtSighashType`] from a raw `u32`.
|
/// Creates a [`PsbtSighashType`] from a raw `u32`.
|
||||||
///
|
///
|
||||||
/// Allows construction of a non-standard or non-valid sighash flag
|
/// Allows construction of a non-standard or non-valid sighash flag
|
||||||
/// ([`EcdsaSighashType`], [`SchnorrSighashType`] respectively).
|
/// ([`EcdsaSighashType`], [`TapSighashType`] respectively).
|
||||||
pub fn from_u32(n: u32) -> PsbtSighashType {
|
pub fn from_u32(n: u32) -> PsbtSighashType {
|
||||||
PsbtSighashType { inner: n }
|
PsbtSighashType { inner: n }
|
||||||
}
|
}
|
||||||
|
@ -234,16 +234,16 @@ impl Input {
|
||||||
.unwrap_or(Ok(EcdsaSighashType::All))
|
.unwrap_or(Ok(EcdsaSighashType::All))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtains the [`SchnorrSighashType`] for this input if one is specified. If no sighash type is
|
/// Obtains the [`TapSighashType`] for this input if one is specified. If no sighash type is
|
||||||
/// specified, returns [`SchnorrSighashType::Default`].
|
/// specified, returns [`TapSighashType::Default`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// If the `sighash_type` field is set to a invalid Schnorr sighash value.
|
/// If the `sighash_type` field is set to a invalid Taproot sighash value.
|
||||||
pub fn schnorr_hash_ty(&self) -> Result<SchnorrSighashType, sighash::Error> {
|
pub fn taproot_hash_ty(&self) -> Result<TapSighashType, sighash::Error> {
|
||||||
self.sighash_type
|
self.sighash_type
|
||||||
.map(|sighash_type| sighash_type.schnorr_hash_ty())
|
.map(|sighash_type| sighash_type.taproot_hash_ty())
|
||||||
.unwrap_or(Ok(SchnorrSighashType::Default))
|
.unwrap_or(Ok(TapSighashType::Default))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), Error> {
|
pub(super) fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), Error> {
|
||||||
|
@ -312,12 +312,12 @@ impl Input {
|
||||||
}
|
}
|
||||||
PSBT_IN_TAP_KEY_SIG => {
|
PSBT_IN_TAP_KEY_SIG => {
|
||||||
impl_psbt_insert_pair! {
|
impl_psbt_insert_pair! {
|
||||||
self.tap_key_sig <= <raw_key: _>|<raw_value: schnorr::Signature>
|
self.tap_key_sig <= <raw_key: _>|<raw_value: taproot::Signature>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PSBT_IN_TAP_SCRIPT_SIG => {
|
PSBT_IN_TAP_SCRIPT_SIG => {
|
||||||
impl_psbt_insert_pair! {
|
impl_psbt_insert_pair! {
|
||||||
self.tap_script_sigs <= <raw_key: (XOnlyPublicKey, TapLeafHash)>|<raw_value: schnorr::Signature>
|
self.tap_script_sigs <= <raw_key: (XOnlyPublicKey, TapLeafHash)>|<raw_value: taproot::Signature>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PSBT_IN_TAP_LEAF_SCRIPT => {
|
PSBT_IN_TAP_LEAF_SCRIPT => {
|
||||||
|
@ -543,21 +543,21 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn psbt_sighash_type_schnorr() {
|
fn psbt_sighash_type_taproot() {
|
||||||
for schnorr in &[
|
for tap in &[
|
||||||
SchnorrSighashType::Default,
|
TapSighashType::Default,
|
||||||
SchnorrSighashType::All,
|
TapSighashType::All,
|
||||||
SchnorrSighashType::None,
|
TapSighashType::None,
|
||||||
SchnorrSighashType::Single,
|
TapSighashType::Single,
|
||||||
SchnorrSighashType::AllPlusAnyoneCanPay,
|
TapSighashType::AllPlusAnyoneCanPay,
|
||||||
SchnorrSighashType::NonePlusAnyoneCanPay,
|
TapSighashType::NonePlusAnyoneCanPay,
|
||||||
SchnorrSighashType::SinglePlusAnyoneCanPay,
|
TapSighashType::SinglePlusAnyoneCanPay,
|
||||||
] {
|
] {
|
||||||
let sighash = PsbtSighashType::from(*schnorr);
|
let sighash = PsbtSighashType::from(*tap);
|
||||||
let s = format!("{}", sighash);
|
let s = format!("{}", sighash);
|
||||||
let back = PsbtSighashType::from_str(&s).unwrap();
|
let back = PsbtSighashType::from_str(&s).unwrap();
|
||||||
assert_eq!(back, sighash);
|
assert_eq!(back, sighash);
|
||||||
assert_eq!(back.schnorr_hash_ty().unwrap(), *schnorr);
|
assert_eq!(back.taproot_hash_ty().unwrap(), *tap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,6 +570,6 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(back, sighash);
|
assert_eq!(back, sighash);
|
||||||
assert_eq!(back.ecdsa_hash_ty(), Err(NonStandardSighashType(nonstd)));
|
assert_eq!(back.ecdsa_hash_ty(), Err(NonStandardSighashType(nonstd)));
|
||||||
assert_eq!(back.schnorr_hash_ty(), Err(sighash::Error::InvalidSighashType(nonstd)));
|
assert_eq!(back.taproot_hash_ty(), Err(sighash::Error::InvalidSighashType(nonstd)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`.
|
||||||
|
@ -1373,9 +1376,9 @@ mod tests {
|
||||||
assert_eq!(err.to_string(), "invalid xonly public key");
|
assert_eq!(err.to_string(), "invalid xonly public key");
|
||||||
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000").unwrap_err();
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000").unwrap_err();
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
assert_eq!(err.to_string(), "invalid Schnorr signature");
|
assert_eq!(err.to_string(), "invalid taproot signature");
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
assert_eq!(err.to_string(), "invalid Schnorr signature: Invalid Schnorr signature size: 66");
|
assert_eq!(err.to_string(), "invalid taproot signature: invalid taproot signature size: 66");
|
||||||
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000").unwrap_err();
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000").unwrap_err();
|
||||||
assert_eq!(err.to_string(), "invalid xonly public key");
|
assert_eq!(err.to_string(), "invalid xonly public key");
|
||||||
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200").unwrap_err();
|
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200").unwrap_err();
|
||||||
|
@ -1389,14 +1392,14 @@ mod tests {
|
||||||
assert_eq!(err.to_string(), "hash parse error: invalid slice length 33 (expected 32)");
|
assert_eq!(err.to_string(), "hash parse error: invalid slice length 33 (expected 32)");
|
||||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000").unwrap_err();
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000").unwrap_err();
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
assert_eq!(err.to_string(), "invalid Schnorr signature");
|
assert_eq!(err.to_string(), "invalid taproot signature");
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
assert_eq!(err.to_string(), "invalid Schnorr signature: Invalid Schnorr signature size: 66");
|
assert_eq!(err.to_string(), "invalid taproot signature: invalid taproot signature size: 66");
|
||||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093989756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093989756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
assert_eq!(err.to_string(), "invalid Schnorr signature");
|
assert_eq!(err.to_string(), "invalid taproot signature");
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
assert_eq!(err.to_string(), "invalid Schnorr signature: Invalid Schnorr signature size: 57");
|
assert_eq!(err.to_string(), "invalid taproot signature: invalid taproot signature size: 57");
|
||||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||||
assert_eq!(err.to_string(), "invalid control block");
|
assert_eq!(err.to_string(), "invalid control block");
|
||||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::consensus::encode::{self, serialize, Decodable, Encodable, deserializ
|
||||||
use secp256k1::{self, XOnlyPublicKey};
|
use secp256k1::{self, XOnlyPublicKey};
|
||||||
use crate::bip32::{ChildNumber, Fingerprint, KeySource};
|
use crate::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||||
use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||||
use crate::crypto::{ecdsa, schnorr};
|
use crate::crypto::{ecdsa, taproot};
|
||||||
use crate::psbt::{Error, PartiallySignedTransaction};
|
use crate::psbt::{Error, PartiallySignedTransaction};
|
||||||
use crate::taproot::{TapNodeHash, TapLeafHash, ControlBlock, LeafVersion};
|
use crate::taproot::{TapNodeHash, TapLeafHash, ControlBlock, LeafVersion};
|
||||||
use crate::crypto::key::PublicKey;
|
use crate::crypto::key::PublicKey;
|
||||||
|
@ -283,24 +283,24 @@ impl Deserialize for XOnlyPublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for schnorr::Signature {
|
impl Serialize for taproot::Signature {
|
||||||
fn serialize(&self) -> Vec<u8> {
|
fn serialize(&self) -> Vec<u8> {
|
||||||
self.to_vec()
|
self.to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deserialize for schnorr::Signature {
|
impl Deserialize for taproot::Signature {
|
||||||
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
||||||
schnorr::Signature::from_slice(bytes)
|
taproot::Signature::from_slice(bytes)
|
||||||
.map_err(|e| match e {
|
.map_err(|e| match e {
|
||||||
schnorr::Error::InvalidSighashType(flag) => {
|
taproot::Error::InvalidSighashType(flag) => {
|
||||||
Error::NonStandardSighashType(flag as u32)
|
Error::NonStandardSighashType(flag as u32)
|
||||||
}
|
}
|
||||||
schnorr::Error::InvalidSignatureSize(_) => {
|
taproot::Error::InvalidSignatureSize(_) => {
|
||||||
Error::InvalidSchnorrSignature(e)
|
Error::InvalidTaprootSignature(e)
|
||||||
}
|
}
|
||||||
schnorr::Error::Secp256k1(..) => {
|
taproot::Error::Secp256k1(..) => {
|
||||||
Error::InvalidSchnorrSignature(e)
|
Error::InvalidTaprootSignature(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,13 @@ use bitcoin_internals::write_err;
|
||||||
use secp256k1::{self, Scalar, Secp256k1};
|
use secp256k1::{self, Scalar, Secp256k1};
|
||||||
|
|
||||||
use crate::consensus::Encodable;
|
use crate::consensus::Encodable;
|
||||||
use crate::crypto::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey};
|
use crate::crypto::key::{TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey};
|
||||||
use crate::hashes::{sha256t_hash_newtype, Hash, HashEngine};
|
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 [`TapSighashHash`].
|
|
||||||
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!(TapSighashHash, 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 TapSighashHash {
|
|
||||||
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
|
||||||
|
@ -1145,7 +1130,6 @@ mod test {
|
||||||
use secp256k1::{VerifyOnly, XOnlyPublicKey};
|
use secp256k1::{VerifyOnly, XOnlyPublicKey};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::crypto::schnorr::TapTweak;
|
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::hashes::sha256t::Tag;
|
use crate::hashes::sha256t::Tag;
|
||||||
use crate::hashes::{sha256, Hash, HashEngine};
|
use crate::hashes::{sha256, Hash, HashEngine};
|
||||||
|
@ -1165,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());
|
||||||
|
@ -1186,7 +1172,7 @@ mod test {
|
||||||
assert_eq!(empty_hash("TapLeaf"), TapLeafHash::hash(&[]).into_inner());
|
assert_eq!(empty_hash("TapLeaf"), TapLeafHash::hash(&[]).into_inner());
|
||||||
assert_eq!(empty_hash("TapBranch"), TapNodeHash::hash(&[]).into_inner());
|
assert_eq!(empty_hash("TapBranch"), TapNodeHash::hash(&[]).into_inner());
|
||||||
assert_eq!(empty_hash("TapTweak"), TapTweakHash::hash(&[]).into_inner());
|
assert_eq!(empty_hash("TapTweak"), TapTweakHash::hash(&[]).into_inner());
|
||||||
assert_eq!(empty_hash("TapSighash"), TapSighashHash::hash(&[]).into_inner());
|
assert_eq!(empty_hash("TapSighash"), TapSighash::hash(&[]).into_inner());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1209,7 +1195,7 @@ mod test {
|
||||||
"8aa4229474ab0100b2d6f0687f031d1fc9d8eef92a042ad97d279bff456b15e4"
|
"8aa4229474ab0100b2d6f0687f031d1fc9d8eef92a042ad97d279bff456b15e4"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TapSighashHash::from_engine(TapSighashTag::engine()).to_string(),
|
TapSighash::from_engine(TapSighashTag::engine()).to_string(),
|
||||||
"dabc11914abcd8072900042a2681e52f8dba99ce82e224f97b5fdb7cd4b9c803"
|
"dabc11914abcd8072900042a2681e52f8dba99ce82e224f97b5fdb7cd4b9c803"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1231,7 +1217,7 @@ mod test {
|
||||||
"cd8737b5e6047fc3f16f03e8b9959e3440e1bdf6dd02f7bb899c352ad490ea1e"
|
"cd8737b5e6047fc3f16f03e8b9959e3440e1bdf6dd02f7bb899c352ad490ea1e"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TapSighashHash::hash(&[0]).to_string(),
|
TapSighash::hash(&[0]).to_string(),
|
||||||
"c2fd0de003889a09c4afcf676656a0d8a1fb706313ff7d509afb00c323c010cd"
|
"c2fd0de003889a09c4afcf676656a0d8a1fb706313ff7d509afb00c323c010cd"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,15 +30,16 @@ use bitcoin::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource};
|
||||||
use bitcoin::blockdata::locktime::{absolute, relative};
|
use bitcoin::blockdata::locktime::{absolute, relative};
|
||||||
use bitcoin::blockdata::witness::Witness;
|
use bitcoin::blockdata::witness::Witness;
|
||||||
use bitcoin::consensus::encode::deserialize;
|
use bitcoin::consensus::encode::deserialize;
|
||||||
|
use bitcoin::crypto::key::UntweakedPublicKey;
|
||||||
|
use bitcoin::crypto::{ecdsa, taproot};
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||||
use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
|
use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
|
||||||
use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
|
use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
|
||||||
use bitcoin::schnorr::{self, UntweakedPublicKey};
|
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
||||||
use bitcoin::sighash::{EcdsaSighashType, SchnorrSighashType};
|
|
||||||
use bitcoin::taproot::{ControlBlock, LeafVersion, TaprootBuilder, TaprootSpendInfo};
|
use bitcoin::taproot::{ControlBlock, LeafVersion, TaprootBuilder, TaprootSpendInfo};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
ecdsa, Address, Block, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence, Target,
|
Address, Block, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence, Target,
|
||||||
Transaction, TxIn, TxOut, Txid, Work,
|
Transaction, TxIn, TxOut, Txid, Work,
|
||||||
};
|
};
|
||||||
use secp256k1::Secp256k1;
|
use secp256k1::Secp256k1;
|
||||||
|
@ -339,15 +340,15 @@ fn serde_regression_proprietary_key() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serde_regression_schnorr_sig() {
|
fn serde_regression_taproot_sig() {
|
||||||
let s = include_str!("data/serde/schnorr_sig_hex");
|
let s = include_str!("data/serde/taproot_sig_hex");
|
||||||
let sig = schnorr::Signature {
|
let sig = taproot::Signature {
|
||||||
sig: secp256k1::schnorr::Signature::from_str(s.trim()).unwrap(),
|
sig: secp256k1::schnorr::Signature::from_str(s.trim()).unwrap(),
|
||||||
hash_ty: SchnorrSighashType::All,
|
hash_ty: TapSighashType::All,
|
||||||
};
|
};
|
||||||
|
|
||||||
let got = serialize(&sig).unwrap();
|
let got = serialize(&sig).unwrap();
|
||||||
let want = include_bytes!("data/serde/schnorr_sig_bincode") as &[_];
|
let want = include_bytes!("data/serde/taproot_sig_bincode") as &[_];
|
||||||
assert_eq!(got, want)
|
assert_eq!(got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue