use crate::engine::{execute_on_stack, ExecuteOpts};
use crate::error::ScriptError;
use crate::stack::{is_true, Stack};
use crate::tokenizer::parse_script;
pub fn validate_p2pkh(script_sig: &[u8], script_pubkey: &[u8]) -> Result<bool, ScriptError> {
validate_p2pkh_with_opts(script_sig, script_pubkey, &ExecuteOpts::default())
}
pub fn validate_p2pkh_with_opts(
script_sig: &[u8],
script_pubkey: &[u8],
opts: &ExecuteOpts,
) -> Result<bool, ScriptError> {
let sig_tokens = parse_script(script_sig)?;
let pk_tokens = parse_script(script_pubkey)?;
let mut stack = Stack::new();
execute_on_stack(&sig_tokens, &mut stack, opts)?;
execute_on_stack(&pk_tokens, &mut stack, opts)?;
if stack.is_empty() {
return Ok(false);
}
let top = stack.pop()?;
Ok(is_true(&top))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash;
fn build_script_sig(sig: &[u8], pubkey: &[u8]) -> Vec<u8> {
let mut script = Vec::new();
assert!(sig.len() <= 0x4b);
script.push(sig.len() as u8);
script.extend_from_slice(sig);
assert!(pubkey.len() <= 0x4b);
script.push(pubkey.len() as u8);
script.extend_from_slice(pubkey);
script
}
fn build_script_pubkey(pubkey_hash: &[u8; 20]) -> Vec<u8> {
let mut script = Vec::new();
script.push(0x76); script.push(0xa9); script.push(0x14); script.extend_from_slice(pubkey_hash);
script.push(0x88); script.push(0xac); script
}
#[test]
fn p2pkh_stub_valid() {
let fake_sig = b"fake-signature";
let fake_pubkey = b"fake-public-key-data";
let pubkey_hash = hash::hash160(fake_pubkey);
let script_sig = build_script_sig(fake_sig, fake_pubkey);
let script_pubkey = build_script_pubkey(&pubkey_hash);
let result = validate_p2pkh(&script_sig, &script_pubkey).unwrap();
assert!(result);
}
#[test]
fn p2pkh_wrong_pubkey_hash() {
let fake_sig = b"fake-signature";
let fake_pubkey = b"fake-public-key-data";
let wrong_hash = [0xab; 20];
let script_sig = build_script_sig(fake_sig, fake_pubkey);
let script_pubkey = build_script_pubkey(&wrong_hash);
let err = validate_p2pkh(&script_sig, &script_pubkey).unwrap_err();
assert!(matches!(err, ScriptError::VerifyFailed));
}
#[test]
fn p2pkh_empty_scriptsig() {
let pubkey_hash = [0x00; 20];
let script_pubkey = build_script_pubkey(&pubkey_hash);
let err = validate_p2pkh(&[], &script_pubkey).unwrap_err();
assert!(matches!(err, ScriptError::StackUnderflow));
}
#[test]
fn p2pkh_with_opts_stub() {
let fake_sig = b"sig";
let fake_pubkey = b"key";
let pubkey_hash = hash::hash160(fake_pubkey);
let script_sig = build_script_sig(fake_sig, fake_pubkey);
let script_pubkey = build_script_pubkey(&pubkey_hash);
let opts = ExecuteOpts { sighash: None };
let result = validate_p2pkh_with_opts(&script_sig, &script_pubkey, &opts).unwrap();
assert!(result);
}
#[test]
fn two_phase_isolation() {
let script_sig = vec![0x6a]; let script_pubkey = vec![0x51];
let err = validate_p2pkh(&script_sig, &script_pubkey).unwrap_err();
assert!(matches!(err, ScriptError::OpReturnEncountered));
}
}