use crate::script::op_codes::{OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160, OP_PUSH};
use crate::script::{Script, next_op};
use crate::util::{Error, Hash160, Result};
#[must_use]
#[inline]
pub fn create_lock_script(address: &Hash160) -> Script {
let mut script = Script::new();
script.append(OP_DUP);
script.append(OP_HASH160);
script.append(OP_PUSH + 20);
script.append_slice(&address.0);
script.append(OP_EQUALVERIFY);
script.append(OP_CHECKSIG);
script
}
#[must_use]
#[inline]
pub fn create_unlock_script(sig: &[u8], public_key: &[u8]) -> Script {
let mut script = Script::new();
script.append_data(sig).unwrap();
script.append_data(public_key).unwrap();
script
}
#[must_use]
#[inline]
pub fn check_lock_script(lock_script: &[u8]) -> bool {
lock_script.len() == 25
&& lock_script[0] == OP_DUP
&& lock_script[1] == OP_HASH160
&& lock_script[2] == OP_PUSH + 20
&& lock_script[23] == OP_EQUALVERIFY
&& lock_script[24] == OP_CHECKSIG
}
#[must_use]
#[inline]
pub fn check_unlock_script(unlock_script: &[u8]) -> bool {
if unlock_script.is_empty() {
return false;
}
let sig_len = unlock_script[0];
if sig_len < OP_PUSH + 71 || sig_len > OP_PUSH + 73 {
return false;
}
let i = next_op(0, unlock_script);
if i >= unlock_script.len() {
return false;
}
let pk_len = unlock_script[i];
if pk_len != OP_PUSH + 33 && pk_len != OP_PUSH + 65 {
return false;
}
next_op(i, unlock_script) == unlock_script.len()
}
#[must_use]
#[inline]
pub fn check_lock_script_addr(hash160: &Hash160, lock_script: &[u8]) -> bool {
check_lock_script(lock_script) && lock_script[3..23] == hash160.0
}
#[must_use]
#[inline]
pub fn check_unlock_script_addr(pubkey: &[u8], unlock_script: &[u8]) -> bool {
if !check_unlock_script(unlock_script) {
return false;
}
let i = next_op(0, unlock_script);
unlock_script[i + 1..] == *pubkey
}
#[must_use]
pub fn extract_pubkey(unlock_script: &[u8]) -> Result<Vec<u8>> {
if !check_unlock_script(unlock_script) {
return Err(Error::BadData("Not P2PKH unlock".to_string()));
}
let i = next_op(0, unlock_script);
Ok(unlock_script[i + 1..].to_vec())
}
#[must_use]
pub fn extract_pubkeyhash(lock_script: &[u8]) -> Result<Hash160> {
if !check_lock_script(lock_script) {
return Err(Error::BadData("Not P2PKH lock".to_string()));
}
let mut hash160 = Hash160([0; 20]);
hash160.0.copy_from_slice(&lock_script[3..23]);
Ok(hash160)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::script::op_codes::OP_1;
#[test]
fn check_lock_script_test() {
let mut s = Script::new();
assert!(!check_lock_script(&s.0));
s.append(OP_DUP);
s.append(OP_HASH160);
s.append(OP_PUSH + 20);
s.append_slice(&[0; 20]);
s.append(OP_EQUALVERIFY);
s.append(OP_CHECKSIG);
assert!(check_lock_script(&s.0));
s.append(OP_1);
assert!(!check_lock_script(&s.0));
}
#[test]
fn check_unlock_script_test() {
assert!(!check_unlock_script(&Script::new().0));
let mut sig71pkh33 = Script::new();
sig71pkh33.append(OP_PUSH + 71);
sig71pkh33.append_slice(&[0; 71]);
assert!(!check_unlock_script(&sig71pkh33.0));
sig71pkh33.append(OP_PUSH + 33);
sig71pkh33.append_slice(&[0; 33]);
assert!(check_unlock_script(&sig71pkh33.0));
sig71pkh33.append(OP_1);
assert!(!check_unlock_script(&sig71pkh33.0));
let mut sig73pkh65 = Script::new();
sig73pkh65.append(OP_PUSH + 73);
sig73pkh65.append_slice(&[0; 73]);
sig73pkh65.append(OP_PUSH + 65);
sig73pkh65.append_slice(&[0; 65]);
assert!(check_unlock_script(&sig73pkh65.0));
let mut sig72pkh30 = Script::new();
sig72pkh30.append(OP_PUSH + 72);
sig72pkh30.append_slice(&[0; 72]);
sig72pkh30.append(OP_PUSH + 30);
sig72pkh30.append_slice(&[0; 30]);
assert!(!check_unlock_script(&sig72pkh30.0));
}
#[test]
fn check_lock_script_addr_test() {
let s = create_lock_script(&Hash160([5; 20]));
assert!(check_lock_script_addr(&Hash160([5; 20]), &s.0));
}
#[test]
fn check_unlock_script_addr_test() {
let mut s = Script::new();
s.append(OP_PUSH + 71);
s.append_slice(&[5; 71]);
s.append(OP_PUSH + 65);
s.append_slice(&[6; 65]);
assert!(check_unlock_script_addr(&[6; 65], &s.0));
assert!(!check_unlock_script_addr(&[7; 65], &s.0));
}
}