use crate::error::{Error, Result};
use crate::primitives::ec::Signature;
use crate::primitives::BigNumber;
pub use crate::primitives::bsv::sighash::{
SIGHASH_ALL, SIGHASH_ANYONECANPAY, SIGHASH_FORKID, SIGHASH_NONE, SIGHASH_SINGLE,
};
#[derive(Clone, PartialEq, Eq)]
pub struct TransactionSignature {
signature: Signature,
scope: u32,
}
impl TransactionSignature {
pub fn new(signature: Signature, scope: u32) -> Self {
Self { signature, scope }
}
pub fn from_components(r: [u8; 32], s: [u8; 32], scope: u32) -> Self {
Self {
signature: Signature::new(r, s),
scope,
}
}
pub fn from_checksig_format(data: &[u8]) -> Result<Self> {
if data.is_empty() {
let r = BigNumber::one().to_bytes_be(32);
let s = BigNumber::one().to_bytes_be(32);
let mut r_arr = [0u8; 32];
let mut s_arr = [0u8; 32];
r_arr.copy_from_slice(&r);
s_arr.copy_from_slice(&s);
return Ok(Self {
signature: Signature::new(r_arr, s_arr),
scope: 1,
});
}
if data.len() < 2 {
return Err(Error::InvalidSignature(
"Checksig format too short".to_string(),
));
}
let scope = data[data.len() - 1] as u32;
let der_bytes = &data[..data.len() - 1];
let signature = Signature::from_der(der_bytes)?;
Ok(Self { signature, scope })
}
pub fn signature(&self) -> &Signature {
&self.signature
}
pub fn scope(&self) -> u32 {
self.scope
}
pub fn r(&self) -> &[u8; 32] {
self.signature.r()
}
pub fn s(&self) -> &[u8; 32] {
self.signature.s()
}
pub fn has_low_s(&self) -> bool {
self.signature.is_low_s()
}
pub fn to_low_s(&self) -> Self {
Self {
signature: self.signature.to_low_s(),
scope: self.scope,
}
}
pub fn to_checksig_format(&self) -> Vec<u8> {
let mut result = self.signature.to_der();
result.push((self.scope & 0xff) as u8);
result
}
pub fn to_der(&self) -> Vec<u8> {
self.signature.to_der()
}
pub fn to_compact(&self) -> [u8; 64] {
self.signature.to_compact()
}
}
impl std::fmt::Debug for TransactionSignature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransactionSignature")
.field("r", &hex::encode(self.signature.r()))
.field("s", &hex::encode(self.signature.s()))
.field("scope", &format!("0x{:08x}", self.scope))
.finish()
}
}
impl std::fmt::Display for TransactionSignature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.to_checksig_format()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checksig_format_roundtrip() {
let r = hex::decode("b4d19cdc7e93c36f3b5d6f7e8a2a6c9e3c8f9a1b2c3d4e5f6a7b8c9d0e1f2a3b")
.unwrap();
let s = hex::decode("0f8e9d0c1b2a3948576a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e")
.unwrap();
let mut r_arr = [0u8; 32];
let mut s_arr = [0u8; 32];
r_arr.copy_from_slice(&r);
s_arr.copy_from_slice(&s);
let tx_sig =
TransactionSignature::from_components(r_arr, s_arr, SIGHASH_ALL | SIGHASH_FORKID);
let encoded = tx_sig.to_checksig_format();
assert_eq!(
encoded.last().unwrap(),
&((SIGHASH_ALL | SIGHASH_FORKID) as u8)
);
let decoded = TransactionSignature::from_checksig_format(&encoded).unwrap();
assert_eq!(decoded.r(), tx_sig.r());
assert_eq!(decoded.scope() & 0xff, SIGHASH_ALL | SIGHASH_FORKID);
}
#[test]
fn test_empty_checksig() {
let sig = TransactionSignature::from_checksig_format(&[]).unwrap();
assert_eq!(sig.scope(), 1);
}
#[test]
fn test_scope_values() {
assert_eq!(SIGHASH_ALL, 0x01);
assert_eq!(SIGHASH_NONE, 0x02);
assert_eq!(SIGHASH_SINGLE, 0x03);
assert_eq!(SIGHASH_FORKID, 0x40);
assert_eq!(SIGHASH_ANYONECANPAY, 0x80);
assert_eq!(SIGHASH_ALL | SIGHASH_FORKID, 0x41);
assert_eq!(SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY, 0xc1);
}
#[test]
fn test_has_low_s() {
let r = [0u8; 32];
let mut s = [0u8; 32];
s[31] = 0x01;
let sig = TransactionSignature::from_components(r, s, SIGHASH_ALL);
assert!(sig.has_low_s());
}
#[test]
fn test_to_low_s() {
let r = [0u8; 32];
let s_high =
hex::decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140")
.unwrap();
let mut s_arr = [0u8; 32];
s_arr.copy_from_slice(&s_high);
let sig = TransactionSignature::from_components(r, s_arr, SIGHASH_ALL);
assert!(!sig.has_low_s());
let low_sig = sig.to_low_s();
assert!(low_sig.has_low_s());
}
#[test]
fn test_new() {
let r = [1u8; 32];
let s = [2u8; 32];
let ecdsa_sig = Signature::new(r, s);
let tx_sig = TransactionSignature::new(ecdsa_sig.clone(), SIGHASH_ALL | SIGHASH_FORKID);
assert_eq!(tx_sig.signature().r(), &r);
assert_eq!(tx_sig.signature().s(), &s);
assert_eq!(tx_sig.scope(), SIGHASH_ALL | SIGHASH_FORKID);
}
}