1use crate::OpCodes::OP_0;
2use std::str::FromStr;
3
4use crate::{BSVErrors, OpCodes, PublicKey, Script, ScriptBit, Signature, VarInt};
5use hex::FromHexError;
6use num_traits::FromPrimitive;
7use serde::{Deserialize, Serialize};
8use strum_macros::Display;
9use thiserror::Error;
10
11#[derive(Debug, Error)]
12pub enum ScriptTemplateErrors {
13 #[error("Script did not match template at index {0}. {2} is not equal to {1:?}. Error: {3:?}")]
14 MatchFailure(usize, MatchToken, ScriptBit, BSVErrors),
15
16 #[error("Failed to parse OP_DATA code {0}: {1}")]
17 OpDataParse(String, String),
18
19 #[error("Script Template and Script lengths do not match.")]
20 LengthsDiffer,
21
22 #[error("{0}")]
23 MalformedHex(
24 #[from]
25 #[source]
26 FromHexError,
27 ),
28}
29
30#[derive(Debug, Clone, Display)]
31pub enum DataLengthConstraints {
32 Equals,
33 GreaterThan,
34 LessThan,
35 GreaterThanOrEquals,
36 LessThanOrEquals,
37}
38
39#[derive(Debug, Clone, Display)]
40pub enum MatchToken {
41 OpCode(OpCodes),
43 Push(Vec<u8>),
44 PushData(OpCodes, Vec<u8>),
45
46 AnyData,
48 Data(usize, DataLengthConstraints),
49 Signature,
50 PublicKey,
51 PublicKeyHash,
52}
53
54#[derive(Debug, Clone, Display, Serialize, Deserialize)]
55pub enum MatchDataTypes {
56 Data,
57 Signature,
58 PublicKey,
59 PublicKeyHash,
60}
61
62#[derive(Debug, Clone)]
63pub struct ScriptTemplate(Vec<MatchToken>);
64
65impl ScriptTemplate {
66 fn map_string_to_match_token(code: &str) -> Result<MatchToken, ScriptTemplateErrors> {
67 if code.len() < 3 {
69 if let Ok(num_code) = u8::from_str(code) {
70 match num_code {
71 0 => return Ok(MatchToken::OpCode(OP_0)),
72 v @ 1..=16 => return Ok(MatchToken::OpCode(OpCodes::from_u8(v + 80).unwrap())),
73 _ => (),
74 }
75 }
76 }
77
78 match OpCodes::from_str(code) {
80 Ok(OpCodes::OP_SIG) => return Ok(MatchToken::Signature),
81 Ok(OpCodes::OP_PUBKEY) => return Ok(MatchToken::PublicKey),
82 Ok(OpCodes::OP_PUBKEYHASH) => return Ok(MatchToken::PublicKeyHash),
83 Ok(OpCodes::OP_DATA) => return Ok(MatchToken::AnyData),
84
85 Ok(v) => return Ok(MatchToken::OpCode(v)),
86 Err(_) => (),
87 }
88
89 if code.starts_with(&OpCodes::OP_DATA.to_string()) {
90 if let Some((_, length_str)) = code.split_once(">=") {
92 let len = usize::from_str(length_str).map_err(|e| ScriptTemplateErrors::OpDataParse(code.to_string(), e.to_string()))?;
93 return Ok(MatchToken::Data(len, DataLengthConstraints::GreaterThanOrEquals));
94 }
95
96 if let Some((_, length_str)) = code.split_once("<=") {
98 let len = usize::from_str(length_str).map_err(|e| ScriptTemplateErrors::OpDataParse(code.to_string(), e.to_string()))?;
99 return Ok(MatchToken::Data(len, DataLengthConstraints::LessThanOrEquals));
100 }
101
102 if let Some((_, length_str)) = code.split_once('=') {
104 let len = usize::from_str(length_str).map_err(|e| ScriptTemplateErrors::OpDataParse(code.to_string(), e.to_string()))?;
105 return Ok(MatchToken::Data(len, DataLengthConstraints::Equals));
106 }
107
108 if let Some((_, length_str)) = code.split_once('>') {
110 let len = usize::from_str(length_str).map_err(|e| ScriptTemplateErrors::OpDataParse(code.to_string(), e.to_string()))?;
111 return Ok(MatchToken::Data(len, DataLengthConstraints::GreaterThan));
112 }
113
114 if let Some((_, length_str)) = code.split_once('<') {
116 let len = usize::from_str(length_str).map_err(|e| ScriptTemplateErrors::OpDataParse(code.to_string(), e.to_string()))?;
117 return Ok(MatchToken::Data(len, DataLengthConstraints::LessThan));
118 }
119 }
120
121 let data_bytes = hex::decode(code)?;
123 let token = match VarInt::get_pushdata_opcode(data_bytes.len() as u64) {
124 Some(v) => MatchToken::PushData(v, data_bytes),
125 None => MatchToken::Push(data_bytes),
126 };
127
128 Ok(token)
129 }
130
131 pub fn from_script_impl(script: &Script) -> Result<ScriptTemplate, ScriptTemplateErrors> {
132 ScriptTemplate::from_asm_string_impl(&script.to_asm_string_impl(false))
133 }
134
135 pub fn from_asm_string_impl(asm: &str) -> Result<ScriptTemplate, ScriptTemplateErrors> {
136 let tokens: Result<Vec<_>, _> = asm.split(' ').map(ScriptTemplate::map_string_to_match_token).collect();
137
138 Ok(ScriptTemplate(tokens?))
139 }
140}
141
142impl ScriptTemplate {
143 pub fn from_script(script: &Script) -> Result<ScriptTemplate, ScriptTemplateErrors> {
144 ScriptTemplate::from_script_impl(script)
145 }
146
147 pub fn from_asm_string(asm: &str) -> Result<ScriptTemplate, ScriptTemplateErrors> {
148 ScriptTemplate::from_asm_string_impl(asm)
149 }
150}
151
152impl Script {
168 pub fn match_impl(&self, script_template: &ScriptTemplate) -> Result<Vec<(MatchDataTypes, Vec<u8>)>, ScriptTemplateErrors> {
169 if self.0.len() != script_template.0.len() {
170 return Err(ScriptTemplateErrors::LengthsDiffer);
171 }
172
173 let mut matches = vec![];
174
175 for (i, (template, script)) in script_template.0.iter().zip(self.0.iter()).enumerate() {
176 let is_match = match (template, script) {
177 (MatchToken::OpCode(tmpl_code), ScriptBit::OpCode(op_code)) => Ok(tmpl_code == op_code),
178 (MatchToken::Push(tmpl_data), ScriptBit::Push(data)) => Ok(*tmpl_data == *data),
179 (MatchToken::PushData(tmpl_op, tmpl_data), ScriptBit::PushData(op, data)) => Ok(tmpl_op == op && tmpl_data == data),
180
181 (MatchToken::Data(len, constraint), ScriptBit::PushData(_, data) | ScriptBit::Push(data)) => match constraint {
182 DataLengthConstraints::Equals => Ok(&data.len() == len),
183 DataLengthConstraints::GreaterThan => Ok(&data.len() > len),
184 DataLengthConstraints::LessThan => Ok(&data.len() < len),
185 DataLengthConstraints::GreaterThanOrEquals => Ok(&data.len() >= len),
186 DataLengthConstraints::LessThanOrEquals => Ok(&data.len() <= len),
187 },
188
189 (MatchToken::AnyData, ScriptBit::Push(_)) => Ok(true),
190 (MatchToken::AnyData, ScriptBit::PushData(_, _)) => Ok(true),
191
192 (MatchToken::Signature, ScriptBit::Push(sig_buf)) => Signature::from_der_impl(sig_buf).map(|_| true),
193
194 (MatchToken::PublicKey, ScriptBit::Push(pubkey_buf)) => PublicKey::from_bytes_impl(pubkey_buf).map(|_| true),
195
196 (MatchToken::PublicKeyHash, ScriptBit::Push(pubkeyhash_buf)) => Ok(pubkeyhash_buf.len() == 20), _ => Ok(false),
199 };
200
201 match is_match {
202 Ok(true) => (),
203 Ok(false) => {
204 return Err(ScriptTemplateErrors::MatchFailure(
205 i,
206 template.clone(),
207 script.clone(),
208 BSVErrors::GenericError(format!("{} != {}", template, script)),
209 ));
210 }
211 Err(e) => {
212 return Err(ScriptTemplateErrors::MatchFailure(i, template.clone(), script.clone(), e));
213 }
214 }
215
216 match (template, script) {
218 (MatchToken::Data(_, _), ScriptBit::PushData(_, data) | ScriptBit::Push(data)) => matches.push((MatchDataTypes::Data, data.clone())),
219
220 (MatchToken::AnyData, ScriptBit::Push(data)) => matches.push((MatchDataTypes::Data, data.clone())),
221 (MatchToken::AnyData, ScriptBit::PushData(_, data)) => matches.push((MatchDataTypes::Data, data.clone())),
222
223 (MatchToken::Signature, ScriptBit::Push(data)) => matches.push((MatchDataTypes::Signature, data.clone())),
224
225 (MatchToken::PublicKey, ScriptBit::Push(data)) => matches.push((MatchDataTypes::PublicKey, data.clone())),
226
227 (MatchToken::PublicKeyHash, ScriptBit::Push(data)) => matches.push((MatchDataTypes::PublicKeyHash, data.clone())), _ => (),
229 }
230 }
231
232 Ok(matches)
233 }
234
235 pub fn test_impl(&self, script_template: &ScriptTemplate) -> bool {
236 self.match_impl(script_template).is_ok()
237 }
238}
239
240impl Script {
241 pub fn matches(&self, script_template: &ScriptTemplate) -> Result<Vec<(MatchDataTypes, Vec<u8>)>, ScriptTemplateErrors> {
264 self.match_impl(script_template)
265 }
266
267 pub fn is_match(&self, script_template: &ScriptTemplate) -> bool {
271 self.test_impl(script_template)
272 }
273}
274
275