Merge rust-bitcoin/rust-bitcoin#702: Separate signature hash types

8361129518 Add SchnorrSig type (sanket1729)
94cfe79170 Rename existing SigHashType to EcdsaSigHashType (sanket1729)
648b3975a5 Add SchnorrSigHashType::from_u8 (sanket1729)
410e8bf46c Rename sighash::SigHashType::SigHashType to SchnorrSigHashType (sanket1729)
fa112a793a Add EcdsaSig (sanket1729)

Pull request description:

  Fixes #670 . Separates `SchnorrSigHashType` and `LegacySigHashType`. Also adds the following new structs:

  ```rust
  pub struct SchnorrSig {
      /// The underlying schnorr signature
      pub sig: secp256k1::schnorrsig::Signature,
      /// The corresponding hash type
      pub hash_ty: SchnorrSigHashType,
  }

  pub struct EcdsaSig {
      /// The underlying DER serialized Signature
      pub sig: secp256k1::Signature,
      /// The corresponding hash type
      pub hash_ty: LegacySigHashType,
  }
  ```

  This code is currently minimal to aid reviews. We can at a later point implement (Encodeable, psbt::Serialize, FromHex, ToHex) etc in follow-up PRs.

ACKs for top commit:
  Kixunil:
    ACK 8361129518
  RCasatta:
    ACK 8361129518

Tree-SHA512: 800ddcb3677a4f19e9d1c2a7eb7e95b0a677e9135e1e99f9e42956fc6a3fc94f639403076b4925b3adba6fdd95f56a99c2e47d0310675ad51ce5e7453c7355b6
This commit is contained in:
Riccardo Casatta 2021-12-15 16:49:23 +01:00
commit 970f574968
No known key found for this signature in database
GPG Key ID: FD986A969E450397
9 changed files with 313 additions and 133 deletions

View File

@ -333,10 +333,10 @@ impl Transaction {
let sighash_type : u32 = sighash_type.into();
assert!(input_index < self.input.len()); // Panic on OOB
let (sighash, anyone_can_pay) = SigHashType::from_u32_consensus(sighash_type).split_anyonecanpay_flag();
let (sighash, anyone_can_pay) = EcdsaSigHashType::from_u32_consensus(sighash_type).split_anyonecanpay_flag();
// Special-case sighash_single bug because this is easy enough.
if sighash == SigHashType::Single && input_index >= self.output.len() {
if sighash == EcdsaSigHashType::Single && input_index >= self.output.len() {
writer.write_all(&[1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@ -365,22 +365,22 @@ impl Transaction {
tx.input.push(TxIn {
previous_output: input.previous_output,
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
sequence: if n != input_index && (sighash == SigHashType::Single || sighash == SigHashType::None) { 0 } else { input.sequence },
sequence: if n != input_index && (sighash == EcdsaSigHashType::Single || sighash == EcdsaSigHashType::None) { 0 } else { input.sequence },
witness: vec![],
});
}
}
// ..then all outputs
tx.output = match sighash {
SigHashType::All => self.output.clone(),
SigHashType::Single => {
EcdsaSigHashType::All => self.output.clone(),
EcdsaSigHashType::Single => {
let output_iter = self.output.iter()
.take(input_index + 1) // sign all outputs up to and including this one, but erase
.enumerate() // all of them except for this one
.map(|(n, out)| if n == input_index { out.clone() } else { TxOut::default() });
output_iter.collect()
}
SigHashType::None => vec![],
EcdsaSigHashType::None => vec![],
_ => unreachable!()
};
// hash the result
@ -673,10 +673,15 @@ impl fmt::Display for NonStandardSigHashType {
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl error::Error for NonStandardSigHashType {}
/// Legacy Hashtype of an input's signature
#[deprecated(since="0.28.0", note="Please use [`EcdsaSigHashType`] instead")]
pub type SigHashType = EcdsaSigHashType;
/// Hashtype of an input's signature, encoded in the last byte of the signature
/// Fixed values so they can be casted as integer types for encoding
/// See also [`crate::SchnorrSigHashType`]
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum SigHashType {
pub enum EcdsaSigHashType {
/// 0x1: Sign all outputs
All = 0x01,
/// 0x2: Sign no outputs --- anyone can choose the destination
@ -693,54 +698,54 @@ pub enum SigHashType {
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means)
SinglePlusAnyoneCanPay = 0x83
}
serde_string_impl!(SigHashType, "a SigHashType data");
serde_string_impl!(EcdsaSigHashType, "a EcdsaSigHashType data");
impl fmt::Display for SigHashType {
impl fmt::Display for EcdsaSigHashType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
SigHashType::All => "SIGHASH_ALL",
SigHashType::None => "SIGHASH_NONE",
SigHashType::Single => "SIGHASH_SINGLE",
SigHashType::AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
SigHashType::NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
SigHashType::SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
EcdsaSigHashType::All => "SIGHASH_ALL",
EcdsaSigHashType::None => "SIGHASH_NONE",
EcdsaSigHashType::Single => "SIGHASH_SINGLE",
EcdsaSigHashType::AllPlusAnyoneCanPay => "SIGHASH_ALL|SIGHASH_ANYONECANPAY",
EcdsaSigHashType::NonePlusAnyoneCanPay => "SIGHASH_NONE|SIGHASH_ANYONECANPAY",
EcdsaSigHashType::SinglePlusAnyoneCanPay => "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY",
};
f.write_str(s)
}
}
impl str::FromStr for SigHashType {
impl str::FromStr for EcdsaSigHashType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.as_ref() {
"SIGHASH_ALL" => Ok(SigHashType::All),
"SIGHASH_NONE" => Ok(SigHashType::None),
"SIGHASH_SINGLE" => Ok(SigHashType::Single),
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(SigHashType::AllPlusAnyoneCanPay),
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(SigHashType::NonePlusAnyoneCanPay),
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(SigHashType::SinglePlusAnyoneCanPay),
"SIGHASH_ALL" => Ok(EcdsaSigHashType::All),
"SIGHASH_NONE" => Ok(EcdsaSigHashType::None),
"SIGHASH_SINGLE" => Ok(EcdsaSigHashType::Single),
"SIGHASH_ALL|SIGHASH_ANYONECANPAY" => Ok(EcdsaSigHashType::AllPlusAnyoneCanPay),
"SIGHASH_NONE|SIGHASH_ANYONECANPAY" => Ok(EcdsaSigHashType::NonePlusAnyoneCanPay),
"SIGHASH_SINGLE|SIGHASH_ANYONECANPAY" => Ok(EcdsaSigHashType::SinglePlusAnyoneCanPay),
_ => Err("can't recognize SIGHASH string".to_string())
}
}
}
impl SigHashType {
impl EcdsaSigHashType {
/// Break the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean
pub(crate) fn split_anyonecanpay_flag(self) -> (SigHashType, bool) {
pub(crate) fn split_anyonecanpay_flag(self) -> (EcdsaSigHashType, bool) {
match self {
SigHashType::All => (SigHashType::All, false),
SigHashType::None => (SigHashType::None, false),
SigHashType::Single => (SigHashType::Single, false),
SigHashType::AllPlusAnyoneCanPay => (SigHashType::All, true),
SigHashType::NonePlusAnyoneCanPay => (SigHashType::None, true),
SigHashType::SinglePlusAnyoneCanPay => (SigHashType::Single, true)
EcdsaSigHashType::All => (EcdsaSigHashType::All, false),
EcdsaSigHashType::None => (EcdsaSigHashType::None, false),
EcdsaSigHashType::Single => (EcdsaSigHashType::Single, false),
EcdsaSigHashType::AllPlusAnyoneCanPay => (EcdsaSigHashType::All, true),
EcdsaSigHashType::NonePlusAnyoneCanPay => (EcdsaSigHashType::None, true),
EcdsaSigHashType::SinglePlusAnyoneCanPay => (EcdsaSigHashType::Single, true)
}
}
/// Reads a 4-byte uint32 as a sighash type.
#[deprecated(since="0.26.1", note="please use `from_u32_consensus` or `from_u32_standard` instead")]
pub fn from_u32(n: u32) -> SigHashType {
pub fn from_u32(n: u32) -> EcdsaSigHashType {
Self::from_u32_consensus(n)
}
@ -748,7 +753,7 @@ impl SigHashType {
///
/// **Note**: this replicates consensus behaviour, for current standardness rules correctness
/// you probably want [Self::from_u32_standard].
pub fn from_u32_consensus(n: u32) -> SigHashType {
pub fn from_u32_consensus(n: u32) -> EcdsaSigHashType {
// In Bitcoin Core, the SignatureHash function will mask the (int32) value with
// 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.
@ -756,29 +761,29 @@ impl SigHashType {
let mask = 0x1f | 0x80;
match n & mask {
// "real" sighashes
0x01 => SigHashType::All,
0x02 => SigHashType::None,
0x03 => SigHashType::Single,
0x81 => SigHashType::AllPlusAnyoneCanPay,
0x82 => SigHashType::NonePlusAnyoneCanPay,
0x83 => SigHashType::SinglePlusAnyoneCanPay,
0x01 => EcdsaSigHashType::All,
0x02 => EcdsaSigHashType::None,
0x03 => EcdsaSigHashType::Single,
0x81 => EcdsaSigHashType::AllPlusAnyoneCanPay,
0x82 => EcdsaSigHashType::NonePlusAnyoneCanPay,
0x83 => EcdsaSigHashType::SinglePlusAnyoneCanPay,
// catchalls
x if x & 0x80 == 0x80 => SigHashType::AllPlusAnyoneCanPay,
_ => SigHashType::All
x if x & 0x80 == 0x80 => EcdsaSigHashType::AllPlusAnyoneCanPay,
_ => EcdsaSigHashType::All
}
}
/// Read a 4-byte uint32 as a standard sighash type, returning an error if the type
/// is non standard.
pub fn from_u32_standard(n: u32) -> Result<SigHashType, NonStandardSigHashType> {
pub fn from_u32_standard(n: u32) -> Result<EcdsaSigHashType, NonStandardSigHashType> {
match n {
// Standard sighashes, see https://github.com/bitcoin/bitcoin/blob/b805dbb0b9c90dadef0424e5b3bf86ac308e103e/src/script/interpreter.cpp#L189-L198
0x01 => Ok(SigHashType::All),
0x02 => Ok(SigHashType::None),
0x03 => Ok(SigHashType::Single),
0x81 => Ok(SigHashType::AllPlusAnyoneCanPay),
0x82 => Ok(SigHashType::NonePlusAnyoneCanPay),
0x83 => Ok(SigHashType::SinglePlusAnyoneCanPay),
0x01 => Ok(EcdsaSigHashType::All),
0x02 => Ok(EcdsaSigHashType::None),
0x03 => Ok(EcdsaSigHashType::Single),
0x81 => Ok(EcdsaSigHashType::AllPlusAnyoneCanPay),
0x82 => Ok(EcdsaSigHashType::NonePlusAnyoneCanPay),
0x83 => Ok(EcdsaSigHashType::SinglePlusAnyoneCanPay),
_ => Err(NonStandardSigHashType)
}
}
@ -787,8 +792,8 @@ impl SigHashType {
pub fn as_u32(self) -> u32 { self as u32 }
}
impl From<SigHashType> for u32 {
fn from(t: SigHashType) -> u32 {
impl From<EcdsaSigHashType> for u32 {
fn from(t: EcdsaSigHashType) -> u32 {
t.as_u32()
}
}
@ -807,7 +812,7 @@ mod tests {
use hashes::hex::FromHex;
use hash_types::*;
use SigHashType;
use super::EcdsaSigHashType;
use util::sighash::SigHashCache;
#[test]
@ -1088,15 +1093,15 @@ mod tests {
#[test]
fn test_sighashtype_fromstr_display() {
let sighashtypes = vec![("SIGHASH_ALL", SigHashType::All),
("SIGHASH_NONE", SigHashType::None),
("SIGHASH_SINGLE", SigHashType::Single),
("SIGHASH_ALL|SIGHASH_ANYONECANPAY", SigHashType::AllPlusAnyoneCanPay),
("SIGHASH_NONE|SIGHASH_ANYONECANPAY", SigHashType::NonePlusAnyoneCanPay),
("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", SigHashType::SinglePlusAnyoneCanPay)];
let sighashtypes = vec![("SIGHASH_ALL", EcdsaSigHashType::All),
("SIGHASH_NONE", EcdsaSigHashType::None),
("SIGHASH_SINGLE", EcdsaSigHashType::Single),
("SIGHASH_ALL|SIGHASH_ANYONECANPAY", EcdsaSigHashType::AllPlusAnyoneCanPay),
("SIGHASH_NONE|SIGHASH_ANYONECANPAY", EcdsaSigHashType::NonePlusAnyoneCanPay),
("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY", EcdsaSigHashType::SinglePlusAnyoneCanPay)];
for (s, sht) in sighashtypes {
assert_eq!(sht.to_string(), s);
assert_eq!(SigHashType::from_str(s).unwrap(), sht);
assert_eq!(EcdsaSigHashType::from_str(s).unwrap(), sht);
}
let sht_mistakes = vec![
"SIGHASH_ALL | SIGHASH_ANYONECANPAY",
@ -1111,7 +1116,7 @@ mod tests {
"SigHash_NONE",
];
for s in sht_mistakes {
assert_eq!(SigHashType::from_str(s).unwrap_err(), "can't recognize SIGHASH string");
assert_eq!(EcdsaSigHashType::from_str(s).unwrap_err(), "can't recognize SIGHASH string");
}
}
@ -1120,10 +1125,10 @@ mod tests {
fn test_sighashtype_standard() {
let nonstandard_hashtype = 0x04;
// This type is not well defined, by consensus it becomes ALL
assert_eq!(SigHashType::from_u32(nonstandard_hashtype), SigHashType::All);
assert_eq!(SigHashType::from_u32_consensus(nonstandard_hashtype), SigHashType::All);
assert_eq!(EcdsaSigHashType::from_u32(nonstandard_hashtype), EcdsaSigHashType::All);
assert_eq!(EcdsaSigHashType::from_u32_consensus(nonstandard_hashtype), EcdsaSigHashType::All);
// But it's policy-invalid to use it!
assert_eq!(SigHashType::from_u32_standard(nonstandard_hashtype), Err(NonStandardSigHashType));
assert_eq!(EcdsaSigHashType::from_u32_standard(nonstandard_hashtype), Err(NonStandardSigHashType));
}
// These test vectors were stolen from libbtc, which is Copyright 2014 Jonas Schnelli MIT

View File

@ -123,7 +123,7 @@ pub use blockdata::transaction::Transaction;
pub use blockdata::transaction::TxIn;
pub use blockdata::transaction::TxOut;
pub use blockdata::transaction::OutPoint;
pub use blockdata::transaction::SigHashType;
pub use blockdata::transaction::EcdsaSigHashType;
pub use consensus::encode::VarInt;
pub use network::constants::Network;
pub use util::Error;
@ -133,9 +133,10 @@ pub use util::amount::Amount;
pub use util::amount::Denomination;
pub use util::amount::SignedAmount;
pub use util::merkleblock::MerkleBlock;
pub use util::sighash::SchnorrSigHashType;
pub use util::ecdsa;
pub use util::schnorr;
pub use util::ecdsa::{self, EcdsaSig, EcdsaSigError};
pub use util::schnorr::{self, SchnorrSig, SchnorrSigError};
#[deprecated(since = "0.26.1", note = "Please use `ecdsa::PrivateKey` instead")]
pub use util::ecdsa::PrivateKey;
#[deprecated(since = "0.26.1", note = "Please use `ecdsa::PublicKey` instead")]

View File

@ -22,7 +22,7 @@
use hashes::Hash;
use hash_types::SigHash;
use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxIn, SigHashType};
use blockdata::transaction::{Transaction, TxIn, EcdsaSigHashType};
use consensus::{encode, Encodable};
use prelude::*;
@ -131,7 +131,7 @@ impl<R: Deref<Target=Transaction>> SigHashCache<R> {
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: SigHashType,
sighash_type: EcdsaSigHashType,
) -> Result<(), encode::Error> {
self.cache
.segwit_encode_signing_data_to(writer, input_index, script_code, value, sighash_type.into())
@ -146,7 +146,7 @@ impl<R: Deref<Target=Transaction>> SigHashCache<R> {
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: SigHashType
sighash_type: EcdsaSigHashType
) -> SigHash {
let mut enc = SigHash::engine();
self.encode_signing_data_to(&mut enc, input_index, script_code, value, sighash_type)
@ -165,7 +165,7 @@ impl<R: DerefMut<Target=Transaction>> SigHashCache<R> {
/// panics if `input_index` is out of bounds with respect of the number of inputs
///
/// ```
/// use bitcoin::blockdata::transaction::{Transaction, SigHashType};
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSigHashType};
/// use bitcoin::util::bip143::SigHashCache;
/// use bitcoin::Script;
///
@ -175,7 +175,7 @@ impl<R: DerefMut<Target=Transaction>> SigHashCache<R> {
/// let mut sig_hasher = SigHashCache::new(&mut tx_to_sign);
/// for inp in 0..input_count {
/// let prevout_script = Script::new();
/// let _sighash = sig_hasher.signature_hash(inp, &prevout_script, 42, SigHashType::All);
/// let _sighash = sig_hasher.signature_hash(inp, &prevout_script, 42, EcdsaSigHashType::All);
/// // ... sign the sighash
/// sig_hasher.access_witness(inp).push(Vec::new());
/// }
@ -212,7 +212,7 @@ mod tests {
let raw_expected = SigHash::from_hex(expected_result).unwrap();
let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap();
let mut cache = SigHashCache::new(&tx);
let sighash_type = SigHashType::from_u32_consensus(hash_type);
let sighash_type = EcdsaSigHashType::from_u32_consensus(hash_type);
let actual_result = cache.signature_hash(input_index, &script, value, sighash_type);
assert_eq!(actual_result, expected_result);
}

View File

@ -29,6 +29,8 @@ use hashes::{Hash, hash160};
use hash_types::{PubkeyHash, WPubkeyHash};
use util::base58;
use util::key::Error;
use blockdata::transaction::EcdsaSigHashType;
/// A Bitcoin ECDSA public key
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -413,6 +415,72 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey {
}
}
/// An ECDSA signature with the corresponding hash type.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct EcdsaSig {
/// The underlying ECDSA Signature
pub sig: secp256k1::Signature,
/// The corresponding hash type
pub hash_ty: EcdsaSigHashType,
}
impl EcdsaSig {
/// Deserialize from slice
pub fn from_slice(sl: &[u8]) -> Result<Self, EcdsaSigError> {
let (hash_ty, sig) = sl.split_last()
.ok_or(EcdsaSigError::EmptySignature)?;
let hash_ty = EcdsaSigHashType::from_u32_standard(*hash_ty as u32)
.map_err(|_| EcdsaSigError::NonStandardSigHashType(*hash_ty))?;
let sig = secp256k1::Signature::from_der(sig)
.map_err(EcdsaSigError::Secp256k1)?;
Ok(EcdsaSig { sig, hash_ty })
}
/// Serialize EcdsaSig
pub fn to_vec(&self) -> Vec<u8> {
// TODO: add support to serialize to a writer to SerializedSig
let mut ser_sig = self.sig.serialize_der().to_vec();
ser_sig.push(self.hash_ty.as_u32() as u8);
ser_sig
}
}
/// A key-related error.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum EcdsaSigError {
/// Base58 encoding error
NonStandardSigHashType(u8),
/// Empty Signature
EmptySignature,
/// secp256k1-related error
Secp256k1(secp256k1::Error),
}
impl fmt::Display for EcdsaSigError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
EcdsaSigError::NonStandardSigHashType(hash_ty) =>
write!(f, "Non standard signature hash type {}", hash_ty),
EcdsaSigError::Secp256k1(ref e) =>
write!(f, "Invalid Ecdsa signature: {}", e),
EcdsaSigError::EmptySignature =>
write!(f, "Empty ECDSA signature"),
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl ::std::error::Error for EcdsaSigError {}
impl From<secp256k1::Error> for EcdsaSigError {
fn from(e: secp256k1::Error) -> EcdsaSigError {
EcdsaSigError::Secp256k1(e)
}
}
#[cfg(test)]
mod tests {
use io;

View File

@ -17,7 +17,7 @@ use prelude::*;
use io;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxOut};
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut};
use consensus::encode;
use util::bip32::KeySource;
use hashes::{self, hash160, ripemd160, sha256, sha256d};
@ -76,7 +76,7 @@ pub struct Input {
pub partial_sigs: BTreeMap<PublicKey, Vec<u8>>,
/// The sighash type to be used for this input. Signatures for this input
/// must use the sighash type.
pub sighash_type: Option<SigHashType>,
pub sighash_type: Option<EcdsaSigHashType>,
/// The redeem script for this input.
pub redeem_script: Option<Script>,
/// The witness script for this input.
@ -137,7 +137,7 @@ impl Map for Input {
}
PSBT_IN_SIGHASH_TYPE => {
impl_psbt_insert_pair! {
self.sighash_type <= <raw_key: _>|<raw_value: SigHashType>
self.sighash_type <= <raw_key: _>|<raw_value: EcdsaSigHashType>
}
}
PSBT_IN_REDEEM_SCRIPT => {

View File

@ -507,7 +507,7 @@ mod tests {
use hash_types::Txid;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxIn, TxOut, OutPoint};
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxIn, TxOut, OutPoint};
use consensus::encode::serialize_hex;
use util::psbt::map::{Map, Input, Output};
use util::psbt::raw;
@ -733,7 +733,7 @@ mod tests {
);
assert_eq!(
(&psbt.inputs[0].sighash_type).as_ref().unwrap(),
&SigHashType::All
&EcdsaSigHashType::All
);
}

View File

@ -23,7 +23,7 @@ use prelude::*;
use io;
use blockdata::script::Script;
use blockdata::transaction::{SigHashType, Transaction, TxOut};
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut};
use consensus::encode::{self, serialize, Decodable};
use util::bip32::{ChildNumber, Fingerprint, KeySource};
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
@ -126,16 +126,16 @@ impl Deserialize for Vec<u8> {
}
}
impl Serialize for SigHashType {
impl Serialize for EcdsaSigHashType {
fn serialize(&self) -> Vec<u8> {
serialize(&self.as_u32())
}
}
impl Deserialize for SigHashType {
impl Deserialize for EcdsaSigHashType {
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
let raw: u32 = encode::deserialize(bytes)?;
let rv: SigHashType = SigHashType::from_u32_consensus(raw);
let rv: EcdsaSigHashType = EcdsaSigHashType::from_u32_consensus(raw);
if rv.as_u32() == raw {
Ok(rv)

View File

@ -17,11 +17,14 @@
//! Schnorr key types.
//!
use core::fmt;
use prelude::*;
pub use secp256k1::schnorrsig::{PublicKey, KeyPair};
use secp256k1::{Secp256k1, Verification, constants};
use secp256k1::{self, Secp256k1, Verification, constants};
use hashes::Hash;
use util::taproot::{TapBranchHash, TapTweakHash};
use core::fmt;
use SchnorrSigHashType;
/// Untweaked Schnorr public key
pub type UntweakedPublicKey = PublicKey;
@ -104,3 +107,87 @@ impl TweakedPublicKey {
self.0.serialize()
}
}
/// A BIP340-341 serialized schnorr signature with the corresponding hash type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SchnorrSig {
/// The underlying schnorr signature
pub sig: secp256k1::schnorrsig::Signature,
/// The corresponding hash type
pub hash_ty: SchnorrSigHashType,
}
impl SchnorrSig {
/// Deserialize from slice
pub fn from_slice(sl: &[u8]) -> Result<Self, SchnorrSigError> {
match sl.len() {
64 => {
// default type
let sig = secp256k1::schnorrsig::Signature::from_slice(sl)
.map_err(SchnorrSigError::Secp256k1)?;
return Ok( SchnorrSig { sig, hash_ty : SchnorrSigHashType::Default });
},
65 => {
let (hash_ty, sig) = sl.split_last().expect("Slice len checked == 65");
let hash_ty = SchnorrSigHashType::from_u8(*hash_ty)
.map_err(|_| SchnorrSigError::InvalidSighashType(*hash_ty))?;
let sig = secp256k1::schnorrsig::Signature::from_slice(sig)
.map_err(SchnorrSigError::Secp256k1)?;
Ok(SchnorrSig { sig, hash_ty })
}
len => {
Err(SchnorrSigError::InvalidSchnorrSigSize(len))
}
}
}
/// Serialize SchnorrSig
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)]
pub enum SchnorrSigError {
/// Base58 encoding error
InvalidSighashType(u8),
/// Signature has valid size but does not parse correctly
Secp256k1(secp256k1::Error),
/// Invalid schnorr signature size
InvalidSchnorrSigSize(usize),
}
impl fmt::Display for SchnorrSigError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SchnorrSigError::InvalidSighashType(hash_ty) =>
write!(f, "Invalid signature hash type {}", hash_ty),
SchnorrSigError::Secp256k1(ref e) =>
write!(f, "Schnorr Signature has correct len, but is malformed : {}", e),
SchnorrSigError::InvalidSchnorrSigSize(sz) =>
write!(f, "Invalid Schnorr signature size: {}", sz),
}
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl ::std::error::Error for SchnorrSigError {}
impl From<secp256k1::Error> for SchnorrSigError {
fn from(e: secp256k1::Error) -> SchnorrSigError {
SchnorrSigError::Secp256k1(e)
}
}

View File

@ -20,7 +20,7 @@
//! and legacy (before Bip143).
//!
pub use blockdata::transaction::SigHashType as LegacySigHashType;
pub use blockdata::transaction::EcdsaSigHashType;
use consensus::{encode, Encodable};
use core::fmt;
use core::ops::{Deref, DerefMut};
@ -79,7 +79,7 @@ struct TaprootCache {
}
/// Contains outputs of previous transactions.
/// In the case [`SigHashType`] variant is `ANYONECANPAY`, [`Prevouts::One`] may be provided
/// In the case [`SchnorrSigHashType`] variant is `ANYONECANPAY`, [`Prevouts::One`] may be provided
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Prevouts<'u> {
/// `One` variant allows to provide the single Prevout needed. It's useful for example
@ -104,8 +104,8 @@ pub struct ScriptPath<'s> {
/// Hashtype of an input's signature, encoded in the last byte of the signature
/// Fixed values so they can be casted as integer types for encoding
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum SigHashType {
/// 0x0: Used when not explicitly specified, defaulting to [`SigHashType::All`]
pub enum SchnorrSigHashType {
/// 0x0: Used when not explicitly specified, defaulting to [`SchnorrSigHashType::All`]
Default = 0x00,
/// 0x1: Sign all outputs
All = 0x01,
@ -164,6 +164,9 @@ pub enum Error {
/// Annex must be at least one byte long and the first bytes must be `0x50`
WrongAnnex,
/// Invalid Sighash type
InvalidSigHashType(u8),
}
impl fmt::Display for Error {
@ -176,6 +179,7 @@ impl fmt::Display for Error {
Error::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`"),
Error::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),
}
}
}
@ -229,31 +233,45 @@ impl<'s> ScriptPath<'s> {
}
}
impl From<LegacySigHashType> for SigHashType {
fn from(s: LegacySigHashType) -> Self {
impl From<EcdsaSigHashType> for SchnorrSigHashType {
fn from(s: EcdsaSigHashType) -> Self {
match s {
LegacySigHashType::All => SigHashType::All,
LegacySigHashType::None => SigHashType::None,
LegacySigHashType::Single => SigHashType::Single,
LegacySigHashType::AllPlusAnyoneCanPay => SigHashType::AllPlusAnyoneCanPay,
LegacySigHashType::NonePlusAnyoneCanPay => SigHashType::NonePlusAnyoneCanPay,
LegacySigHashType::SinglePlusAnyoneCanPay => SigHashType::SinglePlusAnyoneCanPay,
EcdsaSigHashType::All => SchnorrSigHashType::All,
EcdsaSigHashType::None => SchnorrSigHashType::None,
EcdsaSigHashType::Single => SchnorrSigHashType::Single,
EcdsaSigHashType::AllPlusAnyoneCanPay => SchnorrSigHashType::AllPlusAnyoneCanPay,
EcdsaSigHashType::NonePlusAnyoneCanPay => SchnorrSigHashType::NonePlusAnyoneCanPay,
EcdsaSigHashType::SinglePlusAnyoneCanPay => SchnorrSigHashType::SinglePlusAnyoneCanPay,
}
}
}
impl SigHashType {
impl SchnorrSigHashType {
/// Break the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean
pub(crate) fn split_anyonecanpay_flag(self) -> (SigHashType, bool) {
pub(crate) fn split_anyonecanpay_flag(self) -> (SchnorrSigHashType, bool) {
match self {
SigHashType::Default => (SigHashType::Default, false),
SigHashType::All => (SigHashType::All, false),
SigHashType::None => (SigHashType::None, false),
SigHashType::Single => (SigHashType::Single, false),
SigHashType::AllPlusAnyoneCanPay => (SigHashType::All, true),
SigHashType::NonePlusAnyoneCanPay => (SigHashType::None, true),
SigHashType::SinglePlusAnyoneCanPay => (SigHashType::Single, true),
SigHashType::Reserved => (SigHashType::Reserved, false),
SchnorrSigHashType::Default => (SchnorrSigHashType::Default, false),
SchnorrSigHashType::All => (SchnorrSigHashType::All, false),
SchnorrSigHashType::None => (SchnorrSigHashType::None, false),
SchnorrSigHashType::Single => (SchnorrSigHashType::Single, false),
SchnorrSigHashType::AllPlusAnyoneCanPay => (SchnorrSigHashType::All, true),
SchnorrSigHashType::NonePlusAnyoneCanPay => (SchnorrSigHashType::None, true),
SchnorrSigHashType::SinglePlusAnyoneCanPay => (SchnorrSigHashType::Single, true),
SchnorrSigHashType::Reserved => (SchnorrSigHashType::Reserved, false),
}
}
/// Create a [`SchnorrSigHashType`] from raw u8
pub fn from_u8(hash_ty: u8) -> Result<Self, Error> {
match hash_ty {
0x00 => Ok(SchnorrSigHashType::Default),
0x01 => Ok(SchnorrSigHashType::All),
0x02 => Ok(SchnorrSigHashType::None),
0x03 => Ok(SchnorrSigHashType::Single),
0x81 => Ok(SchnorrSigHashType::AllPlusAnyoneCanPay),
0x82 => Ok(SchnorrSigHashType::NonePlusAnyoneCanPay),
0x83 => Ok(SchnorrSigHashType::SinglePlusAnyoneCanPay),
x => Err(Error::InvalidSigHashType(x)),
}
}
}
@ -281,7 +299,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
prevouts: &Prevouts,
annex: Option<Annex>,
script_path: Option<ScriptPath>,
sighash_type: SigHashType,
sighash_type: SchnorrSigHashType,
) -> Result<(), Error> {
prevouts.check_all(&self.tx)?;
@ -321,7 +339,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
// 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.
if sighash != SigHashType::None && sighash != SigHashType::Single {
if sighash != SchnorrSigHashType::None && sighash != SchnorrSigHashType::Single {
self.common_cache().outputs.consensus_encode(&mut writer)?;
}
@ -376,7 +394,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
// * Data about this output:
// If hash_type & 3 equals SIGHASH_SINGLE:
// sha_single_output (32): the SHA256 of the corresponding output in CTxOut format.
if sighash == SigHashType::Single {
if sighash == SchnorrSigHashType::Single {
let mut enc = sha256::Hash::engine();
self.tx
.output
@ -420,7 +438,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
prevouts: &Prevouts,
annex: Option<Annex>,
script_path: Option<ScriptPath>,
sighash_type: SigHashType,
sighash_type: SchnorrSigHashType,
) -> Result<TapSighashHash, Error> {
let mut enc = TapSighashHash::engine();
self.taproot_encode_signing_data_to(
@ -442,7 +460,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: LegacySigHashType,
sighash_type: EcdsaSigHashType,
) -> Result<(), Error> {
let zero_hash = sha256d::Hash::default();
@ -457,8 +475,8 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
}
if !anyone_can_pay
&& sighash != LegacySigHashType::Single
&& sighash != LegacySigHashType::None
&& sighash != EcdsaSigHashType::Single
&& sighash != EcdsaSigHashType::None
{
self.segwit_cache()
.sequences
@ -484,9 +502,9 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
txin.sequence.consensus_encode(&mut writer)?;
}
if sighash != LegacySigHashType::Single && sighash != LegacySigHashType::None {
if sighash != EcdsaSigHashType::Single && sighash != EcdsaSigHashType::None {
self.segwit_cache().outputs.consensus_encode(&mut writer)?;
} else if sighash == LegacySigHashType::Single && input_index < self.tx.output.len() {
} else if sighash == EcdsaSigHashType::Single && input_index < self.tx.output.len() {
let mut single_enc = SigHash::engine();
self.tx.output[input_index].consensus_encode(&mut single_enc)?;
SigHash::from_engine(single_enc).consensus_encode(&mut writer)?;
@ -505,7 +523,7 @@ impl<R: Deref<Target = Transaction>> SigHashCache<R> {
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: LegacySigHashType,
sighash_type: EcdsaSigHashType,
) -> Result<SigHash, Error> {
let mut enc = SigHash::engine();
self.segwit_encode_signing_data_to(
@ -627,7 +645,7 @@ impl<R: DerefMut<Target = Transaction>> SigHashCache<R> {
///
/// This allows in-line signing such as
/// ```
/// use bitcoin::blockdata::transaction::{Transaction, SigHashType};
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSigHashType};
/// use bitcoin::util::sighash::SigHashCache;
/// use bitcoin::Script;
///
@ -637,7 +655,7 @@ impl<R: DerefMut<Target = Transaction>> SigHashCache<R> {
/// let mut sig_hasher = SigHashCache::new(&mut tx_to_sign);
/// for inp in 0..input_count {
/// let prevout_script = Script::new();
/// let _sighash = sig_hasher.segwit_signature_hash(inp, &prevout_script, 42, SigHashType::All);
/// let _sighash = sig_hasher.segwit_signature_hash(inp, &prevout_script, 42, EcdsaSigHashType::All);
/// // ... sign the sighash
/// sig_hasher.witness_mut(inp).unwrap().push(Vec::new());
/// }
@ -681,10 +699,11 @@ impl<'a> Encodable for Annex<'a> {
#[cfg(test)]
mod tests {
use super::*;
use consensus::deserialize;
use hashes::hex::FromHex;
use hashes::{Hash, HashEngine};
use util::sighash::{Annex, Error, Prevouts, ScriptPath, SigHashCache, SigHashType};
use util::sighash::{Annex, Error, Prevouts, ScriptPath, SigHashCache};
use util::taproot::TapSighashHash;
use {Script, Transaction, TxIn, TxOut};
@ -709,7 +728,7 @@ mod tests {
"01365724000000000023542156b39dab4f8f3508e0432cfb41fab110170acaa2d4c42539cb90a4dc7c093bc500",
0,
"33ca0ebfb4a945eeee9569fc0f5040221275f88690b7f8592ada88ce3bdf6703",
SigHashType::Default, None,None,
SchnorrSigHashType::Default, None,None,
);
test_taproot_sighash(
@ -717,7 +736,7 @@ mod tests {
"02591f220000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece48fb310000000000225120f25ad35583ea31998d968871d7de1abd2a52f6fe4178b54ea158274806ff4ece",
1,
"626ab955d58c9a8a600a0c580549d06dc7da4e802eb2a531f62a588e430967a8",
SigHashType::All, None,None,
SchnorrSigHashType::All, None,None,
);
test_taproot_sighash(
@ -725,7 +744,7 @@ mod tests {
"01c4811000000000002251201bf9297d0a2968ae6693aadd0fa514717afefd218087a239afb7418e2d22e65c",
0,
"dfa9437f9c9a1d1f9af271f79f2f5482f287cdb0d2e03fa92c8a9b216cc6061c",
SigHashType::AllPlusAnyoneCanPay, None,None,
SchnorrSigHashType::AllPlusAnyoneCanPay, None,None,
);
test_taproot_sighash(
@ -733,7 +752,7 @@ mod tests {
"0144c84d0000000000225120e3f2107989c88e67296ab2faca930efa2e3a5bd3ff0904835a11c9e807458621",
0,
"3129de36a5d05fff97ffca31eb75fcccbbbc27b3147a7a36a9e4b45d8b625067",
SigHashType::None, None,None,
SchnorrSigHashType::None, None,None,
);
test_taproot_sighash(
@ -741,7 +760,7 @@ mod tests {
"013fed110000000000225120eb536ae8c33580290630fc495046e998086a64f8f33b93b07967d9029b265c55",
0,
"2441e8b0e063a2083ee790f14f2045022f07258ddde5ee01de543c9e789d80ae",
SigHashType::NonePlusAnyoneCanPay, None,None,
SchnorrSigHashType::NonePlusAnyoneCanPay, None,None,
);
test_taproot_sighash(
@ -749,7 +768,7 @@ mod tests {
"01efa558000000000022512007071ea3dc7e331b0687d0193d1e6d6ed10e645ef36f10ef8831d5e522ac9e80",
0,
"30239345177cadd0e3ea413d49803580abb6cb27971b481b7788a78d35117a88",
SigHashType::Single, None,None,
SchnorrSigHashType::Single, None,None,
);
test_taproot_sighash(
@ -757,7 +776,7 @@ mod tests {
"0107af4e00000000002251202c36d243dfc06cb56a248e62df27ecba7417307511a81ae61aa41c597a929c69",
0,
"bf9c83f26c6dd16449e4921f813f551c4218e86f2ec906ca8611175b41b566df",
SigHashType::SinglePlusAnyoneCanPay, None,None,
SchnorrSigHashType::SinglePlusAnyoneCanPay, None,None,
);
}
@ -768,7 +787,7 @@ mod tests {
"01ea49260000000000225120ab5e9800806bf18cb246edcf5fe63441208fe955a4b5a35bbff65f5db622a010",
0,
"3b003000add359a364a156e73e02846782a59d0d95ca8c4638aaad99f2ef915c",
SigHashType::SinglePlusAnyoneCanPay,
SchnorrSigHashType::SinglePlusAnyoneCanPay,
Some("507b979802e62d397acb29f56743a791894b99372872fc5af06a4f6e8d242d0615cda53062bb20e6ec79756fe39183f0c128adfe85559a8fa042b042c018aa8010143799e44f0893c40e1e"),
None,
);
@ -781,7 +800,7 @@ mod tests {
"011bec34000000000022512028055142ea437db73382e991861446040b61dd2185c4891d7daf6893d79f7182",
0,
"d66de5274a60400c7b08c86ba6b7f198f40660079edf53aca89d2a9501317f2e",
SigHashType::All,
SchnorrSigHashType::All,
None,
Some("20cc4e1107aea1d170c5ff5b6817e1303010049724fb3caa7941792ea9d29b3e2bacab"),
);
@ -794,7 +813,7 @@ mod tests {
"011458360000000000225120a7baec3fb9f84614e3899fcc010c638f80f13539344120e1f4d8b68a9a011a13",
0,
"a0042aa434f9a75904b64043f2a283f8b4c143c7f4f7f49a6cbe5b9f745f4c15",
SigHashType::All,
SchnorrSigHashType::All,
Some("50a6272b470e1460e3332ade7bb14b81671c564fb6245761bd5bd531394b28860e0b3808ab229fb51791fb6ae6fa82d915b2efb8f6df83ae1f5ab3db13e30928875e2a22b749d89358de481f19286cd4caa792ce27f9559082d227a731c5486882cc707f83da361c51b7aadd9a0cf68fe7480c410fa137b454482d9a1ebf0f96d760b4d61426fc109c6e8e99a508372c45caa7b000a41f8251305da3f206c1849985ba03f3d9592832b4053afbd23ab25d0465df0bc25a36c223aacf8e04ec736a418c72dc319e4da3e972e349713ca600965e7c665f2090d5a70e241ac164115a1f5639f28b1773327715ca307ace64a2de7f0e3df70a2ffee3857689f909c0dad46d8a20fa373a4cc6eed6d4c9806bf146f0d76baae1"),
Some("7520ab9160dd8299dc1367659be3e8f66781fe440d52940c7f8d314a89b9f2698d406ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6ead6eadac"),
);
@ -811,27 +830,27 @@ mod tests {
let mut c = SigHashCache::new(&dumb_tx);
assert_eq!(
c.taproot_signature_hash(0, &Prevouts::All(&vec![]), None, None, SigHashType::All),
c.taproot_signature_hash(0, &Prevouts::All(&vec![]), None, None, SchnorrSigHashType::All),
Err(Error::PrevoutsSize)
);
let two = vec![TxOut::default(), TxOut::default()];
let too_many_prevouts = Prevouts::All(&two);
assert_eq!(
c.taproot_signature_hash(0, &too_many_prevouts, None, None, SigHashType::All),
c.taproot_signature_hash(0, &too_many_prevouts, None, None, SchnorrSigHashType::All),
Err(Error::PrevoutsSize)
);
let tx_out = TxOut::default();
let prevout = Prevouts::One(1, &tx_out);
assert_eq!(
c.taproot_signature_hash(0, &prevout, None, None, SigHashType::All),
c.taproot_signature_hash(0, &prevout, None, None, SchnorrSigHashType::All),
Err(Error::PrevoutKind)
);
assert_eq!(
c.taproot_signature_hash(0, &prevout, None, None, SigHashType::AllPlusAnyoneCanPay),
c.taproot_signature_hash(0, &prevout, None, None, SchnorrSigHashType::AllPlusAnyoneCanPay),
Err(Error::PrevoutIndex)
);
assert_eq!(
c.taproot_signature_hash(10, &prevout, None, None, SigHashType::AllPlusAnyoneCanPay),
c.taproot_signature_hash(10, &prevout, None, None, SchnorrSigHashType::AllPlusAnyoneCanPay),
Err(Error::IndexOutOfInputsBounds {
index: 10,
inputs_size: 1
@ -839,7 +858,7 @@ mod tests {
);
let prevout = Prevouts::One(0, &tx_out);
assert_eq!(
c.taproot_signature_hash(0, &prevout, None, None, SigHashType::SinglePlusAnyoneCanPay),
c.taproot_signature_hash(0, &prevout, None, None, SchnorrSigHashType::SinglePlusAnyoneCanPay),
Err(Error::SingleWithoutCorrespondingOutput {
index: 0,
outputs_size: 0
@ -866,7 +885,7 @@ mod tests {
prevout_hex: &str,
input_index: usize,
expected_hash: &str,
sighash_type: SigHashType,
sighash_type: SchnorrSigHashType,
annex_hex: Option<&str>,
script_hex: Option<&str>,
) {