use bsv_primitives::ec::PrivateKey;
use bsv_script::opcodes::*;
use bsv_script::{Address, Script};
use crate::sighash::SIGHASH_ALL_FORKID;
use crate::template::UnlockingScriptTemplate;
use crate::transaction::Transaction;
use crate::TransactionError;
pub fn lock(address: &Address) -> Result<Script, TransactionError> {
let pkh = &address.public_key_hash;
let mut bytes = Vec::with_capacity(25);
bytes.push(OP_DUP);
bytes.push(OP_HASH160);
bytes.push(OP_DATA_20);
bytes.extend_from_slice(pkh);
bytes.push(OP_EQUALVERIFY);
bytes.push(OP_CHECKSIG);
Ok(Script::from_bytes(&bytes))
}
pub fn unlock(private_key: PrivateKey, sighash_flag: Option<u32>) -> P2PKH {
P2PKH {
private_key,
sighash_flag: sighash_flag.unwrap_or(SIGHASH_ALL_FORKID),
}
}
pub struct P2PKH {
private_key: PrivateKey,
sighash_flag: u32,
}
impl UnlockingScriptTemplate for P2PKH {
fn sign(&self, tx: &Transaction, input_index: u32) -> Result<Script, TransactionError> {
let idx = input_index as usize;
if idx >= tx.inputs.len() {
return Err(TransactionError::SigningError(format!(
"input index {} out of range (tx has {} inputs)",
idx,
tx.inputs.len()
)));
}
let input = &tx.inputs[idx];
if input.source_tx_output().is_none() {
return Err(TransactionError::SigningError(
"missing source output on input (no previous tx info)".to_string(),
));
}
let sig_hash = tx.calc_input_signature_hash(idx, self.sighash_flag)?;
let signature = self.private_key.sign(&sig_hash)?;
let pub_key_bytes = self.private_key.pub_key().to_compressed();
let der_sig = signature.to_der();
let mut sig_buf = Vec::with_capacity(der_sig.len() + 1);
sig_buf.extend_from_slice(&der_sig);
sig_buf.push(self.sighash_flag as u8);
let mut script = Script::new();
script.append_push_data(&sig_buf)?;
script.append_push_data(&pub_key_bytes)?;
Ok(script)
}
fn estimate_length(&self, _tx: &Transaction, _input_index: u32) -> u32 {
106
}
}