diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 8448c991..2faa3d7c 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -608,6 +608,19 @@ 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; + +impl fmt::Display for NonStandardSigHashType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Non standard sighash type") + } +} + +impl error::Error for NonStandardSigHashType {} + /// 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(PartialEq, Eq, Debug, Copy, Clone)] @@ -673,7 +686,10 @@ impl SigHashType { } } - /// Reads a 4-byte uint32 as a sighash type + /// Reads a 4-byte uint32 as a sighash type. + /// + /// **Note**: this replicates consensus behaviour, for current standardness rules correctness + /// you probably want [from_u32_standard]. pub fn from_u32(n: u32) -> SigHashType { // In Bitcoin Core, the SignatureHash function will mask the (int32) value with // 0x1f to (apparently) deactivate ACP when checking for SINGLE and NONE bits. @@ -694,6 +710,21 @@ impl SigHashType { } } + /// 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 { + 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), + _ => Err(NonStandardSigHashType) + } + } + /// Converts to a u32 pub fn as_u32(self) -> u32 { self as u32 } } @@ -706,7 +737,7 @@ impl From for u32 { #[cfg(test)] mod tests { - use super::{OutPoint, ParseOutPointError, Transaction, TxIn}; + use super::{OutPoint, ParseOutPointError, Transaction, TxIn, NonStandardSigHashType}; use std::str::FromStr; use blockdata::constants::WITNESS_SCALE_FACTOR; @@ -999,6 +1030,15 @@ mod tests { } } + #[test] + 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); + // But it's policy-invalid to use it! + assert_eq!(SigHashType::from_u32_standard(nonstandard_hashtype), Err(NonStandardSigHashType)); + } + // 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 // OP_CODESEPARATOR in their pubkeys were removed