use alloc::{borrow::ToOwned, string::String, vec::Vec};
#[cfg(feature = "signature-validation")]
use secp256k1::ecdsa;
use thiserror::Error;
use crate::script::Asm;
#[cfg(feature = "signature-validation")]
use crate::external::pubkey::PubKey;
#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)]
pub enum InvalidHashType {
#[error("invalid signed outputs")]
InvalidSignedOutputs,
#[error("extra bits set")]
ExtraBitsSet(i32),
}
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)]
pub enum InvalidDerInteger {
#[error("missing the 0x02 integer encoding byte")]
NotAnInteger,
#[error("the integer was expected to be {expected} bytes, but it was {actual} bytes")]
IncorrectLength { actual: usize, expected: u8 },
#[error("integers can’t be zero-length")]
ZeroLength,
#[error("leading 0x00 bytes are disallowed, unless it would otherwise be interpreted as a negative number.")]
LeadingNullByte,
#[error("integers can’t be negative")]
Negative,
}
#[allow(missing_docs)]
#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum InvalidDerEncoding {
#[error("didn’t start with 0x30, or was missing the length")]
WrongType,
#[error("the signature can’t be longer than 70 bytes")]
TooLong,
#[error("the signature was expected to be {expected} bytes, but it was {actual} bytes")]
IncorrectLength { actual: usize, expected: u8 },
#[error("the {name} component ({value:?}) failed: {error}")]
InvalidComponent {
name: &'static str,
value: Vec<u8>,
error: InvalidDerInteger,
},
}
#[allow(missing_docs)]
#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum Error {
#[error(
"{}",
.0.map_or(
"unknown signature hash type error".to_owned(),
|iht| format!("signature hash type error: {iht}")
)
)]
SigHashType(Option<InvalidHashType>),
#[error(
"{}",
.0.clone().map_or(
"unknown signature DER encoding error".to_owned(),
|ide| format!("signature DER encoding error: {ide}")
)
)]
SigDER(Option<InvalidDerEncoding>),
#[error("signature s value is too high")]
SigHighS,
}
impl From<InvalidHashType> for Error {
fn from(value: InvalidHashType) -> Self {
Self::SigHashType(Some(value))
}
}
impl From<InvalidDerEncoding> for Error {
fn from(value: InvalidDerEncoding) -> Self {
Self::SigDER(Some(value))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SignedOutputs {
All,
Single,
None,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HashType {
signed_outputs: SignedOutputs,
anyone_can_pay: bool,
raw_bits: i32,
}
impl HashType {
pub fn from_bits(bits: i32, is_strict: bool) -> Result<Self, InvalidHashType> {
let unknown_bits = (bits | 0x83) ^ 0x83;
if is_strict && unknown_bits != 0 {
Err(InvalidHashType::ExtraBitsSet(unknown_bits))
} else if is_strict && bits & 0x03 == 0 {
Err(InvalidHashType::InvalidSignedOutputs)
} else {
Ok(HashType {
signed_outputs: match bits & 0x1f {
2 => SignedOutputs::None,
3 => SignedOutputs::Single,
_ => SignedOutputs::All,
},
anyone_can_pay: bits & 0x80 != 0,
raw_bits: bits,
})
}
}
pub fn signed_outputs(&self) -> SignedOutputs {
self.signed_outputs
}
pub fn anyone_can_pay(&self) -> bool {
self.anyone_can_pay
}
pub fn raw_bits(&self) -> i32 {
self.raw_bits
}
}
impl Asm for HashType {
fn to_asm(&self, _attempt_sighash_decode: bool) -> String {
let signed_outputs = match self.signed_outputs {
SignedOutputs::All => "ALL",
SignedOutputs::Single => "SINGLE",
SignedOutputs::None => "NONE",
};
let anyone_can_pay = if self.anyone_can_pay {
"|ANYONECANPAY"
} else {
""
};
format!("{}{}", signed_outputs, anyone_can_pay)
}
}
impl core::fmt::Display for HashType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.to_asm(false))
}
}
pub enum Validity {
InvalidAbort(Error),
InvalidContinue,
Valid(Decoded),
}
#[cfg(feature = "signature-validation")]
#[derive(Clone)]
pub struct Decoded {
sig: ecdsa::Signature,
hash_type: HashType,
}
#[cfg(not(feature = "signature-validation"))]
#[derive(Clone)]
pub struct Decoded {}
#[cfg(feature = "signature-validation")]
impl Decoded {
fn is_valid_integer(int_bytes: &[u8]) -> Result<(), InvalidDerInteger> {
match int_bytes {
[] => Err(InvalidDerInteger::ZeroLength),
[0x00, next, ..] => {
if next & 0x80 != 0 {
Ok(())
} else {
Err(InvalidDerInteger::LeadingNullByte)
}
}
[first, ..] => {
if first & 0x80 == 0 {
Ok(())
} else {
Err(InvalidDerInteger::Negative)
}
}
}
}
fn is_valid_encoding(sig: &[u8]) -> Result<(), InvalidDerEncoding> {
match sig {
[0x30, total_len, content @ ..] => {
if *total_len <= 70 {
if usize::from(*total_len) == content.len() {
match content {
[0x02, r_len, r_s @ ..] => {
match r_s.split_at_checked((*r_len).into()) {
None => Err(InvalidDerEncoding::InvalidComponent {
name: "r",
value: r_s.to_vec(),
error: InvalidDerInteger::IncorrectLength {
actual: r_s.len(),
expected: *r_len,
},
}),
Some((r, s)) => Self::is_valid_integer(r)
.map_err(|error| InvalidDerEncoding::InvalidComponent {
name: "r",
value: r.to_vec(),
error,
})
.and_then(|()| match s {
[0x02, s_len, s @ ..] => {
if usize::from(*s_len) == s.len() {
Self::is_valid_integer(s).map_err(|error| {
InvalidDerEncoding::InvalidComponent {
name: "s",
value: s.to_vec(),
error,
}
})
} else {
Err(InvalidDerEncoding::InvalidComponent {
name: "s",
value: s.to_vec(),
error: InvalidDerInteger::IncorrectLength {
actual: s.len(),
expected: *s_len,
},
})
}
}
[..] => Err(InvalidDerEncoding::InvalidComponent {
name: "s",
value: s.to_vec(),
error: InvalidDerInteger::NotAnInteger,
}),
}),
}
}
r_s => Err(InvalidDerEncoding::InvalidComponent {
name: "r",
value: r_s.to_vec(),
error: InvalidDerInteger::NotAnInteger,
}),
}
} else {
Err(InvalidDerEncoding::IncorrectLength {
actual: content.len(),
expected: *total_len,
})
}
} else {
Err(InvalidDerEncoding::TooLong)
}
}
[..] => Err(InvalidDerEncoding::WrongType),
}
}
pub fn from_bytes(vch_sig_in: &[u8], require_low_s: bool, is_strict: bool) -> Validity {
match vch_sig_in.split_last() {
None => Validity::InvalidContinue,
Some((hash_type, vch_sig)) => {
let validated = Self::is_valid_encoding(vch_sig)
.map_err(|e| Error::SigDER(Some(e)))
.and_then(|()| {
HashType::from_bits((*hash_type).into(), is_strict)
.map_err(|e| Error::SigHashType(Some(e)))
});
match validated {
Err(e) => Validity::InvalidAbort(e),
Ok(hash_type) => match ecdsa::Signature::from_der(vch_sig) {
Err(_) => Validity::InvalidContinue,
Ok(sig) => {
if require_low_s && !PubKey::check_low_s(&sig) {
Validity::InvalidAbort(Error::SigHighS)
} else {
Validity::Valid(Decoded { sig, hash_type })
}
}
},
}
}
}
}
pub fn sig(&self) -> &ecdsa::Signature {
&self.sig
}
pub fn sighash_type(&self) -> &HashType {
&self.hash_type
}
}