mini_bitcoin_script/
script.rs1use crate::engine::{execute_on_stack, ExecuteOpts};
2use crate::error::ScriptError;
3use crate::stack::{is_true, Stack};
4use crate::tokenizer::parse_script;
5
6pub fn validate_p2pkh(script_sig: &[u8], script_pubkey: &[u8]) -> Result<bool, ScriptError> {
24 validate_p2pkh_with_opts(script_sig, script_pubkey, &ExecuteOpts::default())
25}
26
27pub fn validate_p2pkh_with_opts(
32 script_sig: &[u8],
33 script_pubkey: &[u8],
34 opts: &ExecuteOpts,
35) -> Result<bool, ScriptError> {
36 let sig_tokens = parse_script(script_sig)?;
37 let pk_tokens = parse_script(script_pubkey)?;
38
39 let mut stack = Stack::new();
40
41 execute_on_stack(&sig_tokens, &mut stack, opts)?;
43
44 execute_on_stack(&pk_tokens, &mut stack, opts)?;
46
47 if stack.is_empty() {
49 return Ok(false);
50 }
51 let top = stack.pop()?;
52 Ok(is_true(&top))
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::hash;
59
60 fn build_script_sig(sig: &[u8], pubkey: &[u8]) -> Vec<u8> {
62 let mut script = Vec::new();
63 assert!(sig.len() <= 0x4b);
65 script.push(sig.len() as u8);
66 script.extend_from_slice(sig);
67 assert!(pubkey.len() <= 0x4b);
69 script.push(pubkey.len() as u8);
70 script.extend_from_slice(pubkey);
71 script
72 }
73
74 fn build_script_pubkey(pubkey_hash: &[u8; 20]) -> Vec<u8> {
77 let mut script = Vec::new();
78 script.push(0x76); script.push(0xa9); script.push(0x14); script.extend_from_slice(pubkey_hash);
82 script.push(0x88); script.push(0xac); script
85 }
86
87 #[test]
88 fn p2pkh_stub_valid() {
89 let fake_sig = b"fake-signature";
90 let fake_pubkey = b"fake-public-key-data";
91 let pubkey_hash = hash::hash160(fake_pubkey);
92
93 let script_sig = build_script_sig(fake_sig, fake_pubkey);
94 let script_pubkey = build_script_pubkey(&pubkey_hash);
95
96 let result = validate_p2pkh(&script_sig, &script_pubkey).unwrap();
98 assert!(result);
99 }
100
101 #[test]
102 fn p2pkh_wrong_pubkey_hash() {
103 let fake_sig = b"fake-signature";
104 let fake_pubkey = b"fake-public-key-data";
105 let wrong_hash = [0xab; 20]; let script_sig = build_script_sig(fake_sig, fake_pubkey);
108 let script_pubkey = build_script_pubkey(&wrong_hash);
109
110 let err = validate_p2pkh(&script_sig, &script_pubkey).unwrap_err();
112 assert!(matches!(err, ScriptError::VerifyFailed));
113 }
114
115 #[test]
116 fn p2pkh_empty_scriptsig() {
117 let pubkey_hash = [0x00; 20];
118 let script_pubkey = build_script_pubkey(&pubkey_hash);
119
120 let err = validate_p2pkh(&[], &script_pubkey).unwrap_err();
123 assert!(matches!(err, ScriptError::StackUnderflow));
124 }
125
126 #[test]
127 fn p2pkh_with_opts_stub() {
128 let fake_sig = b"sig";
129 let fake_pubkey = b"key";
130 let pubkey_hash = hash::hash160(fake_pubkey);
131
132 let script_sig = build_script_sig(fake_sig, fake_pubkey);
133 let script_pubkey = build_script_pubkey(&pubkey_hash);
134
135 let opts = ExecuteOpts { sighash: None };
136 let result = validate_p2pkh_with_opts(&script_sig, &script_pubkey, &opts).unwrap();
137 assert!(result);
138 }
139
140 #[test]
141 fn two_phase_isolation() {
142 let script_sig = vec![0x6a]; let script_pubkey = vec![0x51]; let err = validate_p2pkh(&script_sig, &script_pubkey).unwrap_err();
148 assert!(matches!(err, ScriptError::OpReturnEncountered));
149 }
150}