use crate::types::{Transaction, encode_varint};
use sha2::{Sha256, Digest};
pub mod sighash_type {
pub const ALL: u32 = 0x01;
pub const NONE: u32 = 0x02;
pub const SINGLE: u32 = 0x03;
pub const ANYONECANPAY: u32 = 0x80;
}
pub fn sighash_legacy(
tx: &Transaction,
input_index: usize,
script_pubkey: &[u8],
sighash_type: u32,
) -> [u8; 32] {
let mut bytes = Vec::new();
bytes.extend_from_slice(&tx.version.to_le_bytes());
bytes.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
for (i, input) in tx.inputs.iter().enumerate() {
bytes.extend_from_slice(&input.txid);
bytes.extend_from_slice(&input.vout.to_le_bytes());
if i == input_index {
bytes.extend_from_slice(&encode_varint(script_pubkey.len() as u64));
bytes.extend_from_slice(script_pubkey);
} else {
bytes.push(0x00);
}
bytes.extend_from_slice(&input.sequence.to_le_bytes());
}
bytes.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
for output in &tx.outputs {
bytes.extend_from_slice(&output.value.to_le_bytes());
bytes.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
bytes.extend_from_slice(&output.script_pubkey);
}
bytes.extend_from_slice(&tx.locktime.to_le_bytes());
bytes.extend_from_slice(&sighash_type.to_le_bytes());
let hash1 = Sha256::digest(&bytes);
let hash2 = Sha256::digest(hash1);
hash2.into()
}
pub fn sighash_segwit(
tx: &Transaction,
input_index: usize,
script_code: &[u8],
value: u64,
sighash_type: u32,
) -> [u8; 32] {
let version = tx.version.to_le_bytes();
let hash_prevouts = {
let mut data = Vec::new();
for input in &tx.inputs {
data.extend_from_slice(&input.txid);
data.extend_from_slice(&input.vout.to_le_bytes());
}
double_sha256(&data)
};
let hash_sequence = {
let mut data = Vec::new();
for input in &tx.inputs {
data.extend_from_slice(&input.sequence.to_le_bytes());
}
double_sha256(&data)
};
let input = &tx.inputs[input_index];
let outpoint_txid = input.txid;
let outpoint_vout = input.vout.to_le_bytes();
let script_code_len = encode_varint(script_code.len() as u64);
let value_bytes = value.to_le_bytes();
let sequence = input.sequence.to_le_bytes();
let hash_outputs = {
let mut data = Vec::new();
for output in &tx.outputs {
data.extend_from_slice(&output.value.to_le_bytes());
data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
data.extend_from_slice(&output.script_pubkey);
}
double_sha256(&data)
};
let locktime = tx.locktime.to_le_bytes();
let sighash = sighash_type.to_le_bytes();
let mut preimage = Vec::new();
preimage.extend_from_slice(&version);
preimage.extend_from_slice(&hash_prevouts);
preimage.extend_from_slice(&hash_sequence);
preimage.extend_from_slice(&outpoint_txid);
preimage.extend_from_slice(&outpoint_vout);
preimage.extend_from_slice(&script_code_len);
preimage.extend_from_slice(script_code);
preimage.extend_from_slice(&value_bytes);
preimage.extend_from_slice(&sequence);
preimage.extend_from_slice(&hash_outputs);
preimage.extend_from_slice(&locktime);
preimage.extend_from_slice(&sighash);
double_sha256(&preimage)
}
fn double_sha256(data: &[u8]) -> [u8; 32] {
let hash1 = Sha256::digest(data);
let hash2 = Sha256::digest(hash1);
hash2.into()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{TxInput, TxOutput};
#[test]
fn test_sighash_legacy() {
let mut tx = Transaction::new();
tx.inputs.push(TxInput::new([0u8; 32], 0));
tx.outputs.push(TxOutput::new(50_000, vec![0x76, 0xa9]));
let script = vec![0x76, 0xa9, 0x14]; let hash = sighash_legacy(&tx, 0, &script, sighash_type::ALL);
assert_eq!(hash.len(), 32);
}
#[test]
fn test_sighash_segwit() {
let mut tx = Transaction::new();
tx.inputs.push(TxInput::new([0u8; 32], 0));
tx.outputs.push(TxOutput::new(50_000, vec![0x00, 0x14]));
let script_code = vec![0x76, 0xa9, 0x14]; let hash = sighash_segwit(&tx, 0, &script_code, 100_000, sighash_type::ALL);
assert_eq!(hash.len(), 32);
}
}