use bsv_primitives::hash::sha256d;
use bsv_primitives::util::{BsvWriter, VarInt};
use crate::transaction::Transaction;
use crate::TransactionError;
pub const SIGHASH_ALL: u32 = 0x01;
pub const SIGHASH_NONE: u32 = 0x02;
pub const SIGHASH_SINGLE: u32 = 0x03;
pub const SIGHASH_ANYONECANPAY: u32 = 0x80;
pub const SIGHASH_FORKID: u32 = 0x40;
pub const SIGHASH_ALL_FORKID: u32 = SIGHASH_ALL | SIGHASH_FORKID;
pub const SIGHASH_MASK: u32 = 0x1f;
pub fn signature_hash(
tx: &Transaction,
input_index: usize,
prev_output_script: &[u8],
sighash_type: u32,
satoshis: u64,
) -> Result<[u8; 32], TransactionError> {
if input_index >= tx.inputs.len() {
return Err(TransactionError::InvalidTransaction(format!(
"input index {} out of range (tx has {} inputs)",
input_index,
tx.inputs.len()
)));
}
let preimage = calc_preimage(tx, input_index, prev_output_script, sighash_type, satoshis)?;
Ok(sha256d(&preimage))
}
pub fn calc_preimage(
tx: &Transaction,
input_index: usize,
prev_output_script: &[u8],
sighash_type: u32,
satoshis: u64,
) -> Result<Vec<u8>, TransactionError> {
if input_index >= tx.inputs.len() {
return Err(TransactionError::InvalidTransaction(format!(
"input index {} out of range (tx has {} inputs)",
input_index,
tx.inputs.len()
)));
}
let input = &tx.inputs[input_index];
let base_type = sighash_type & SIGHASH_MASK;
let hash_prevouts = if sighash_type & SIGHASH_ANYONECANPAY == 0 {
source_out_hash(tx)
} else {
[0u8; 32]
};
let hash_sequence = if sighash_type & SIGHASH_ANYONECANPAY == 0
&& base_type != SIGHASH_SINGLE
&& base_type != SIGHASH_NONE
{
sequence_hash(tx)
} else {
[0u8; 32]
};
let hash_outputs = if base_type != SIGHASH_SINGLE && base_type != SIGHASH_NONE {
outputs_hash(tx, -1)
} else if base_type == SIGHASH_SINGLE && input_index < tx.outputs.len() {
outputs_hash(tx, input_index as i32)
} else {
[0u8; 32]
};
let mut writer = BsvWriter::with_capacity(256);
writer.write_u32_le(tx.version);
writer.write_bytes(&hash_prevouts);
writer.write_bytes(&hash_sequence);
writer.write_bytes(&input.source_txid);
writer.write_u32_le(input.source_tx_out_index);
writer.write_varint(VarInt::from(prev_output_script.len()));
writer.write_bytes(prev_output_script);
writer.write_u64_le(satoshis);
writer.write_u32_le(input.sequence_number);
writer.write_bytes(&hash_outputs);
writer.write_u32_le(tx.lock_time);
writer.write_u32_le(sighash_type);
Ok(writer.into_bytes())
}
fn source_out_hash(tx: &Transaction) -> [u8; 32] {
let mut writer = BsvWriter::with_capacity(tx.inputs.len() * 36);
for input in &tx.inputs {
writer.write_bytes(&input.source_txid);
writer.write_u32_le(input.source_tx_out_index);
}
sha256d(writer.as_bytes())
}
fn sequence_hash(tx: &Transaction) -> [u8; 32] {
let mut writer = BsvWriter::with_capacity(tx.inputs.len() * 4);
for input in &tx.inputs {
writer.write_u32_le(input.sequence_number);
}
sha256d(writer.as_bytes())
}
fn outputs_hash(tx: &Transaction, n: i32) -> [u8; 32] {
let mut writer = BsvWriter::new();
if n == -1 {
for output in &tx.outputs {
writer.write_bytes(&output.bytes_for_sig_hash());
}
} else {
writer.write_bytes(&tx.outputs[n as usize].bytes_for_sig_hash());
}
sha256d(writer.as_bytes())
}