Move sighash types to the sighash module

Currently we have a bunch of sighash types defined in the `transaction`
module and we have a newer `sighash` module that defines a bunch of
related types. This is kruft from the development process as we added
the new types. If we put things that are related together it makes it
easier for devs to find their way around the library and grok the code.

Move all the sighash types from the `blockdata::transaction` module to
the `sighash` module.

Refactor only, no logic changes.
This commit is contained in:
Tobin C. Harding 2022-07-19 11:41:35 +10:00
parent 7c040f4437
commit cc2c67fc2d
8 changed files with 199 additions and 194 deletions

View File

@ -29,7 +29,7 @@ use crate::blockdata::locktime::{LockTime, PackedLockTime, Height, Time};
use crate::consensus::{encode, Decodable, Encodable}; use crate::consensus::{encode, Decodable, Encodable};
use crate::hash_types::{Sighash, Txid, Wtxid}; use crate::hash_types::{Sighash, Txid, Wtxid};
use crate::VarInt; use crate::VarInt;
use crate::internal_macros::{impl_consensus_encoding, serde_string_impl, serde_struct_human_string_impl, write_err}; use crate::internal_macros::{impl_consensus_encoding, serde_struct_human_string_impl, write_err};
use crate::parse::impl_parse_str_through_int; use crate::parse::impl_parse_str_through_int;
#[cfg(doc)] #[cfg(doc)]
@ -1014,183 +1014,16 @@ impl Decodable for Transaction {
} }
} }
/// This type is consensus valid but an input including it would prevent the transaction from
/// being relayed on today's Bitcoin network.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NonStandardSighashType(pub u32);
impl fmt::Display for NonStandardSighashType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Non standard sighash type {}", self.0)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for NonStandardSighashType {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
/// Legacy Hashtype of an input's signature /// Legacy Hashtype of an input's signature
#[deprecated(since = "0.28.0", note = "Please use [`EcdsaSighashType`] instead")] #[deprecated(since = "0.28.0", note = "Please use [`EcdsaSighashType`] instead")]
pub type SigHashType = EcdsaSighashType; pub type SigHashType = EcdsaSighashType;
/// Hashtype of an input's signature, encoded in the last byte of the signature. #[deprecated(since = "0.30.0", note = "use crate::NonStandardSighashType instead")]
/// pub use crate::util::sighash::NonStandardSighashType;
/// Fixed values so they can be cast as integer types for encoding (see also #[deprecated(since = "0.30.0", note = "use crate::EcdsaSighashType instead")]
/// [`SchnorrSighashType`]). pub use crate::util::sighash::EcdsaSighashType;
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)] #[deprecated(since = "0.30.0", note = "use crate::SighashTypeParseError instead")]
pub enum EcdsaSighashType { pub use crate::util::sighash::SighashTypeParseError;
/// 0x1: Sign all outputs.
All = 0x01,
/// 0x2: Sign no outputs --- anyone can choose the destination.
None = 0x02,
/// 0x3: Sign the output whose index matches this input's index. If none exists,
/// sign the hash `0000000000000000000000000000000000000000000000000000000000000001`.
/// (This rule is probably an unintentional C++ism, but it's consensus so we have
/// to follow it.)
Single = 0x03,
/// 0x81: Sign all outputs but only this input.
AllPlusAnyoneCanPay = 0x81,
/// 0x82: Sign no outputs and only this input.
NonePlusAnyoneCanPay = 0x82,
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means).
SinglePlusAnyoneCanPay = 0x83
}
serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data");
impl fmt::Display for EcdsaSighashType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
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 EcdsaSighashType {
type Err = SighashTypeParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"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(SighashTypeParseError { unrecognized: s.to_owned() }),
}
}
}
impl EcdsaSighashType {
/// Splits the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean.
pub(crate) fn split_anyonecanpay_flag(self) -> (EcdsaSighashType, bool) {
match self {
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)
}
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
#[deprecated(since="0.28.0", note="please use `from_consensus`")]
pub fn from_u32_consensus(n: u32) -> EcdsaSighashType {
EcdsaSighashType::from_consensus(n)
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
///
/// **Note**: this replicates consensus behaviour, for current standardness rules correctness
/// you probably want [`Self::from_standard`].
///
/// This might cause unexpected behavior because it does not roundtrip. That is,
/// `EcdsaSighashType::from_consensus(n) as u32 != n` for non-standard values of `n`. While
/// verifying signatures, the user should retain the `n` and use it compute the signature hash
/// message.
pub fn from_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.
// So here we re-activate ACP.
let mask = 0x1f | 0x80;
match n & mask {
// "real" sighashes
0x01 => EcdsaSighashType::All,
0x02 => EcdsaSighashType::None,
0x03 => EcdsaSighashType::Single,
0x81 => EcdsaSighashType::AllPlusAnyoneCanPay,
0x82 => EcdsaSighashType::NonePlusAnyoneCanPay,
0x83 => EcdsaSighashType::SinglePlusAnyoneCanPay,
// catchalls
x if x & 0x80 == 0x80 => EcdsaSighashType::AllPlusAnyoneCanPay,
_ => EcdsaSighashType::All
}
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
#[deprecated(since="0.28.0", note="please use `from_standard`")]
pub fn from_u32_standard(n: u32) -> Result<EcdsaSighashType, NonStandardSighashType> {
EcdsaSighashType::from_standard(n)
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
///
/// # Errors
///
/// If `n` is a non-standard sighash value.
pub fn from_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(EcdsaSighashType::All),
0x02 => Ok(EcdsaSighashType::None),
0x03 => Ok(EcdsaSighashType::Single),
0x81 => Ok(EcdsaSighashType::AllPlusAnyoneCanPay),
0x82 => Ok(EcdsaSighashType::NonePlusAnyoneCanPay),
0x83 => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay),
non_standard => Err(NonStandardSighashType(non_standard))
}
}
/// Converts [`EcdsaSighashType`] to a `u32` sighash flag.
///
/// The returned value is guaranteed to be a valid according to standardness rules.
pub fn to_u32(self) -> u32 { self as u32 }
}
/// Error returned for failure during parsing one of the sighash types.
///
/// This is currently returned for unrecognized sighash strings.
#[derive(Debug, Clone)]
pub struct SighashTypeParseError {
/// The unrecognized string we attempted to parse.
pub unrecognized: String,
}
impl fmt::Display for SighashTypeParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Unrecognized SIGHASH string '{}'", self.unrecognized)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for SighashTypeParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -1207,7 +1040,7 @@ mod tests {
use crate::hashes::hex::FromHex; use crate::hashes::hex::FromHex;
use crate::hash_types::*; use crate::hash_types::*;
use super::EcdsaSighashType; use crate::util::sighash::NonStandardSighashType;
const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000"; const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";

View File

@ -7,9 +7,9 @@
use secp256k1::ecdsa; use secp256k1::ecdsa;
use crate::blockdata::transaction::EcdsaSighashType;
use crate::consensus::encode::{Error, MAX_VEC_SIZE}; use crate::consensus::encode::{Error, MAX_VEC_SIZE};
use crate::consensus::{Decodable, Encodable, WriteExt}; use crate::consensus::{Decodable, Encodable, WriteExt};
use crate::util::sighash::EcdsaSighashType;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::prelude::*; use crate::prelude::*;
use crate::VarInt; use crate::VarInt;

View File

@ -100,10 +100,7 @@ pub use crate::blockdata::locktime::{self, LockTime, PackedLockTime};
pub use crate::blockdata::script::Script; pub use crate::blockdata::script::Script;
#[allow(deprecated)] #[allow(deprecated)]
pub use crate::blockdata::transaction::SigHashType; pub use crate::blockdata::transaction::SigHashType;
pub use crate::blockdata::transaction::{ pub use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
EcdsaSighashType, NonStandardSighashType, OutPoint, Sequence, SighashTypeParseError,
Transaction, TxIn, TxOut,
};
pub use crate::blockdata::witness::Witness; pub use crate::blockdata::witness::Witness;
pub use crate::consensus::encode::VarInt; pub use crate::consensus::encode::VarInt;
pub use crate::hash_types::*; pub use crate::hash_types::*;
@ -114,7 +111,10 @@ pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey}; pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};
pub use crate::util::merkleblock::MerkleBlock; pub use crate::util::merkleblock::MerkleBlock;
pub use crate::util::schnorr::{self, SchnorrSig, SchnorrSigError}; pub use crate::util::schnorr::{self, SchnorrSig, SchnorrSigError};
pub use crate::util::sighash::{SchnorrSighashType, SighashCache}; pub use crate::util::sighash::{
EcdsaSighashType, NonStandardSighashType, SchnorrSighashType, SighashCache,
SighashTypeParseError,
};
pub use crate::util::{psbt, Error}; pub use crate::util::{psbt, Error};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]

View File

@ -16,10 +16,10 @@ use crate::io;
use crate::hashes::Hash; use crate::hashes::Hash;
use crate::hash_types::Sighash; use crate::hash_types::Sighash;
use crate::blockdata::script::Script; use crate::blockdata::script::Script;
use crate::blockdata::transaction::{Transaction, EcdsaSighashType}; use crate::blockdata::transaction::Transaction;
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::consensus::encode; use crate::consensus::encode;
use crate::util::sighash; use crate::util::sighash::{self, EcdsaSighashType};
/// A replacement for SigHashComponents which supports all sighash modes /// A replacement for SigHashComponents which supports all sighash modes
#[deprecated(since = "0.28.0", note = "please use [sighash::SighashCache] instead")] #[deprecated(since = "0.28.0", note = "please use [sighash::SighashCache] instead")]
@ -78,9 +78,8 @@ impl<R: DerefMut<Target = Transaction>> SigHashCache<R> {
/// panics if `input_index` is out of bounds with respect of the number of inputs /// panics if `input_index` is out of bounds with respect of the number of inputs
/// ///
/// ``` /// ```
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
/// use bitcoin::util::bip143::SigHashCache; /// use bitcoin::util::bip143::SigHashCache;
/// use bitcoin::{PackedLockTime, Script}; /// use bitcoin::{EcdsaSighashType, Script, Transaction, PackedLockTime};
/// ///
/// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() }; /// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
/// let input_count = tx_to_sign.input.len(); /// let input_count = tx_to_sign.input.len();

View File

@ -12,9 +12,8 @@ use secp256k1;
use crate::prelude::*; use crate::prelude::*;
use crate::hashes::hex::{self, FromHex}; use crate::hashes::hex::{self, FromHex};
use crate::blockdata::transaction::NonStandardSighashType;
use crate::EcdsaSighashType;
use crate::internal_macros::write_err; use crate::internal_macros::write_err;
use crate::util::sighash::{EcdsaSighashType, NonStandardSighashType};
/// An ECDSA signature with the corresponding hash type. /// An ECDSA signature with the corresponding hash type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]

View File

@ -11,7 +11,7 @@ use secp256k1::XOnlyPublicKey;
use crate::blockdata::script::Script; use crate::blockdata::script::Script;
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::blockdata::transaction::{Transaction, TxOut, NonStandardSighashType, SighashTypeParseError}; use crate::blockdata::transaction::{Transaction, TxOut};
use crate::consensus::encode; use crate::consensus::encode;
use crate::hashes::{self, hash160, ripemd160, sha256, sha256d}; use crate::hashes::{self, hash160, ripemd160, sha256, sha256d};
use crate::util::bip32::KeySource; use crate::util::bip32::KeySource;
@ -21,9 +21,10 @@ use crate::util::psbt::raw;
use crate::util::psbt::serialize::Deserialize; use crate::util::psbt::serialize::Deserialize;
use crate::util::psbt::{Error, error}; use crate::util::psbt::{Error, error};
use crate::util::key::PublicKey; use crate::util::key::PublicKey;
use crate::util::sighash::{NonStandardSighashType, SighashTypeParseError, EcdsaSighashType, SchnorrSighashType};
use crate::util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash}; use crate::util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash};
use crate::util::sighash; use crate::util::sighash;
use crate::{EcdsaSighashType, SchnorrSighashType, EcdsaSig, SchnorrSig}; use crate::{EcdsaSig, SchnorrSig};
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00 /// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00; const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00;

View File

@ -598,12 +598,13 @@ mod tests {
use crate::hash_types::Txid; use crate::hash_types::Txid;
use crate::blockdata::script::Script; use crate::blockdata::script::Script;
use crate::blockdata::transaction::{EcdsaSighashType, Transaction, TxIn, TxOut, OutPoint, Sequence}; use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
use crate::consensus::encode::serialize_hex; use crate::consensus::encode::serialize_hex;
use crate::blockdata::locktime::PackedLockTime; use crate::blockdata::locktime::PackedLockTime;
use crate::util::psbt::map::{Map, Input, Output}; use crate::util::psbt::map::{Map, Input, Output};
use crate::util::psbt::raw; use crate::util::psbt::raw;
use crate::util::psbt::{PartiallySignedTransaction, Error}; use crate::util::psbt::{PartiallySignedTransaction, Error};
use crate::util::sighash::EcdsaSighashType;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::blockdata::witness::Witness; use crate::blockdata::witness::Witness;
use crate::internal_macros::hex_script; use crate::internal_macros::hex_script;

View File

@ -22,8 +22,6 @@ use crate::internal_macros::serde_string_impl;
use crate::prelude::*; use crate::prelude::*;
use crate::util::taproot::{TapLeafHash, TAPROOT_ANNEX_PREFIX, TapSighashHash, LeafVersion}; use crate::util::taproot::{TapLeafHash, TAPROOT_ANNEX_PREFIX, TapSighashHash, LeafVersion};
pub use crate::blockdata::transaction::{EcdsaSighashType, SighashTypeParseError};
/// Used for signature hash for invalid use of SIGHASH_SINGLE. /// Used for signature hash for invalid use of SIGHASH_SINGLE.
pub(crate) const UINT256_ONE: [u8; 32] = [ pub(crate) const UINT256_ONE: [u8; 32] = [
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
@ -297,6 +295,138 @@ impl<'s> From<ScriptPath<'s>> for TapLeafHash {
} }
} }
/// 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
/// [`SchnorrSighashType`]).
#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
pub enum EcdsaSighashType {
/// 0x1: Sign all outputs.
All = 0x01,
/// 0x2: Sign no outputs --- anyone can choose the destination.
None = 0x02,
/// 0x3: Sign the output whose index matches this input's index. If none exists,
/// sign the hash `0000000000000000000000000000000000000000000000000000000000000001`.
/// (This rule is probably an unintentional C++ism, but it's consensus so we have
/// to follow it.)
Single = 0x03,
/// 0x81: Sign all outputs but only this input.
AllPlusAnyoneCanPay = 0x81,
/// 0x82: Sign no outputs and only this input.
NonePlusAnyoneCanPay = 0x82,
/// 0x83: Sign one output and only this input (see `Single` for what "one output" means).
SinglePlusAnyoneCanPay = 0x83
}
serde_string_impl!(EcdsaSighashType, "a EcdsaSighashType data");
impl fmt::Display for EcdsaSighashType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
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 EcdsaSighashType {
type Err = SighashTypeParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"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(SighashTypeParseError { unrecognized: s.to_owned() }),
}
}
}
impl EcdsaSighashType {
/// Splits the sighash flag into the "real" sighash flag and the ANYONECANPAY boolean.
pub(crate) fn split_anyonecanpay_flag(self) -> (EcdsaSighashType, bool) {
match self {
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)
}
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
#[deprecated(since="0.28.0", note="please use `from_consensus`")]
pub fn from_u32_consensus(n: u32) -> EcdsaSighashType {
EcdsaSighashType::from_consensus(n)
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
///
/// **Note**: this replicates consensus behaviour, for current standardness rules correctness
/// you probably want [`Self::from_standard`].
///
/// This might cause unexpected behavior because it does not roundtrip. That is,
/// `EcdsaSighashType::from_consensus(n) as u32 != n` for non-standard values of `n`. While
/// verifying signatures, the user should retain the `n` and use it compute the signature hash
/// message.
pub fn from_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.
// So here we re-activate ACP.
let mask = 0x1f | 0x80;
match n & mask {
// "real" sighashes
0x01 => EcdsaSighashType::All,
0x02 => EcdsaSighashType::None,
0x03 => EcdsaSighashType::Single,
0x81 => EcdsaSighashType::AllPlusAnyoneCanPay,
0x82 => EcdsaSighashType::NonePlusAnyoneCanPay,
0x83 => EcdsaSighashType::SinglePlusAnyoneCanPay,
// catchalls
x if x & 0x80 == 0x80 => EcdsaSighashType::AllPlusAnyoneCanPay,
_ => EcdsaSighashType::All
}
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
#[deprecated(since="0.28.0", note="please use `from_standard`")]
pub fn from_u32_standard(n: u32) -> Result<EcdsaSighashType, NonStandardSighashType> {
EcdsaSighashType::from_standard(n)
}
/// Creates a [`EcdsaSighashType`] from a raw `u32`.
///
/// # Errors
///
/// If `n` is a non-standard sighash value.
pub fn from_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(EcdsaSighashType::All),
0x02 => Ok(EcdsaSighashType::None),
0x03 => Ok(EcdsaSighashType::Single),
0x81 => Ok(EcdsaSighashType::AllPlusAnyoneCanPay),
0x82 => Ok(EcdsaSighashType::NonePlusAnyoneCanPay),
0x83 => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay),
non_standard => Err(NonStandardSighashType(non_standard))
}
}
/// Converts [`EcdsaSighashType`] to a `u32` sighash flag.
///
/// The returned value is guaranteed to be a valid according to standardness rules.
pub fn to_u32(self) -> u32 { self as u32 }
}
impl From<EcdsaSighashType> for SchnorrSighashType { impl From<EcdsaSighashType> for SchnorrSighashType {
fn from(s: EcdsaSighashType) -> Self { fn from(s: EcdsaSighashType) -> Self {
match s { match s {
@ -347,6 +477,48 @@ impl SchnorrSighashType {
} }
} }
/// This type is consensus valid but an input including it would prevent the transaction from
/// being relayed on today's Bitcoin network.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NonStandardSighashType(pub u32);
impl fmt::Display for NonStandardSighashType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Non standard sighash type {}", self.0)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for NonStandardSighashType {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
/// Error returned for failure during parsing one of the sighash types.
///
/// This is currently returned for unrecognized sighash strings.
#[derive(Debug, Clone)]
pub struct SighashTypeParseError {
/// The unrecognized string we attempted to parse.
pub unrecognized: String,
}
impl fmt::Display for SighashTypeParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Unrecognized SIGHASH string '{}'", self.unrecognized)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl std::error::Error for SighashTypeParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
impl<R: Deref<Target = Transaction>> SighashCache<R> { impl<R: Deref<Target = Transaction>> SighashCache<R> {
/// Constructs a new `SighashCache` from an unsigned transaction. /// Constructs a new `SighashCache` from an unsigned transaction.
/// ///
@ -863,8 +1035,8 @@ impl<R: DerefMut<Target=Transaction>> SighashCache<R> {
/// ///
/// This allows in-line signing such as /// This allows in-line signing such as
/// ``` /// ```
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType}; /// use bitcoin::blockdata::transaction::Transaction;
/// use bitcoin::util::sighash::SighashCache; /// use bitcoin::util::sighash::{EcdsaSighashType, SighashCache};
/// use bitcoin::{PackedLockTime, Script}; /// use bitcoin::{PackedLockTime, Script};
/// ///
/// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() }; /// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };