bsv_wasm/script/
mod.rs

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
33/**
34 * Serialise Methods
35 */
36impl 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
114/**
115 * Deserialise Methods
116 */
117impl 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        // Number OP_CODES
171        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        // Standard OP_CODES
193        if let Ok(opcode) = OpCodes::from_str(code) {
194            return Ok(ScriptBit::OpCode(opcode));
195        }
196
197        // PUSHDATA OP_CODES
198        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                    // Read until OP_ELSE
213                    pass: Script::read_pass(bits_iter)?,
214                    // Read until OP_ENDIF
215                    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                    // Read until OP_ELSE
232                    pass: Script::read_pass(bits_iter)?,
233                    // Read until OP_ENDIF
234                    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    /// Iterates over a ScriptBit array, finds OP_XIF codes and calculates the nested ScriptBit::If block  
245    /// TODO: name this function better
246    fn if_statement_pass(bits: &[ScriptBit]) -> Result<Vec<ScriptBit>, BSVErrors> {
247        // let mut cursor = Cursor::new(bits);
248
249        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                    // Read until OP_ELSE
256                    pass: Script::read_pass(&mut bits_iter)?,
257                    // Read until OP_ENDIF
258                    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/**
317 * Shared Functions
318 */
319#[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
342/**
343 * Only export to inside Rust calling code
344 */
345impl Script {
346    /**
347     * Rust only: wasm-bindgen doesnt handle 2D arrays of u8.
348     */
349    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/**
355 * Native Specific Functions
356 */
357#[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    /**
384     * Gets the OP_PUSHDATA prefix varint
385     */
386    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/**
396 * WASM Specific Functions
397 */
398#[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    /**
444     * Gets the OP_PUSHDATA prefix varint
445     */
446    #[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}