1pub mod op_codes;
2pub use op_codes::*;
3
4pub mod script_bit;
5pub use script_bit::*;
6
7use crate::OpCodes::OP_0;
8use strum_macros::Display;
9
10use crate::utils::{from_hex, to_hex};
11use std::{
12 io::{Cursor, Read},
13 slice::Iter,
14 str::FromStr,
15 usize,
16};
17
18use crate::{BSVErrors, VarInt};
19use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
20use num_traits::{FromPrimitive, ToPrimitive};
21
22use serde::{Deserialize, Serialize};
23#[cfg(target_arch = "wasm32")]
24use wasm_bindgen::{prelude::*, throw_str};
25
26mod script_template;
27pub use script_template::*;
28
29#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
30#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub struct Script(pub(crate) Vec<ScriptBit>);
32
33impl Script {
37 fn script_bits_to_asm_string(codes: &[ScriptBit], extended: bool) -> String {
38 codes
39 .iter()
40 .map(|x| match x {
41 ScriptBit::OpCode(OP_0) => match extended {
42 true => OP_0.to_string(),
43 false => 0.to_string(),
44 },
45 ScriptBit::Push(bytes) => match extended {
46 true => format!("OP_PUSH {} {}", bytes.len(), hex::encode(bytes)),
47 false => hex::encode(bytes),
48 },
49 ScriptBit::PushData(code, bytes) => match extended {
50 true => format!("{} {} {}", code, bytes.len(), hex::encode(bytes)),
51 false => hex::encode(bytes),
52 },
53 ScriptBit::OpCode(code) => code.to_string(),
54 ScriptBit::If { code, pass, fail } => {
55 format!(
56 "{} {} {} {} {}",
57 code,
58 Script::script_bits_to_asm_string(pass, extended),
59 OpCodes::OP_ELSE,
60 Script::script_bits_to_asm_string(fail, extended),
61 OpCodes::OP_ENDIF
62 )
63 }
64 ScriptBit::Coinbase(bytes) => hex::encode(bytes),
65 })
66 .collect::<Vec<String>>()
67 .join(" ")
68 }
69
70 fn script_bits_to_bytes(codes: &[ScriptBit]) -> Vec<u8> {
71 let bytes = codes
72 .iter()
73 .flat_map(|x| match x {
74 ScriptBit::OpCode(code) => vec![*code as u8],
75 ScriptBit::Push(bytes) => {
76 let mut pushbytes = bytes.clone();
77 pushbytes.insert(0, bytes.len() as u8);
78 pushbytes
79 }
80 ScriptBit::PushData(code, bytes) => {
81 let mut pushbytes = vec![*code as u8];
82
83 let length_bytes = match code {
84 OpCodes::OP_PUSHDATA1 => (bytes.len() as u8).to_le_bytes().to_vec(),
85 OpCodes::OP_PUSHDATA2 => (bytes.len() as u16).to_le_bytes().to_vec(),
86 _ => (bytes.len() as u32).to_le_bytes().to_vec(),
87 };
88 pushbytes.extend(length_bytes);
89 pushbytes.extend(bytes);
90 pushbytes
91 }
92 ScriptBit::If { code, pass, fail } => {
93 let mut bytes = vec![*code as u8];
94
95 bytes.extend_from_slice(&Script::script_bits_to_bytes(pass));
96 bytes.push(OpCodes::OP_ELSE as u8);
97 bytes.extend_from_slice(&Script::script_bits_to_bytes(fail));
98 bytes.push(OpCodes::OP_ENDIF as u8);
99
100 bytes
101 }
102 ScriptBit::Coinbase(bytes) => bytes.to_vec(),
103 })
104 .collect();
105
106 bytes
107 }
108
109 pub(crate) fn to_asm_string_impl(&self, extended: bool) -> String {
110 Script::script_bits_to_asm_string(&self.0, extended)
111 }
112}
113
114impl Script {
118 pub(crate) fn from_hex_impl(hex: &str) -> Result<Script, BSVErrors> {
119 Script::from_bytes_impl(&hex::decode(hex)?)
120 }
121
122 pub(crate) fn from_bytes_impl(bytes: &[u8]) -> Result<Script, BSVErrors> {
123 let mut cursor = Cursor::new(bytes);
124
125 let mut bit_accumulator = vec![];
126 while let Ok(byte) = cursor.read_u8() {
127 if (0x01..=0x4b).contains(&byte) {
128 let mut data = vec![0; byte as usize];
129 if let Err(e) = cursor.read(&mut data) {
130 return Err(BSVErrors::DeserialiseScript(format!("Failed to read OP_PUSH data {}", e)));
131 }
132
133 bit_accumulator.push(ScriptBit::Push(data));
134 continue;
135 }
136
137 let bit = match OpCodes::from_u8(byte) {
138 Some(v @ (OpCodes::OP_PUSHDATA1 | OpCodes::OP_PUSHDATA2 | OpCodes::OP_PUSHDATA4)) => {
139 let data_length = match v {
140 OpCodes::OP_PUSHDATA1 => cursor.read_u8()? as usize,
141 OpCodes::OP_PUSHDATA2 => cursor.read_u16::<LittleEndian>()? as usize,
142 _ => cursor.read_u32::<LittleEndian>()? as usize,
143 };
144
145 let mut data = vec![0; data_length];
146 if let Err(e) = cursor.read(&mut data) {
147 return Err(BSVErrors::DeserialiseScript(format!("Failed to read OP_PUSHDATA data {}", e)));
148 }
149
150 ScriptBit::PushData(v, data)
151 }
152 Some(v) => ScriptBit::OpCode(v),
153 None => return Err(BSVErrors::DeserialiseScript(format!("Unknown opcode {}", byte))),
154 };
155
156 bit_accumulator.push(bit);
157 }
158
159 let nested_bits = Script::if_statement_pass(&bit_accumulator)?;
160
161 Ok(Script(nested_bits))
162 }
163
164 pub(crate) fn from_coinbase_bytes_impl(bytes: &[u8]) -> Result<Script, BSVErrors> {
165 Ok(Script(vec![ScriptBit::Coinbase(bytes.to_vec())]))
166 }
167
168 fn map_string_to_script_bit(code: &str) -> Result<ScriptBit, BSVErrors> {
169 let code = code.trim();
170 match code {
172 "0" => return Ok(ScriptBit::OpCode(OpCodes::OP_0)),
173 "1" => return Ok(ScriptBit::OpCode(OpCodes::OP_1)),
174 "2" => return Ok(ScriptBit::OpCode(OpCodes::OP_2)),
175 "3" => return Ok(ScriptBit::OpCode(OpCodes::OP_3)),
176 "4" => return Ok(ScriptBit::OpCode(OpCodes::OP_4)),
177 "5" => return Ok(ScriptBit::OpCode(OpCodes::OP_5)),
178 "6" => return Ok(ScriptBit::OpCode(OpCodes::OP_6)),
179 "7" => return Ok(ScriptBit::OpCode(OpCodes::OP_7)),
180 "8" => return Ok(ScriptBit::OpCode(OpCodes::OP_8)),
181 "9" => return Ok(ScriptBit::OpCode(OpCodes::OP_9)),
182 "10" => return Ok(ScriptBit::OpCode(OpCodes::OP_10)),
183 "11" => return Ok(ScriptBit::OpCode(OpCodes::OP_11)),
184 "12" => return Ok(ScriptBit::OpCode(OpCodes::OP_12)),
185 "13" => return Ok(ScriptBit::OpCode(OpCodes::OP_13)),
186 "14" => return Ok(ScriptBit::OpCode(OpCodes::OP_14)),
187 "15" => return Ok(ScriptBit::OpCode(OpCodes::OP_15)),
188 "16" => return Ok(ScriptBit::OpCode(OpCodes::OP_16)),
189 _ => (),
190 }
191
192 if let Ok(opcode) = OpCodes::from_str(code) {
194 return Ok(ScriptBit::OpCode(opcode));
195 }
196
197 let data_bytes = hex::decode(code)?;
199 let bit = match VarInt::get_pushdata_opcode(data_bytes.len() as u64) {
200 Some(v) => ScriptBit::PushData(v, data_bytes),
201 None => ScriptBit::Push(data_bytes),
202 };
203 Ok(bit)
204 }
205
206 fn read_pass(bits_iter: &mut Iter<ScriptBit>) -> Result<Vec<ScriptBit>, BSVErrors> {
207 let mut nested_bits = vec![];
208 while let Some(thing) = bits_iter.next() {
209 match thing {
210 ScriptBit::OpCode(v @ (OpCodes::OP_IF | OpCodes::OP_NOTIF | OpCodes::OP_VERIF | OpCodes::OP_VERNOTIF)) => nested_bits.push(ScriptBit::If {
211 code: *v,
212 pass: Script::read_pass(bits_iter)?,
214 fail: Script::read_fail(bits_iter)?,
216 }),
217 ScriptBit::OpCode(OpCodes::OP_ELSE) => return Ok(nested_bits),
218 o => nested_bits.push(o.clone()),
219 }
220 }
221
222 Err(BSVErrors::DeserialiseScript("OP_IF statement requires an OP_ELSE code".into()))
223 }
224
225 fn read_fail(bits_iter: &mut Iter<ScriptBit>) -> Result<Vec<ScriptBit>, BSVErrors> {
226 let mut nested_bits = vec![];
227 while let Some(thing) = bits_iter.next() {
228 match thing {
229 ScriptBit::OpCode(v @ (OpCodes::OP_IF | OpCodes::OP_NOTIF | OpCodes::OP_VERIF | OpCodes::OP_VERNOTIF)) => nested_bits.push(ScriptBit::If {
230 code: *v,
231 pass: Script::read_pass(bits_iter)?,
233 fail: Script::read_fail(bits_iter)?,
235 }),
236 ScriptBit::OpCode(OpCodes::OP_ENDIF) => return Ok(nested_bits),
237 o => nested_bits.push(o.clone()),
238 }
239 }
240
241 Err(BSVErrors::DeserialiseScript("OP_IF statement requires an OP_ENDIF code".into()))
242 }
243
244 fn if_statement_pass(bits: &[ScriptBit]) -> Result<Vec<ScriptBit>, BSVErrors> {
247 let mut nested_bits = vec![];
250 let mut bits_iter = bits.iter();
251 while let Some(thing) = bits_iter.next() {
252 match thing {
253 ScriptBit::OpCode(v @ (OpCodes::OP_IF | OpCodes::OP_NOTIF | OpCodes::OP_VERIF | OpCodes::OP_VERNOTIF)) => nested_bits.push(ScriptBit::If {
254 code: *v,
255 pass: Script::read_pass(&mut bits_iter)?,
257 fail: Script::read_fail(&mut bits_iter)?,
259 }),
260 o => nested_bits.push(o.clone()),
261 }
262 }
263
264 Ok(nested_bits)
265 }
266
267 pub(crate) fn from_asm_string_impl(asm: &str) -> Result<Script, BSVErrors> {
268 let bits: Result<Vec<ScriptBit>, _> = asm.split(' ').filter(|x| !(x.is_empty() || x == &"\n" || x == &"\r")).map(Script::map_string_to_script_bit).collect();
269 let bits = Script::if_statement_pass(&bits?)?;
270
271 Ok(Script(bits))
272 }
273
274 pub(crate) fn get_pushdata_prefix_bytes_impl(length: usize) -> Result<Vec<u8>, BSVErrors> {
275 match length {
276 op_push @ 0x01..=0x4b => Ok(vec![op_push as u8]),
277 op_pushdata1_size @ 0x4c..=0xFF => {
278 let op_pushdata1_byte = OpCodes::OP_PUSHDATA1
279 .to_u8()
280 .ok_or_else(|| BSVErrors::DeserialiseScript("Unable to deserialise OP_PUSHDATA1 Code to u8".into()))?;
281
282 Ok(vec![op_pushdata1_byte, op_pushdata1_size as u8])
283 }
284 op_pushdata2_size @ 0x100..=0xFFFF => {
285 let op_pushdata2_byte = OpCodes::OP_PUSHDATA2
286 .to_u8()
287 .ok_or_else(|| BSVErrors::DeserialiseScript("Unable to deserialise OP_PUSHDATA2 Code to u8".into()))?;
288
289 let mut push_data_prefix = vec![op_pushdata2_byte];
290 push_data_prefix.write_u16::<LittleEndian>(op_pushdata2_size as u16)?;
291
292 Ok(push_data_prefix)
293 }
294 op_pushdata4_size if op_pushdata4_size > 0x10000 && op_pushdata4_size <= 0xFFFFFFFF => {
295 let op_pushdata4_byte = OpCodes::OP_PUSHDATA4
296 .to_u8()
297 .ok_or_else(|| BSVErrors::DeserialiseScript("Unable to deserialise OP_PUSHDATA4 Code to u8".into()))?;
298
299 let mut push_data_prefix = vec![op_pushdata4_byte];
300 push_data_prefix.write_u32::<LittleEndian>(op_pushdata4_size as u32)?;
301
302 Ok(push_data_prefix)
303 }
304 size => return Err(BSVErrors::DeserialiseScript(format!("{} is too large for OP_PUSHDATAX commands", size))),
305 }
306 }
307
308 pub(crate) fn encode_pushdata_impl(data_bytes: &[u8]) -> Result<Vec<u8>, BSVErrors> {
309 let mut pushdata_bytes = Script::get_pushdata_prefix_bytes_impl(data_bytes.len())?;
310 pushdata_bytes.append(&mut data_bytes.to_vec());
311
312 Ok(pushdata_bytes)
313 }
314}
315
316#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-script"), wasm_bindgen)]
320impl Script {
321 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-script"), wasm_bindgen(js_name = toBytes))]
322 pub fn to_bytes(&self) -> Vec<u8> {
323 Script::script_bits_to_bytes(&self.0)
324 }
325
326 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-script"), wasm_bindgen(js_name = getScriptLength))]
327 pub fn get_script_length(&self) -> usize {
328 self.to_bytes().len()
329 }
330
331 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-script"), wasm_bindgen(js_name = toHex))]
332 pub fn to_hex(&self) -> String {
333 hex::encode(self.to_bytes())
334 }
335
336 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-script"), wasm_bindgen(js_name = removeCodeSeparators))]
337 pub fn remove_codeseparators(&mut self) {
338 self.0 = self.0.clone().into_iter().filter(|x| *x != ScriptBit::OpCode(OpCodes::OP_CODESEPARATOR)).collect();
339 }
340}
341
342impl Script {
346 pub fn from_chunks(chunks: Vec<Vec<u8>>) -> Result<Script, BSVErrors> {
350 Script::from_bytes_impl(&chunks.into_iter().flatten().collect::<Vec<u8>>())
351 }
352}
353
354#[cfg(not(all(target_arch = "wasm32", feature = "wasm-bindgen-script")))]
358impl Script {
359 pub fn to_asm_string(&self) -> String {
360 Script::to_asm_string_impl(self, false)
361 }
362
363 pub fn from_bytes(bytes: &[u8]) -> Result<Script, BSVErrors> {
364 Script::from_bytes_impl(bytes)
365 }
366
367 pub fn to_extended_asm_string(&self) -> String {
368 Script::to_asm_string_impl(self, true)
369 }
370
371 pub fn from_hex(hex: &str) -> Result<Script, BSVErrors> {
372 Script::from_hex_impl(hex)
373 }
374
375 pub fn from_asm_string(asm_string: &str) -> Result<Script, BSVErrors> {
376 Script::from_asm_string_impl(asm_string)
377 }
378
379 pub fn encode_pushdata(data_bytes: &[u8]) -> Result<Vec<u8>, BSVErrors> {
380 Script::encode_pushdata_impl(data_bytes)
381 }
382
383 pub fn get_pushdata_bytes(length: usize) -> Result<Vec<u8>, BSVErrors> {
387 Script::get_pushdata_prefix_bytes_impl(length)
388 }
389
390 pub fn to_script_bits(&self) -> Vec<ScriptBit> {
391 self.0.clone()
392 }
393}
394
395#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen-script"))]
399#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-script"), wasm_bindgen)]
400impl Script {
401 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toASMString))]
402 pub fn to_asm_string(&self) -> String {
403 Script::to_asm_string_impl(&self, false)
404 }
405
406 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toExtendedASMString))]
407 pub fn to_extended_asm_string(&self) -> String {
408 Script::to_asm_string_impl(&self, true)
409 }
410
411 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromHex))]
412 pub fn from_hex(hex: &str) -> Result<Script, JsValue> {
413 match Script::from_hex_impl(hex) {
414 Ok(v) => Ok(v),
415 Err(e) => Err(JsValue::from_str(&e.to_string())),
416 }
417 }
418
419 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromBytes))]
420 pub fn from_bytes(bytes: &[u8]) -> Result<Script, JsValue> {
421 match Script::from_bytes_impl(bytes) {
422 Ok(v) => Ok(v),
423 Err(e) => Err(JsValue::from_str(&e.to_string())),
424 }
425 }
426
427 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = fromASMString))]
428 pub fn from_asm_string(asm_string: &str) -> Result<Script, JsValue> {
429 match Script::from_asm_string_impl(asm_string) {
430 Ok(v) => Ok(v),
431 Err(e) => Err(JsValue::from_str(&e.to_string())),
432 }
433 }
434
435 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = encodePushData))]
436 pub fn encode_pushdata(data_bytes: &[u8]) -> Result<Vec<u8>, JsValue> {
437 match Script::encode_pushdata_impl(data_bytes) {
438 Ok(v) => Ok(v),
439 Err(e) => Err(JsValue::from_str(&e.to_string())),
440 }
441 }
442
443 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = getPushDataBytes))]
447 pub fn get_pushdata_bytes(length: usize) -> Result<Vec<u8>, JsValue> {
448 match Script::get_pushdata_prefix_bytes_impl(length) {
449 Ok(v) => Ok(v),
450 Err(e) => Err(JsValue::from_str(&e.to_string())),
451 }
452 }
453
454 #[cfg_attr(target_arch = "wasm32", wasm_bindgen(js_name = toScriptBits))]
455 pub fn to_script_bits(&self) -> Result<JsValue, JsValue> {
456 match JsValue::from_serde(self) {
457 Ok(v) => Ok(v),
458 Err(e) => Err(JsValue::from_str(&e.to_string())),
459 }
460 }
461}