use std::fmt::{self, Display, Formatter};
use std::iter;
use amplify::{ByteArray, Bytes32, Wrapper};
use commit_verify::{DigestExt, Sha256};
use secp256k1::{ecdsa, schnorr};
use crate::{NonStandardValue, ScriptBytes, ScriptPubkey, WitnessScript, LIB_NAME_BITCOIN};
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Display, Default)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
#[display(uppercase)]
#[repr(u8)]
pub enum SighashFlag {
#[default]
All = 0x01,
None = 0x02,
Single = 0x03,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Default)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct SighashType {
pub flag: SighashFlag,
pub anyone_can_pay: bool,
}
impl SighashType {
pub const fn all() -> Self {
SighashType {
flag: SighashFlag::All,
anyone_can_pay: false,
}
}
pub const fn none() -> Self {
SighashType {
flag: SighashFlag::None,
anyone_can_pay: false,
}
}
pub const fn single() -> Self {
SighashType {
flag: SighashFlag::Single,
anyone_can_pay: false,
}
}
pub const fn all_anyone_can_pay() -> Self {
SighashType {
flag: SighashFlag::All,
anyone_can_pay: true,
}
}
pub const fn none_anyone_can_pay() -> Self {
SighashType {
flag: SighashFlag::None,
anyone_can_pay: true,
}
}
pub const fn single_anyone_can_pay() -> Self {
SighashType {
flag: SighashFlag::Single,
anyone_can_pay: true,
}
}
pub fn from_consensus_u32(n: u32) -> SighashType {
let mask = 0x1f | 0x80;
let (flag, anyone_can_pay) = match n & mask {
0x01 => (SighashFlag::All, false),
0x02 => (SighashFlag::None, false),
0x03 => (SighashFlag::Single, false),
0x81 => (SighashFlag::All, true),
0x82 => (SighashFlag::None, true),
0x83 => (SighashFlag::Single, true),
x if x & 0x80 == 0x80 => (SighashFlag::All, true),
_ => (SighashFlag::All, false),
};
SighashType {
flag,
anyone_can_pay,
}
}
pub fn from_standard_u32(n: u32) -> Result<SighashType, NonStandardValue<u32>> {
let (flag, anyone_can_pay) = match n {
0x01 => (SighashFlag::All, false),
0x02 => (SighashFlag::None, false),
0x03 => (SighashFlag::Single, false),
0x81 => (SighashFlag::All, true),
0x82 => (SighashFlag::None, true),
0x83 => (SighashFlag::Single, true),
non_standard => return Err(NonStandardValue::with(non_standard, "SighashType")),
};
Ok(SighashType {
flag,
anyone_can_pay,
})
}
#[inline]
pub const fn into_consensus_u32(self) -> u32 { self.into_consensus_u8() as u32 }
#[inline]
pub const fn to_consensus_u32(&self) -> u32 { self.into_consensus_u32() }
pub const fn into_consensus_u8(self) -> u8 {
let flag = self.flag as u8;
let mask = (self.anyone_can_pay as u8) << 7;
flag | mask
}
pub const fn to_consensus_u8(self) -> u8 {
let flag = self.flag as u8;
let mask = (self.anyone_can_pay as u8) << 7;
flag | mask
}
}
impl Display for SighashType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.flag, f)?;
if self.anyone_can_pay {
f.write_str(" | ANYONECANPAY")?;
}
Ok(())
}
}
#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Index, RangeOps, AsSlice, BorrowSlice, Hex, Display, FromStr)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct Sighash(
#[from]
#[from([u8; 32])]
pub Bytes32,
);
impl From<Sighash> for [u8; 32] {
fn from(value: Sighash) -> Self { value.0.into_inner() }
}
impl From<Sighash> for secp256k1::Message {
fn from(sighash: Sighash) -> Self { secp256k1::Message::from_digest(sighash.to_byte_array()) }
}
impl Sighash {
pub fn engine() -> Sha256 { Sha256::default() }
pub fn from_engine(engine: Sha256) -> Self {
let mut engine2 = Sha256::default();
engine2.input_raw(&engine.finish());
Self(engine2.finish().into())
}
}
#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, AsSlice, Hex)]
#[wrapper_mut(DerefMut, AsSliceMut)]
pub struct ScriptCode(ScriptBytes);
impl ScriptCode {
pub fn with_p2sh_wpkh(script_pubkey: &ScriptPubkey) -> Self { Self::with_p2wpkh(script_pubkey) }
pub fn with_p2wpkh(script_pubkey: &ScriptPubkey) -> Self {
let mut pubkey_hash = [0u8; 20];
pubkey_hash.copy_from_slice(&script_pubkey[2..22]);
let script_code = ScriptPubkey::p2pkh(pubkey_hash);
ScriptCode(script_code.into_inner())
}
pub fn with_p2sh_wsh(witness_script: &WitnessScript) -> Self {
Self::with_p2wsh(witness_script)
}
pub fn with_p2wsh(witness_script: &WitnessScript) -> Self {
ScriptCode(witness_script.to_inner())
}
#[inline]
pub fn as_script_bytes(&self) -> &ScriptBytes { &self.0 }
}
#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum SigError {
#[display(inner)]
#[from]
SighashType(NonStandardValue<u32>),
EmptySignature,
DerEncoding,
Bip340Encoding(usize),
InvalidSignature,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[derive(StrictType)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct LegacySig {
pub sig: ecdsa::Signature,
pub sighash_type: SighashType,
}
impl LegacySig {
pub fn sighash_all(sig: ecdsa::Signature) -> LegacySig {
LegacySig {
sig,
sighash_type: SighashType::all(),
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, SigError> {
let (hash_ty, sig) = bytes.split_last().ok_or(SigError::EmptySignature)?;
let sighash_type = SighashType::from_standard_u32(*hash_ty as u32)?;
let sig = ecdsa::Signature::from_der(sig).map_err(|_| SigError::DerEncoding)?;
Ok(LegacySig { sig, sighash_type })
}
pub fn to_vec(self) -> Vec<u8> {
self.sig
.serialize_der()
.iter()
.copied()
.chain(iter::once(self.sighash_type.into_consensus_u8()))
.collect()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[derive(StrictType)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct Bip340Sig {
pub sig: schnorr::Signature,
pub sighash_type: Option<SighashType>,
}
impl Bip340Sig {
pub fn sighash_default(sig: schnorr::Signature) -> Self {
Bip340Sig {
sig,
sighash_type: None,
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, SigError> {
let (hash_ty, sig) = match bytes.len() {
0 => return Err(SigError::EmptySignature),
64 => (None, bytes),
65 => (Some(bytes[64] as u32), &bytes[..64]),
invalid => return Err(SigError::Bip340Encoding(invalid)),
};
let sighash_type = hash_ty.map(SighashType::from_standard_u32).transpose()?;
let sig = schnorr::Signature::from_slice(sig).map_err(|_| SigError::InvalidSignature)?;
Ok(Bip340Sig { sig, sighash_type })
}
pub fn to_vec(self) -> Vec<u8> {
let mut ser = Vec::<u8>::with_capacity(65);
ser.extend_from_slice(&self.sig[..]);
if let Some(sighash_type) = self.sighash_type {
ser.push(sighash_type.into_consensus_u8())
}
ser
}
}
mod _strict_encode {
use std::io;
use amplify::confinement::TinyBlob;
use amplify::hex::FromHex;
use amplify::Bytes64;
use strict_encoding::{
DecodeError, ReadStruct, StrictDecode, StrictDumb, StrictEncode, TypedRead, TypedWrite,
WriteStruct,
};
use super::*;
impl StrictDumb for LegacySig {
fn strict_dumb() -> Self {
Self {
sig: ecdsa::Signature::from_der(&Vec::<u8>::from_hex(
"304402206fa6c164fb89906e2e1d291cc5461ceadf0f115c6b71e58f87482c94d512c3630220\
0ab641f3ece1d77f13ad2d8910cb7abd5a9b85f0f9036317dbb1470f22e7714c").unwrap()
).expect("hardcoded signature"),
sighash_type: default!(),
}
}
}
impl StrictEncode for LegacySig {
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
writer.write_struct::<Self>(|w| {
Ok(w.write_field(
fname!("sig"),
&TinyBlob::try_from(self.sig.serialize_der().to_vec())
.expect("invalid signature"),
)?
.write_field(fname!("sighash_type"), &self.sighash_type)?
.complete())
})
}
}
impl StrictDecode for LegacySig {
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
reader.read_struct(|r| {
let bytes: TinyBlob = r.read_field(fname!("sig"))?;
let sig = ecdsa::Signature::from_der(bytes.as_slice()).map_err(|_| {
DecodeError::DataIntegrityError(s!("invalid signature DER encoding"))
})?;
let sighash_type = r.read_field(fname!("sighash_type"))?;
Ok(Self { sig, sighash_type })
})
}
}
impl StrictDumb for Bip340Sig {
fn strict_dumb() -> Self {
Bip340Sig::from_bytes(&Vec::<u8>::from_hex(
"a12b3f4c224619d7834f0bad0a598b79111ba08146ae1205f3e6220a132aef0ed8290379624db643\
e6b861d8dcd37b406a11f91a51bf5a6cdf9b3c9b772f67c301"
).unwrap())
.expect("hardcoded signature")
}
}
impl StrictEncode for Bip340Sig {
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
writer.write_struct::<Self>(|w| {
Ok(w.write_field(fname!("sig"), &Bytes64::from(*self.sig.as_ref()))?
.write_field(fname!("sighash_type"), &self.sighash_type)?
.complete())
})
}
}
impl StrictDecode for Bip340Sig {
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
reader.read_struct(|r| {
let bytes: Bytes64 = r.read_field(fname!("sig"))?;
let sig = schnorr::Signature::from_slice(bytes.as_slice()).map_err(|_| {
DecodeError::DataIntegrityError(format!(
"invalid signature BIP340 encoding '{bytes:x}'"
))
})?;
let sighash_type = r.read_field(fname!("sighash_type"))?;
Ok(Self { sig, sighash_type })
})
}
}
}