transaction: add a method to err on non-standard types to SigHashType
Right now, any sighash type could be parsed without error, which matches consensus rules. However most of them would be invalid by standardness, so it's a bit footgun-y (even more so for pre-signed transactions protocols for which standardness is critical). This adds `from_u32_standard()`, which takes care to error if we are passed an invalid-by-current-policy-rules SIGHASH type. Signed-off-by: Antoine Poinsot <darosior@protonmail.com>
This commit is contained in:
parent
466f161e0b
commit
bf98d9fd60
|
@ -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<SigHashType, 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),
|
||||
_ => Err(NonStandardSigHashType)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to a u32
|
||||
pub fn as_u32(self) -> u32 { self as u32 }
|
||||
}
|
||||
|
@ -706,7 +737,7 @@ impl From<SigHashType> 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
|
||||
|
|
Loading…
Reference in New Issue