Merge rust-bitcoin/rust-bitcoin#1077: Move sighash types to the sighash module
cc2c67fc2d
Move sighash types to the sighash module (Tobin C. Harding)7c040f4437
Refactor imports (Tobin C. Harding)29f21d6ff5
Add additional sighash related pubic exports (Tobin C. Harding) Pull request description: This is non-urgent work, something I've wanted to do for months now, please only review at your leisure :) 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 an artifact of 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. The first two patches are preparatory cleanup. ### Labels I added "API break" because I move the types without adding a re-export from the original place. I believe this is correct because we are, hopefully, aiming to de-couple the public API from the internal module structure so there is going to be a load more of this. ACKs for top commit: apoelstra: ACKcc2c67fc2d
sanket1729: ACKcc2c67fc2d
Tree-SHA512: 62a586b63c388baf91655a9afc8595394565d6ea6eb9aa3bd4746f899c140f332999e886cd85b098ec01304d2b96a310848d42d5ae38af476d3a26496576e36c
This commit is contained in:
commit
deb867e33d
|
@ -29,7 +29,7 @@ use crate::blockdata::locktime::{LockTime, PackedLockTime, Height, Time};
|
|||
use crate::consensus::{encode, Decodable, Encodable};
|
||||
use crate::hash_types::{Sighash, Txid, Wtxid};
|
||||
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;
|
||||
|
||||
#[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
|
||||
#[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 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 }
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
#[deprecated(since = "0.30.0", note = "use crate::NonStandardSighashType instead")]
|
||||
pub use crate::util::sighash::NonStandardSighashType;
|
||||
#[deprecated(since = "0.30.0", note = "use crate::EcdsaSighashType instead")]
|
||||
pub use crate::util::sighash::EcdsaSighashType;
|
||||
#[deprecated(since = "0.30.0", note = "use crate::SighashTypeParseError instead")]
|
||||
pub use crate::util::sighash::SighashTypeParseError;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -1207,7 +1040,7 @@ mod tests {
|
|||
use crate::hashes::hex::FromHex;
|
||||
|
||||
use crate::hash_types::*;
|
||||
use super::EcdsaSighashType;
|
||||
use crate::util::sighash::NonStandardSighashType;
|
||||
|
||||
const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
use secp256k1::ecdsa;
|
||||
|
||||
use crate::blockdata::transaction::EcdsaSighashType;
|
||||
use crate::consensus::encode::{Error, MAX_VEC_SIZE};
|
||||
use crate::consensus::{Decodable, Encodable, WriteExt};
|
||||
use crate::util::sighash::EcdsaSighashType;
|
||||
use crate::io::{self, Read, Write};
|
||||
use crate::prelude::*;
|
||||
use crate::VarInt;
|
||||
|
|
|
@ -99,9 +99,7 @@ pub use crate::blockdata::locktime::{self, LockTime, PackedLockTime};
|
|||
pub use crate::blockdata::script::Script;
|
||||
#[allow(deprecated)]
|
||||
pub use crate::blockdata::transaction::SigHashType;
|
||||
pub use crate::blockdata::transaction::{
|
||||
EcdsaSighashType, OutPoint, Sequence, Transaction, TxIn, TxOut,
|
||||
};
|
||||
pub use crate::blockdata::transaction::{OutPoint, Sequence, Transaction, TxIn, TxOut};
|
||||
pub use crate::blockdata::witness::Witness;
|
||||
pub use crate::consensus::encode::VarInt;
|
||||
pub use crate::hash_types::*;
|
||||
|
@ -111,7 +109,10 @@ pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
|
|||
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};
|
||||
pub use crate::util::merkleblock::MerkleBlock;
|
||||
pub use crate::util::schnorr::{self, SchnorrSig, SchnorrSigError};
|
||||
pub use crate::util::sighash::SchnorrSighashType;
|
||||
pub use crate::util::sighash::{
|
||||
EcdsaSighashType, NonStandardSighashType, SchnorrSighashType, SighashCache,
|
||||
SighashTypeParseError,
|
||||
};
|
||||
pub use crate::util::{psbt, Error};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
|
|
|
@ -9,16 +9,17 @@
|
|||
//! signatures, which are placed in the scriptSig.
|
||||
//!
|
||||
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::io;
|
||||
|
||||
use crate::hashes::Hash;
|
||||
use crate::hash_types::Sighash;
|
||||
use crate::blockdata::script::Script;
|
||||
use crate::blockdata::transaction::Transaction;
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::blockdata::transaction::{Transaction, EcdsaSighashType};
|
||||
use crate::consensus::encode;
|
||||
|
||||
use crate::io;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use crate::util::sighash;
|
||||
use crate::util::sighash::{self, EcdsaSighashType};
|
||||
|
||||
/// A replacement for SigHashComponents which supports all sighash modes
|
||||
#[deprecated(since = "0.28.0", note = "please use [sighash::SighashCache] instead")]
|
||||
|
@ -77,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
|
||||
///
|
||||
/// ```
|
||||
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
|
||||
/// 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 input_count = tx_to_sign.input.len();
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
//!
|
||||
//! This module provides ECDSA signatures used Bitcoin that can be roundtrip (de)serialized.
|
||||
|
||||
use crate::prelude::*;
|
||||
use core::str::FromStr;
|
||||
use core::{fmt, iter};
|
||||
use crate::hashes::hex::{self, FromHex};
|
||||
use crate::blockdata::transaction::NonStandardSighashType;
|
||||
|
||||
use secp256k1;
|
||||
use crate::EcdsaSighashType;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::hashes::hex::{self, FromHex};
|
||||
use crate::internal_macros::write_err;
|
||||
use crate::util::sighash::{EcdsaSighashType, NonStandardSighashType};
|
||||
|
||||
/// An ECDSA signature with the corresponding hash type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
|
||||
use crate::prelude::*;
|
||||
use crate::io;
|
||||
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use secp256k1;
|
||||
use secp256k1::XOnlyPublicKey;
|
||||
|
||||
use crate::blockdata::script::Script;
|
||||
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::hashes::{self, hash160, ripemd160, sha256, sha256d};
|
||||
use secp256k1::XOnlyPublicKey;
|
||||
use crate::util::bip32::KeySource;
|
||||
use crate::util::psbt;
|
||||
use crate::util::psbt::map::Map;
|
||||
|
@ -20,10 +21,10 @@ use crate::util::psbt::raw;
|
|||
use crate::util::psbt::serialize::Deserialize;
|
||||
use crate::util::psbt::{Error, error};
|
||||
use crate::util::key::PublicKey;
|
||||
|
||||
use crate::util::sighash::{NonStandardSighashType, SighashTypeParseError, EcdsaSighashType, SchnorrSighashType};
|
||||
use crate::util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash};
|
||||
use crate::util::sighash;
|
||||
use crate::{EcdsaSighashType, SchnorrSighashType, EcdsaSig, SchnorrSig};
|
||||
use crate::{EcdsaSig, SchnorrSig};
|
||||
|
||||
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
|
||||
const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00;
|
||||
|
|
|
@ -598,12 +598,13 @@ mod tests {
|
|||
use crate::hash_types::Txid;
|
||||
|
||||
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::blockdata::locktime::PackedLockTime;
|
||||
use crate::util::psbt::map::{Map, Input, Output};
|
||||
use crate::util::psbt::raw;
|
||||
use crate::util::psbt::{PartiallySignedTransaction, Error};
|
||||
use crate::util::sighash::EcdsaSighashType;
|
||||
use std::collections::BTreeMap;
|
||||
use crate::blockdata::witness::Witness;
|
||||
use crate::internal_macros::hex_script;
|
||||
|
|
|
@ -22,8 +22,6 @@ use crate::internal_macros::serde_string_impl;
|
|||
use crate::prelude::*;
|
||||
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.
|
||||
pub(crate) const UINT256_ONE: [u8; 32] = [
|
||||
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 {
|
||||
fn from(s: EcdsaSighashType) -> Self {
|
||||
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> {
|
||||
/// 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
|
||||
/// ```
|
||||
/// use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
|
||||
/// use bitcoin::util::sighash::SighashCache;
|
||||
/// use bitcoin::blockdata::transaction::Transaction;
|
||||
/// use bitcoin::util::sighash::{EcdsaSighashType, SighashCache};
|
||||
/// use bitcoin::{PackedLockTime, Script};
|
||||
///
|
||||
/// let mut tx_to_sign = Transaction { version: 2, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: Vec::new() };
|
||||
|
|
Loading…
Reference in New Issue