├── .editorconfig ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── build-wasm.sh └── src ├── asm.rs ├── data_structures.rs ├── error.rs ├── json.rs ├── lib.rs ├── macros.rs ├── main.rs ├── parse.rs ├── signatures.rs ├── utils.rs └── wasm.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # see https://editorconfig.org for more options, and setup instructions for yours editor 2 | 3 | [*.rs] 4 | indent_style = tab 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitcoin-scriptexec" 3 | version = "0.0.0" 4 | edition = "2021" 5 | description = "Bitcoin Script interpreter/executor" 6 | authors = ["Steven Roose "] 7 | license = "CC0-1.0" 8 | 9 | [lib] 10 | crate-type = ["cdylib", "rlib"] 11 | 12 | [[bin]] 13 | name = "btcexec" 14 | path = "src/main.rs" 15 | required-features = ["cli"] 16 | 17 | [features] 18 | default = ["cli", "wasm"] 19 | json = ["serde", "serde_json", "bitcoin/serde"] 20 | cli = ["json", "clap"] 21 | wasm = [ 22 | "json", 23 | "wasm-bindgen", 24 | "serde-wasm-bindgen", 25 | "console_error_panic_hook", 26 | "getrandom/js", 27 | ] 28 | 29 | [dependencies] 30 | bitcoin = "0.32.5" 31 | lazy_static = "1.4.0" 32 | 33 | # cli 34 | clap = { version = "4.5.23", features = ["derive"], optional = true } 35 | 36 | # wasm 37 | serde = { version = "1.0.216", features = ["derive"], optional = true } 38 | serde_json = { version = "1.0.133", optional = true } 39 | wasm-bindgen = { version = "0.2.99", optional = true } 40 | serde-wasm-bindgen = { version = "0.6.5", optional = true } 41 | console_error_panic_hook = { version = "0.1.7", optional = true } 42 | # I think we need to mention this for secp256k1-sys to work 43 | getrandom = { version = "0.2.15", optional = true } 44 | 45 | [dev-dependencies] 46 | bincode = { version = "1.3.3" } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | bitcoin-scriptexec 3 | ================== 4 | 5 | A work-in-progress Bitcoin Script execution utility. 6 | 7 | **DISCLAIMER: DO NOT EVER, EVER, TRY TO USE THIS CRATE FOR CONSENSUS PURPOSES !!** 8 | 9 | 10 | # Status 11 | 12 | This project is a work-in-progress mostly attempting to facilitate BitVM development. 13 | It does not yet fully implement all opcodes, but as a library already gives you pretty 14 | good insight into the internals of the execution in a step-wise manner. 15 | 16 | 17 | # Usage 18 | 19 | ## CLI 20 | 21 | You can simply use `cargo run` or build/intall the binary as follows: 22 | 23 | ``` 24 | # to build in debug mode 25 | $ cargo build --locked 26 | # to build in release (optimized) mode 27 | $ cargo build --locked --release 28 | # to install in ~/.cargo/bin 29 | $ cargo install --locked --path . 30 | ``` 31 | 32 | ### Usage 33 | 34 | The CLI currently takes only a single argument: the path to the ASM script file: 35 | 36 | ``` 37 | # using the binary 38 | $ btcexec 39 | # using cargo run 40 | $ cargo run -- 41 | ``` 42 | 43 | ## WASM 44 | 45 | There are wasm bindings provided. For API documentation, see the `src/wasm.rs`a file. 46 | 47 | To build the WASM bindings, [install wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) 48 | and then run the following script: 49 | 50 | ``` 51 | ./build-wasm.sh 52 | ``` 53 | -------------------------------------------------------------------------------- /build-wasm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | wasm-pack build --target web -- --features wasm 4 | -------------------------------------------------------------------------------- /src/asm.rs: -------------------------------------------------------------------------------- 1 | //! Implements the ASM parser for [`ScriptBuf`]. 2 | 3 | use std::str::FromStr; 4 | 5 | use bitcoin::{ 6 | hex, 7 | opcodes::{self, all::*}, 8 | script::{Builder, PushBytes}, 9 | Opcode, ScriptBuf, 10 | }; 11 | 12 | use crate::parse_opcode; 13 | 14 | /// Trait that something can be parsed from ASM. 15 | pub trait FromAsm: Sized { 16 | /// Parses `Self` from ASM. 17 | fn from_asm(asm: &str) -> Result; 18 | } 19 | 20 | impl FromAsm for ScriptBuf { 21 | fn from_asm(asm: &str) -> Result { 22 | let mut buf = Vec::with_capacity(65); 23 | let mut builder = Builder::new(); 24 | let mut words = iter_words(asm); 25 | while let Some((pos, mut word)) = words.next() { 26 | // We have this special case in our formatter. 27 | if word == "OP_0" { 28 | builder = builder.push_opcode(OP_PUSHBYTES_0); 29 | continue; 30 | } 31 | if let Ok(op) = parse_opcode(word) { 32 | // check for push opcodes 33 | if op.to_u8() <= OP_PUSHDATA4.to_u8() { 34 | let (next, push) = words 35 | .next() 36 | .ok_or(err(pos, FromAsmErrorKind::UnexpectedEOF))?; 37 | if !try_parse_raw_hex(push, &mut buf) { 38 | return Err(err(next, FromAsmErrorKind::InvalidHex)); 39 | } 40 | // NB our API doesn't actually allow us to make byte pushes with 41 | // non-minimal length prefix, so we can only check and error if 42 | // the user wants a non-minimal push 43 | let expected_push_op = match buf.len() { 44 | n if n < opcodes::all::OP_PUSHDATA1.to_u8() as usize => { 45 | Opcode::from(n as u8) 46 | } 47 | n if n < 0x100 => opcodes::all::OP_PUSHDATA1, 48 | n if n < 0x10000 => opcodes::all::OP_PUSHDATA2, 49 | n if n < 0x100000000 => opcodes::all::OP_PUSHDATA4, 50 | _ => return Err(err(next, FromAsmErrorKind::PushExceedsMaxSize)), 51 | }; 52 | if op != expected_push_op { 53 | return Err(err(pos, FromAsmErrorKind::NonMinimalBytePush)); 54 | } 55 | let push = <&PushBytes>::try_from(&buf[..]) 56 | .map_err(|_| err(next, FromAsmErrorKind::PushExceedsMaxSize))?; 57 | builder = builder.push_slice(push); 58 | } else { 59 | builder = builder.push_opcode(op); 60 | } 61 | continue; 62 | } 63 | // Not an opcode, try to interpret as number or push. 64 | if word.starts_with('<') && word.ends_with('>') { 65 | word = &word[1..word.len() - 1]; 66 | } 67 | // Try a number. 68 | if let Ok(i) = i64::from_str(word) { 69 | builder = builder.push_int(i); 70 | continue; 71 | } 72 | // Finally, try hex in various forms. 73 | if word.starts_with("0x") { 74 | word = &word[2..]; 75 | } 76 | if try_parse_raw_hex(word, &mut buf) { 77 | let push = <&PushBytes>::try_from(&buf[..]) 78 | .map_err(|_| err(pos, FromAsmErrorKind::PushExceedsMaxSize))?; 79 | builder = builder.push_slice(push); 80 | } else { 81 | return Err(err(pos, FromAsmErrorKind::UnknownInstruction)); 82 | } 83 | } 84 | Ok(builder.into_script()) 85 | } 86 | } 87 | 88 | /// Try to parse raw hex bytes and push them into the buffer. 89 | fn try_parse_raw_hex(hex: &str, buf: &mut Vec) -> bool { 90 | buf.clear(); 91 | let iter = match hex::HexToBytesIter::new(hex) { 92 | Ok(i) => i, 93 | Err(_) => return false, 94 | }; 95 | for item in iter { 96 | let item = match item { 97 | Ok(i) => i, 98 | Err(_) => return false, 99 | }; 100 | buf.push(item); 101 | } 102 | true 103 | } 104 | /// Create an iterator over instruction words and their position in the file. 105 | fn iter_words(asm: &str) -> impl Iterator { 106 | asm.lines().enumerate().flat_map(|(line_idx, line)| { 107 | let content = line.split("#") 108 | .next() 109 | .unwrap().split("//") 110 | .next() 111 | .unwrap(); 112 | content 113 | .split_whitespace() 114 | .enumerate() 115 | .map(move |(word_idx, word)| ((line_idx, word_idx), word)) 116 | }) 117 | } 118 | 119 | fn err(position: (usize, usize), kind: FromAsmErrorKind) -> FromAsmError { 120 | FromAsmError { position, kind } 121 | } 122 | 123 | /// The different kinds of [`FromAsmError`] that can occur. 124 | #[derive(Debug, Clone, PartialEq, Eq)] 125 | #[non_exhaustive] 126 | pub enum FromAsmErrorKind { 127 | /// ASM ended unexpectedly. 128 | UnexpectedEOF, 129 | /// We were not able to interpret the instruction. 130 | UnknownInstruction, 131 | /// Invalid hexadecimal bytes. 132 | InvalidHex, 133 | /// Byte push exceeding the maximum size. 134 | PushExceedsMaxSize, 135 | /// ASM contains a byte push with non-minimal size prefix. 136 | /// 137 | /// This is not necessarily invalid, but we can't construct such pushes. 138 | NonMinimalBytePush, 139 | } 140 | /// Error from parsing Script ASM. 141 | #[derive(Debug, Clone, PartialEq, Eq)] 142 | pub struct FromAsmError { 143 | /// The position of the instruction that caused the error. 144 | /// 145 | /// The value is (line, word) with word incremented after 146 | /// every chunk of whitespace. 147 | pub position: (usize, usize), 148 | /// The kind of error that occurred. 149 | pub kind: FromAsmErrorKind, 150 | } 151 | -------------------------------------------------------------------------------- /src/data_structures.rs: -------------------------------------------------------------------------------- 1 | use crate::{read_scriptint, ExecError}; 2 | use alloc::rc::Rc; 3 | use bitcoin::script::write_scriptint; 4 | use core::cell::RefCell; 5 | use core::cmp::PartialEq; 6 | use core::slice::Iter; 7 | use serde::de::{self, Unexpected, Visitor}; 8 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 9 | use std::fmt; 10 | use std::iter::Map; 11 | 12 | #[derive(Clone, Debug, Eq, PartialEq)] 13 | pub enum StackEntry { 14 | Num(i64), 15 | StrRef(Rc>>), 16 | } 17 | 18 | /// Returns minimally encoded scriptint as a byte vector. 19 | pub fn scriptint_vec(n: i64) -> Vec { 20 | let mut buf = [0u8; 8]; 21 | let len = write_scriptint(&mut buf, n); 22 | buf[0..len].to_vec() 23 | } 24 | 25 | impl StackEntry { 26 | #[deprecated(note = "Use `as_bytes` to avoid the borrow")] 27 | // This assumes the StackEntry fit in a u32 and will pad it with leading zeros to 4 bytes. 28 | pub fn serialize_to_bytes(self) -> Vec { 29 | self.as_bytes() 30 | } 31 | 32 | // This assumes the StackEntry fit in a u32 and will pad it with leading zeros to 4 bytes. 33 | pub fn as_bytes(&self) -> Vec { 34 | match self { 35 | StackEntry::Num(v) => scriptint_vec(*v), 36 | StackEntry::StrRef(v) => { 37 | let v = v.borrow().to_vec(); 38 | assert!( 39 | v.len() <= 4, 40 | "There should not be entries with more than 32 bits on the Stack at this point" 41 | ); 42 | v 43 | } 44 | } 45 | } 46 | } 47 | 48 | impl Serialize for StackEntry { 49 | fn serialize(&self, serializer: S) -> Result { 50 | serializer.serialize_bytes(&self.as_bytes()) 51 | } 52 | } 53 | 54 | impl<'de> Deserialize<'de> for StackEntry { 55 | fn deserialize(deserializer: D) -> Result 56 | where 57 | D: Deserializer<'de>, 58 | { 59 | deserializer.deserialize_bytes(StackEntryVisitor) 60 | } 61 | } 62 | 63 | struct StackEntryVisitor; 64 | 65 | impl<'de> Visitor<'de> for StackEntryVisitor { 66 | type Value = StackEntry; 67 | 68 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 69 | formatter.write_str("a byte array representing a StackEntry") 70 | } 71 | 72 | fn visit_bytes(self, v: &[u8]) -> Result 73 | where 74 | E: de::Error, 75 | { 76 | if v.len() <= 4 { 77 | Ok(StackEntry::StrRef(Rc::new(RefCell::new(v.to_vec())))) 78 | } else { 79 | Err(de::Error::invalid_value(Unexpected::Bytes(v), &self)) 80 | } 81 | } 82 | 83 | fn visit_seq(self, mut seq: A) -> Result 84 | where 85 | A: de::SeqAccess<'de>, 86 | { 87 | let mut vec = Vec::with_capacity(4); 88 | while let Some(value) = seq.next_element()? { 89 | vec.push(value); 90 | } 91 | 92 | if vec.len() <= 4 { 93 | Ok(StackEntry::StrRef(Rc::new(RefCell::new(vec)))) 94 | } else { 95 | Err(de::Error::invalid_length(vec.len(), &self)) 96 | } 97 | } 98 | } 99 | 100 | #[derive(Clone, Debug, Serialize, Deserialize)] 101 | pub struct Stack(Vec); 102 | 103 | impl Stack { 104 | pub fn new() -> Self { 105 | Self(Vec::with_capacity(1000)) 106 | } 107 | 108 | pub fn is_empty(&self) -> bool { 109 | self.0.is_empty() 110 | } 111 | 112 | pub fn last(&self) -> Result, ExecError> { 113 | self.topstr(-1) 114 | } 115 | 116 | pub fn top(&self, offset: isize) -> Result<&StackEntry, ExecError> { 117 | debug_assert!(offset < 0, "offsets should be < 0"); 118 | self.0 119 | .len() 120 | .checked_sub(offset.unsigned_abs()).map(|i| &self.0[i]) 121 | .ok_or(ExecError::InvalidStackOperation) 122 | } 123 | 124 | pub fn topstr(&self, offset: isize) -> Result, ExecError> { 125 | let entry = self.top(offset)?; 126 | match entry { 127 | StackEntry::Num(v) => Ok(scriptint_vec(*v)), 128 | StackEntry::StrRef(v) => Ok(v.borrow().to_vec()), 129 | } 130 | } 131 | 132 | pub fn topnum(&self, offset: isize, require_minimal: bool) -> Result { 133 | let entry = self.top(offset)?; 134 | match entry { 135 | StackEntry::Num(v) => { 136 | if *v <= i32::MAX as i64 { 137 | Ok(*v) 138 | } else { 139 | Err(ExecError::ScriptIntNumericOverflow) 140 | } 141 | } 142 | StackEntry::StrRef(v) => Ok(read_scriptint(v.borrow().as_slice(), 4, require_minimal)?), 143 | } 144 | } 145 | 146 | pub fn pushnum(&mut self, num: i64) { 147 | self.0.push(StackEntry::Num(num)); 148 | } 149 | 150 | pub fn pushstr(&mut self, v: &[u8]) { 151 | self.0 152 | .push(StackEntry::StrRef(Rc::new(RefCell::new(v.to_vec())))); 153 | } 154 | 155 | pub fn push(&mut self, v: StackEntry) { 156 | self.0.push(v); 157 | } 158 | 159 | pub fn needn(&self, min_nb_items: usize) -> Result<(), ExecError> { 160 | if self.len() < min_nb_items { 161 | Err(ExecError::InvalidStackOperation) 162 | } else { 163 | Ok(()) 164 | } 165 | } 166 | 167 | pub fn popn(&mut self, n: usize) -> Result<(), ExecError> { 168 | for _ in 0..n { 169 | self.0.pop().ok_or(ExecError::InvalidStackOperation)?; 170 | } 171 | Ok(()) 172 | } 173 | 174 | pub fn pop(&mut self) -> Option { 175 | self.0.pop() 176 | } 177 | 178 | pub fn popstr(&mut self) -> Result, ExecError> { 179 | let entry = self.0.pop().ok_or(ExecError::InvalidStackOperation)?; 180 | match entry { 181 | StackEntry::Num(v) => Ok(scriptint_vec(v)), 182 | StackEntry::StrRef(v) => Ok(v.borrow().to_vec()), 183 | } 184 | } 185 | 186 | pub fn popnum(&mut self, require_minimal: bool) -> Result { 187 | let entry = self.0.pop().ok_or(ExecError::InvalidStackOperation)?; 188 | match entry { 189 | StackEntry::Num(v) => { 190 | if v <= i32::MAX as i64 { 191 | Ok(v) 192 | } else { 193 | Err(ExecError::ScriptIntNumericOverflow) 194 | } 195 | } 196 | StackEntry::StrRef(v) => Ok(read_scriptint(v.borrow().as_slice(), 4, require_minimal)?), 197 | } 198 | } 199 | 200 | pub fn len(&self) -> usize { 201 | self.0.len() 202 | } 203 | 204 | pub fn remove(&mut self, v: usize) { 205 | self.0.remove(v); 206 | } 207 | 208 | pub fn iter_str(&self) -> Map, fn(&StackEntry) -> Vec> { 209 | self.0.iter().map(|v| match v { 210 | StackEntry::Num(v) => scriptint_vec(*v), 211 | StackEntry::StrRef(v) => v.borrow().to_vec(), 212 | }) 213 | } 214 | 215 | pub fn get(&self, index: usize) -> Vec { 216 | match &self.0[index] { 217 | StackEntry::Num(v) => scriptint_vec(*v), 218 | StackEntry::StrRef(v) => v.borrow().to_vec(), 219 | } 220 | } 221 | 222 | #[deprecated(note = "Use `as_v8_vec` to be symmetry")] 223 | // Will serialize the stack into a series of bytes such that every 4 bytes correspond to a u32 224 | // (or smaller) stack entry (smaller entries are padded with 0). 225 | pub fn serialize_to_bytes(self) -> Vec { 226 | let mut bytes = vec![]; 227 | for entry in self.0 { 228 | bytes.extend(entry.as_bytes()); 229 | } 230 | bytes 231 | } 232 | 233 | pub fn from_u8_vec(v: Vec>) -> Self { 234 | let mut res = Self::new(); 235 | for entry in v { 236 | res.0.push(StackEntry::StrRef(Rc::new(RefCell::new(entry)))); 237 | } 238 | res 239 | } 240 | 241 | pub fn as_v8_vec(&self) -> Vec> { 242 | self.0.iter().map(|entry| entry.as_bytes()).collect() 243 | } 244 | } 245 | 246 | impl Default for Stack { 247 | fn default() -> Self { 248 | Self::new() 249 | } 250 | } 251 | 252 | impl PartialEq for Stack { 253 | fn eq(&self, other: &Self) -> bool { 254 | self.0.len() == other.0.len() 255 | && self 256 | .0 257 | .iter() 258 | .zip(other.0.iter()) 259 | .all(|(a, b)| a.as_bytes() == b.as_bytes()) 260 | } 261 | } 262 | 263 | impl Eq for Stack {} 264 | 265 | #[test] 266 | fn test_stack_serialize_json() { 267 | let mut stack_0 = Stack::new(); 268 | stack_0.pushnum(42); 269 | stack_0.pushstr(&[1, 2]); 270 | 271 | // Serialize it 272 | let serialized_stack = serde_json::to_string(&stack_0).expect("Failed to serialize Stack"); 273 | 274 | // Deserialize it 275 | let mut stack_1: Stack = 276 | serde_json::from_str(&serialized_stack).expect("Failed to deserialize Stack"); 277 | 278 | assert_eq!(stack_0, stack_1); 279 | assert_eq!(stack_0.popnum(true), stack_1.popnum(true)); 280 | assert_eq!(stack_0.popstr(), stack_1.popstr()); 281 | } 282 | 283 | #[test] 284 | fn test_stack_serialize_bincode() { 285 | let mut stack_0 = Stack::new(); 286 | stack_0.pushnum(42); 287 | stack_0.pushstr(&[1, 2]); 288 | 289 | // Serialize it 290 | let serialized_stack = bincode::serialize(&stack_0).expect("Failed to serialize Stack"); 291 | 292 | // Deserialize it 293 | let mut stack_1: Stack = 294 | bincode::deserialize(&serialized_stack).expect("Failed to deserialize Stack"); 295 | 296 | assert_eq!(stack_0, stack_1); 297 | assert_eq!(stack_0.popnum(true), stack_1.popnum(true)); 298 | assert_eq!(stack_0.popstr(), stack_1.popstr()); 299 | } 300 | 301 | #[test] 302 | fn test_stack_entry_serialize_json() { 303 | let entry = StackEntry::Num(42); 304 | let serialized = serde_json::to_string(&entry).unwrap(); 305 | let deserialized: StackEntry = serde_json::from_str(&serialized).unwrap(); 306 | assert_eq!(entry.as_bytes(), deserialized.as_bytes()); 307 | 308 | let entry = StackEntry::StrRef(Rc::new(RefCell::new(vec![1, 2, 3]))); 309 | let serialized = serde_json::to_string(&entry).unwrap(); 310 | let deserialized: StackEntry = serde_json::from_str(&serialized).unwrap(); 311 | assert_eq!(entry, deserialized); 312 | } 313 | 314 | #[test] 315 | fn test_stack_entry_serialize_bincode() { 316 | let entry = StackEntry::Num(42); 317 | let serialized = bincode::serialize(&entry).unwrap(); 318 | let deserialized: StackEntry = bincode::deserialize(&serialized).unwrap(); 319 | assert_eq!(entry.as_bytes(), deserialized.as_bytes()); 320 | 321 | let entry = StackEntry::StrRef(Rc::new(RefCell::new(vec![1, 2, 3]))); 322 | let serialized = bincode::serialize(&entry).unwrap(); 323 | let deserialized: StackEntry = bincode::deserialize(&serialized).unwrap(); 324 | assert_eq!(entry, deserialized); 325 | } 326 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::blockdata::script; 2 | 3 | /// Error of a script execution. 4 | /// 5 | /// Equivalent to Bitcoin Core's `ScriptError_t`. 6 | #[derive(Debug, Clone, PartialEq, Eq)] 7 | pub enum ExecError { 8 | DisabledOpcode, 9 | OpCodeseparator, 10 | BadOpcode, 11 | OpCount, 12 | PushSize, 13 | MinimalData, 14 | InvalidStackOperation, 15 | NegativeLocktime, 16 | UnsatisfiedLocktime, 17 | UnbalancedConditional, 18 | TapscriptMinimalIf, 19 | Verify, 20 | OpReturn, 21 | EqualVerify, 22 | NumEqualVerify, 23 | CheckSigVerify, 24 | TapscriptValidationWeight, 25 | PubkeyType, 26 | SchnorrSigSize, 27 | SchnorrSigHashtype, 28 | SchnorrSig, 29 | TapscriptCheckMultiSig, 30 | PubkeyCount, 31 | StackSize, 32 | WitnessPubkeyType, 33 | 34 | // new ones for us 35 | ScriptIntNumericOverflow, 36 | Debug, 37 | } 38 | 39 | #[derive(Debug, Clone, PartialEq, Eq)] 40 | pub enum Error { 41 | Exec(ExecError), 42 | InvalidScript(script::Error), 43 | Other(&'static str), 44 | } 45 | -------------------------------------------------------------------------------- /src/json.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use bitcoin::hex::DisplayHex; 4 | use bitcoin::{Opcode, Script}; 5 | use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer}; 6 | 7 | use crate::ExecStats; 8 | 9 | /// Simple utility wrapper to serde-serialize using [fmt::Display]. 10 | struct FmtSer<'a, T: fmt::Display>(&'a T); 11 | impl Serialize for FmtSer<'_, T> { 12 | fn serialize(&self, s: S) -> Result { 13 | s.collect_str(&self.0) 14 | } 15 | } 16 | 17 | /// Wrapper to fmt::Display a Script as ASM. 18 | struct ScriptAsm<'a>(&'a Script); 19 | impl fmt::Display for ScriptAsm<'_> { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | self.0.fmt_asm(f) 22 | } 23 | } 24 | 25 | /// Wrapper to serialize a stack as hex elements. 26 | struct StackSer<'a>(&'a [Vec]); 27 | impl Serialize for StackSer<'_> { 28 | fn serialize(&self, s: S) -> Result { 29 | let mut seq = s.serialize_seq(Some(self.0.len()))?; 30 | for i in self.0.iter() { 31 | seq.serialize_element(&FmtSer(&i.as_hex()))?; 32 | } 33 | seq.end() 34 | } 35 | } 36 | 37 | pub struct RunStep<'a> { 38 | pub remaining_script: &'a Script, 39 | pub stack: &'a [Vec], 40 | pub altstack: &'a [Vec], 41 | pub stats: Option<&'a ExecStats>, 42 | } 43 | 44 | impl Serialize for RunStep<'_> { 45 | fn serialize(&self, s: S) -> Result { 46 | let mut m = s.serialize_map(None)?; 47 | m.serialize_entry( 48 | "remaining_script_hex", 49 | &FmtSer(&self.remaining_script.as_bytes().as_hex()), 50 | )?; 51 | m.serialize_entry( 52 | "remaining_script_asm", 53 | &FmtSer(&ScriptAsm(self.remaining_script)), 54 | )?; 55 | m.serialize_entry("stack", &StackSer(self.stack))?; 56 | m.serialize_entry("altstack", &StackSer(self.altstack))?; 57 | if let Some(ref stats) = self.stats { 58 | m.serialize_entry("stats", stats)?; 59 | } 60 | m.end() 61 | } 62 | } 63 | 64 | pub struct RunResult<'a> { 65 | pub success: bool, 66 | pub error: Option, 67 | pub opcode: Option, 68 | pub final_stack: &'a [Vec], 69 | pub stats: Option<&'a ExecStats>, 70 | } 71 | 72 | impl Serialize for RunResult<'_> { 73 | fn serialize(&self, s: S) -> Result { 74 | let mut m = s.serialize_map(None)?; 75 | m.serialize_entry("success", &self.success)?; 76 | if let Some(ref err) = self.error { 77 | m.serialize_entry("error", err)?; 78 | } 79 | if let Some(opcode) = self.opcode { 80 | m.serialize_entry("opcode", &FmtSer(&opcode))?; 81 | } 82 | m.serialize_entry("final_stack", &StackSer(self.final_stack))?; 83 | if let Some(ref stats) = self.stats { 84 | m.serialize_entry("stats", stats)?; 85 | } 86 | m.end() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | extern crate core; 3 | 4 | use alloc::borrow::Cow; 5 | use core::cmp; 6 | 7 | use bitcoin::consensus::Encodable; 8 | use bitcoin::hashes::{hash160, ripemd160, sha1, sha256, sha256d, Hash}; 9 | use bitcoin::opcodes::{all::*, Opcode}; 10 | use bitcoin::script::{self, Instruction, Instructions, Script, ScriptBuf}; 11 | use bitcoin::sighash::SighashCache; 12 | use bitcoin::taproot::{self, TapLeafHash}; 13 | use bitcoin::transaction::{self, Transaction, TxOut}; 14 | 15 | #[cfg(feature = "serde")] 16 | use serde; 17 | 18 | #[macro_use] 19 | mod macros; 20 | 21 | mod utils; 22 | use utils::ConditionStack; 23 | 24 | mod signatures; 25 | 26 | mod error; 27 | pub use error::{Error, ExecError}; 28 | 29 | pub mod asm; 30 | pub use asm::{FromAsm, FromAsmError, FromAsmErrorKind}; 31 | 32 | pub mod parse; 33 | pub use parse::parse_opcode; 34 | 35 | #[cfg(feature = "json")] 36 | pub mod json; 37 | #[cfg(feature = "wasm")] 38 | mod wasm; 39 | 40 | mod data_structures; 41 | pub use data_structures::Stack; 42 | 43 | /// Maximum number of non-push operations per script 44 | const MAX_OPS_PER_SCRIPT: usize = 201; 45 | 46 | /// Maximum number of bytes pushable to the stack 47 | const MAX_SCRIPT_ELEMENT_SIZE: usize = 520; 48 | 49 | /// Maximum number of values on script interpreter stack 50 | const MAX_STACK_SIZE: usize = 1000; 51 | 52 | /// The default maximum size of scriptints. 53 | const DEFAULT_MAX_SCRIPTINT_SIZE: usize = 4; 54 | 55 | /// If this flag is set, CTxIn::nSequence is NOT interpreted as a 56 | /// relative lock-time. 57 | /// It skips SequenceLocks() for any input that has it set (BIP 68). 58 | /// It fails OP_CHECKSEQUENCEVERIFY/CheckSequence() for any input that has 59 | /// it set (BIP 112). 60 | const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31; 61 | 62 | /// How much weight budget is added to the witness size (Tapscript only, see BIP 342). 63 | const VALIDATION_WEIGHT_OFFSET: i64 = 50; 64 | 65 | /// Validation weight per passing signature (Tapscript only, see BIP 342). 66 | const VALIDATION_WEIGHT_PER_SIGOP_PASSED: i64 = 50; 67 | 68 | // Maximum number of public keys per multisig 69 | const _MAX_PUBKEYS_PER_MULTISIG: i64 = 20; 70 | 71 | /// Used to enable experimental script features. 72 | #[derive(Debug, Clone, PartialEq, Eq)] 73 | pub struct Experimental { 74 | /// Enable an experimental implementation of OP_CAT. 75 | pub op_cat: bool, 76 | } 77 | 78 | /// Used to fine-tune different variables during execution. 79 | #[derive(Debug, Clone, PartialEq, Eq)] 80 | pub struct Options { 81 | /// Require data pushes be minimally encoded. 82 | pub require_minimal: bool, //TODO(stevenroose) double check all fRequireMinimal usage in Core 83 | /// Verify OP_CHECKLOCKTIMEVERIFY. 84 | pub verify_cltv: bool, 85 | /// Verify OP_CHECKSEQUENCEVERIFY. 86 | pub verify_csv: bool, 87 | /// Verify conditionals are minimally encoded. 88 | pub verify_minimal_if: bool, 89 | /// Enfore a strict limit of 1000 total stack items. 90 | pub enforce_stack_limit: bool, 91 | 92 | pub experimental: Experimental, 93 | } 94 | 95 | impl Default for Options { 96 | fn default() -> Self { 97 | Options { 98 | require_minimal: true, 99 | verify_cltv: true, 100 | verify_csv: true, 101 | verify_minimal_if: true, 102 | enforce_stack_limit: true, 103 | experimental: Experimental { op_cat: true }, 104 | } 105 | } 106 | } 107 | 108 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 109 | pub enum ExecCtx { 110 | Legacy, 111 | SegwitV0, 112 | Tapscript, 113 | } 114 | 115 | pub struct TxTemplate { 116 | pub tx: Transaction, 117 | pub prevouts: Vec, 118 | pub input_idx: usize, 119 | pub taproot_annex_scriptleaf: Option<(TapLeafHash, Option>)>, 120 | } 121 | 122 | #[derive(Debug, Clone, PartialEq, Eq)] 123 | pub struct ExecutionResult { 124 | pub success: bool, 125 | pub error: Option, 126 | pub opcode: Option, 127 | pub final_stack: Stack, 128 | } 129 | 130 | impl ExecutionResult { 131 | fn from_final_stack(ctx: ExecCtx, final_stack: Stack) -> ExecutionResult { 132 | ExecutionResult { 133 | success: match ctx { 134 | ExecCtx::Legacy => { 135 | if final_stack.is_empty() { 136 | false 137 | } else { 138 | !(!script::read_scriptbool(&final_stack.last().unwrap())) 139 | } 140 | } 141 | ExecCtx::SegwitV0 | ExecCtx::Tapscript => { 142 | if final_stack.len() != 1 { 143 | false 144 | } else { 145 | !(!script::read_scriptbool(&final_stack.last().unwrap())) 146 | } 147 | } 148 | }, 149 | final_stack, 150 | error: None, 151 | opcode: None, 152 | } 153 | } 154 | } 155 | 156 | #[derive(Debug, Clone, PartialEq, Eq, Default)] 157 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 158 | pub struct ExecStats { 159 | /// The highest number of stack items occurred during execution. 160 | /// This counts both the stack and the altstack. 161 | pub max_nb_stack_items: usize, 162 | 163 | /// The number of opcodes executed, plus an additional one 164 | /// per signature in CHECKMULTISIG. 165 | pub opcode_count: usize, 166 | 167 | /// The validation weight execution started with. 168 | pub start_validation_weight: i64, 169 | /// The current remaining validation weight. 170 | pub validation_weight: i64, 171 | } 172 | 173 | /// Partial execution of a script. 174 | pub struct Exec { 175 | ctx: ExecCtx, 176 | opt: Options, 177 | tx: TxTemplate, 178 | result: Option, 179 | 180 | sighashcache: SighashCache, 181 | script: &'static Script, 182 | instructions: Instructions<'static>, 183 | current_position: usize, 184 | cond_stack: ConditionStack, 185 | stack: Stack, 186 | altstack: Stack, 187 | last_codeseparator_pos: Option, 188 | // Initially set to the whole script, but updated when 189 | // OP_CODESEPARATOR is encountered. 190 | script_code: &'static Script, 191 | 192 | opcode_count: usize, 193 | validation_weight: i64, 194 | 195 | // runtime statistics 196 | stats: ExecStats, 197 | } 198 | 199 | impl std::ops::Drop for Exec { 200 | fn drop(&mut self) { 201 | // we need to safely drop the script we allocated 202 | unsafe { 203 | let script = core::mem::replace(&mut self.script, Script::from_bytes(&[])); 204 | let _ = Box::from_raw(script as *const Script as *mut Script); 205 | } 206 | } 207 | } 208 | 209 | impl Exec { 210 | pub fn new( 211 | ctx: ExecCtx, 212 | opt: Options, 213 | tx: TxTemplate, 214 | script: ScriptBuf, 215 | script_witness: Vec>, 216 | ) -> Result { 217 | if ctx == ExecCtx::Tapscript { 218 | if tx.taproot_annex_scriptleaf.is_none() { 219 | return Err(Error::Other("missing taproot tx info in tapscript context")); 220 | } 221 | 222 | if let Some((_, Some(ref annex))) = tx.taproot_annex_scriptleaf { 223 | if annex.first() != Some(&taproot::TAPROOT_ANNEX_PREFIX) { 224 | return Err(Error::Other("invalid annex: missing prefix")); 225 | } 226 | } 227 | } 228 | 229 | // We want to make sure the script is valid so we don't have to throw parsing errors 230 | // while executing. 231 | let instructions = if opt.require_minimal { 232 | script.instructions_minimal() 233 | } else { 234 | script.instructions() 235 | }; 236 | if let Some(err) = instructions.clone().find_map(|res| res.err()) { 237 | return Err(Error::InvalidScript(err)); 238 | } 239 | 240 | // ***** 241 | // Make sure there is no more possible exit path after this point! 242 | // Otherwise we are leaking memory. 243 | // ***** 244 | 245 | // We box alocate the script to get a static Instructions iterator. 246 | // We will manually drop this allocation in the ops::Drop impl. 247 | let script = Box::leak(script.into_boxed_script()) as &'static Script; 248 | let instructions = if opt.require_minimal { 249 | script.instructions_minimal() 250 | } else { 251 | script.instructions() 252 | }; 253 | 254 | //TODO(stevenroose) make this more efficient 255 | let witness_size = 256 | Encodable::consensus_encode(&script_witness, &mut bitcoin::io::sink()).unwrap(); 257 | let start_validation_weight = VALIDATION_WEIGHT_OFFSET + witness_size as i64; 258 | 259 | let mut ret = Exec { 260 | ctx, 261 | result: None, 262 | 263 | sighashcache: SighashCache::new(tx.tx.clone()), 264 | script, 265 | instructions, 266 | current_position: 0, 267 | cond_stack: ConditionStack::new(), 268 | //TODO(stevenroose) does this need to be reversed? 269 | stack: Stack::from_u8_vec(script_witness), 270 | altstack: Stack::new(), 271 | opcode_count: 0, 272 | validation_weight: start_validation_weight, 273 | last_codeseparator_pos: None, 274 | script_code: script, 275 | 276 | opt, 277 | tx, 278 | 279 | stats: ExecStats { 280 | start_validation_weight, 281 | validation_weight: start_validation_weight, 282 | ..Default::default() 283 | }, 284 | }; 285 | ret.update_stats(); 286 | Ok(ret) 287 | } 288 | 289 | pub fn with_stack( 290 | ctx: ExecCtx, 291 | opt: Options, 292 | tx: TxTemplate, 293 | script: ScriptBuf, 294 | script_witness: Vec>, 295 | stack: Stack, 296 | altstack: Stack, 297 | ) -> Result { 298 | let mut ret = Self::new(ctx, opt, tx, script, script_witness); 299 | if let Ok(exec) = &mut ret { 300 | exec.stack = stack; 301 | exec.altstack = altstack; 302 | } 303 | ret 304 | } 305 | ////////////////// 306 | // SOME GETTERS // 307 | ////////////////// 308 | 309 | pub fn result(&self) -> Option<&ExecutionResult> { 310 | self.result.as_ref() 311 | } 312 | 313 | pub fn script_position(&self) -> usize { 314 | self.script.len() - self.instructions.as_script().len() 315 | } 316 | 317 | pub fn remaining_script(&self) -> &Script { 318 | let pos = self.script_position(); 319 | &self.script[pos..] 320 | } 321 | 322 | pub fn stack(&self) -> &Stack { 323 | &self.stack 324 | } 325 | 326 | pub fn altstack(&self) -> &Stack { 327 | &self.altstack 328 | } 329 | 330 | pub fn stats(&self) -> &ExecStats { 331 | &self.stats 332 | } 333 | 334 | /////////////// 335 | // UTILITIES // 336 | /////////////// 337 | 338 | fn fail(&mut self, err: ExecError) -> Result<(), &ExecutionResult> { 339 | let res = ExecutionResult { 340 | success: false, 341 | error: Some(err), 342 | opcode: None, 343 | final_stack: self.stack.clone(), 344 | }; 345 | self.result = Some(res); 346 | Err(self.result.as_ref().unwrap()) 347 | } 348 | 349 | fn failop(&mut self, err: ExecError, op: Opcode) -> Result<(), &ExecutionResult> { 350 | let res = ExecutionResult { 351 | success: false, 352 | error: Some(err), 353 | opcode: Some(op), 354 | final_stack: self.stack.clone(), 355 | }; 356 | self.result = Some(res); 357 | Err(self.result.as_ref().unwrap()) 358 | } 359 | 360 | fn check_lock_time(&mut self, lock_time: i64) -> bool { 361 | use bitcoin::locktime::absolute::LockTime; 362 | let lock_time = match lock_time.try_into() { 363 | Ok(l) => LockTime::from_consensus(l), 364 | Err(_) => return false, 365 | }; 366 | 367 | match (lock_time, self.tx.tx.lock_time) { 368 | (LockTime::Blocks(h1), LockTime::Blocks(h2)) if h1 > h2 => return false, 369 | (LockTime::Seconds(t1), LockTime::Seconds(t2)) if t1 > t2 => return false, 370 | (LockTime::Blocks(_), LockTime::Seconds(_)) => return false, 371 | (LockTime::Seconds(_), LockTime::Blocks(_)) => return false, 372 | _ => {} 373 | } 374 | 375 | if self.tx.tx.input[self.tx.input_idx].sequence.is_final() { 376 | return false; 377 | } 378 | 379 | true 380 | } 381 | 382 | fn check_sequence(&mut self, sequence: i64) -> bool { 383 | use bitcoin::locktime::relative::LockTime; 384 | 385 | // Fail if the transaction's version number is not set high 386 | // enough to trigger BIP 68 rules. 387 | if self.tx.tx.version < transaction::Version::TWO { 388 | return false; 389 | } 390 | 391 | let input_sequence = self.tx.tx.input[self.tx.input_idx].sequence; 392 | let input_lock_time = match input_sequence.to_relative_lock_time() { 393 | Some(lt) => lt, 394 | None => return false, 395 | }; 396 | 397 | let lock_time = 398 | match LockTime::from_consensus(u32::try_from(sequence).expect("sequence is u32")) { 399 | Ok(lt) => lt, 400 | Err(_) => return false, 401 | }; 402 | 403 | match (lock_time, input_lock_time) { 404 | (LockTime::Blocks(h1), LockTime::Blocks(h2)) if h1 > h2 => return false, 405 | (LockTime::Time(t1), LockTime::Time(t2)) if t1 > t2 => return false, 406 | (LockTime::Blocks(_), LockTime::Time(_)) => return false, 407 | (LockTime::Time(_), LockTime::Blocks(_)) => return false, 408 | _ => {} 409 | } 410 | 411 | true 412 | } 413 | 414 | fn check_sig_pre_tap(&mut self, sig: &[u8], pk: &[u8]) -> Result { 415 | //TODO(stevenroose) somehow sigops limit should be checked somewhere 416 | 417 | // Drop the signature in pre-segwit scripts but not segwit scripts 418 | let mut scriptcode = Cow::Borrowed(self.script_code.as_bytes()); 419 | if self.ctx == ExecCtx::Legacy { 420 | let mut i = 0; 421 | while i < scriptcode.len() - sig.len() { 422 | if &scriptcode[i..i + sig.len()] == sig { 423 | scriptcode.to_mut().drain(i..i + sig.len()); 424 | } else { 425 | i += 1; 426 | } 427 | } 428 | } 429 | 430 | //TODO(stevenroose) the signature and pk encoding checks we use here 431 | // might not be exactly identical to Core's 432 | 433 | if self.ctx == ExecCtx::SegwitV0 && pk.len() == 65 { 434 | return Err(ExecError::WitnessPubkeyType); 435 | } 436 | 437 | Ok(self.check_sig_ecdsa(sig, pk, &scriptcode)) 438 | } 439 | 440 | fn check_sig_tap(&mut self, sig: &[u8], pk: &[u8]) -> Result { 441 | if !sig.is_empty() { 442 | self.validation_weight -= VALIDATION_WEIGHT_PER_SIGOP_PASSED; 443 | if self.validation_weight < 0 { 444 | return Err(ExecError::TapscriptValidationWeight); 445 | } 446 | } 447 | 448 | if pk.is_empty() { 449 | Err(ExecError::PubkeyType) 450 | } else if pk.len() == 32 { 451 | if !sig.is_empty() { 452 | self.check_sig_schnorr(sig, pk)?; 453 | Ok(true) 454 | } else { 455 | Ok(false) 456 | } 457 | } else { 458 | Ok(true) 459 | } 460 | } 461 | 462 | fn check_sig(&mut self, sig: &[u8], pk: &[u8]) -> Result { 463 | match self.ctx { 464 | ExecCtx::Legacy | ExecCtx::SegwitV0 => self.check_sig_pre_tap(sig, pk), 465 | ExecCtx::Tapscript => self.check_sig_tap(sig, pk), 466 | } 467 | } 468 | 469 | /////////////// 470 | // EXECUTION // 471 | /////////////// 472 | 473 | /// Returns true when execution is done. 474 | pub fn exec_next(&mut self) -> Result<(), &ExecutionResult> { 475 | if let Some(ref res) = self.result { 476 | return Err(res); 477 | } 478 | 479 | self.current_position = self.script.len() - self.instructions.as_script().len(); 480 | let instruction = match self.instructions.next() { 481 | Some(Ok(i)) => i, 482 | None => { 483 | let res = ExecutionResult::from_final_stack(self.ctx, self.stack.clone()); 484 | self.result = Some(res); 485 | return Err(self.result.as_ref().unwrap()); 486 | } 487 | Some(Err(_)) => unreachable!("we checked the script beforehand"), 488 | }; 489 | 490 | let exec = self.cond_stack.all_true(); 491 | match instruction { 492 | Instruction::PushBytes(p) => { 493 | if p.len() > MAX_SCRIPT_ELEMENT_SIZE { 494 | return self.fail(ExecError::PushSize); 495 | } 496 | if exec { 497 | self.stack.pushstr(p.as_bytes()); 498 | } 499 | } 500 | Instruction::Op(op) => { 501 | // Some things we do even when we're not executing. 502 | 503 | // Note how OP_RESERVED does not count towards the opcode limit. 504 | if (self.ctx == ExecCtx::Legacy || self.ctx == ExecCtx::SegwitV0) 505 | && op.to_u8() > OP_PUSHNUM_16.to_u8() 506 | { 507 | self.opcode_count += 1; 508 | if self.opcode_count > MAX_OPS_PER_SCRIPT { 509 | return self.fail(ExecError::OpCount); 510 | } 511 | } 512 | 513 | match op { 514 | OP_CAT if !self.opt.experimental.op_cat || self.ctx != ExecCtx::Tapscript => { 515 | return self.failop(ExecError::DisabledOpcode, op); 516 | } 517 | OP_SUBSTR | OP_LEFT | OP_RIGHT | OP_INVERT | OP_AND | OP_OR | OP_XOR 518 | | OP_2MUL | OP_2DIV | OP_MUL | OP_DIV | OP_MOD | OP_LSHIFT | OP_RSHIFT => { 519 | return self.failop(ExecError::DisabledOpcode, op); 520 | } 521 | OP_RESERVED => { 522 | return self.failop(ExecError::Debug, op); 523 | } 524 | 525 | _ => {} 526 | } 527 | 528 | if exec || (op.to_u8() >= OP_IF.to_u8() && op.to_u8() <= OP_ENDIF.to_u8()) { 529 | if let Err(err) = self.exec_opcode(op) { 530 | return self.failop(err, op); 531 | } 532 | } 533 | } 534 | } 535 | 536 | self.update_stats(); 537 | Ok(()) 538 | } 539 | 540 | fn exec_opcode(&mut self, op: Opcode) -> Result<(), ExecError> { 541 | let exec = self.cond_stack.all_true(); 542 | 543 | // Remember to leave stack intact until all errors have occurred. 544 | match op { 545 | // 546 | // Push value 547 | OP_PUSHNUM_NEG1 | OP_PUSHNUM_1 | OP_PUSHNUM_2 | OP_PUSHNUM_3 | OP_PUSHNUM_4 548 | | OP_PUSHNUM_5 | OP_PUSHNUM_6 | OP_PUSHNUM_7 | OP_PUSHNUM_8 | OP_PUSHNUM_9 549 | | OP_PUSHNUM_10 | OP_PUSHNUM_11 | OP_PUSHNUM_12 | OP_PUSHNUM_13 | OP_PUSHNUM_14 550 | | OP_PUSHNUM_15 | OP_PUSHNUM_16 => { 551 | let n = op.to_u8() - (OP_PUSHNUM_1.to_u8() - 2); 552 | self.stack.pushnum((n as i64) - 1); 553 | } 554 | 555 | // 556 | // Control 557 | OP_NOP => {} 558 | 559 | OP_CLTV if self.opt.verify_cltv => { 560 | let top = self.stack.topstr(-1)?; 561 | 562 | // Note that elsewhere numeric opcodes are limited to 563 | // operands in the range -2**31+1 to 2**31-1, however it is 564 | // legal for opcodes to produce results exceeding that 565 | // range. This limitation is implemented by CScriptNum's 566 | // default 4-byte limit. 567 | // 568 | // If we kept to that limit we'd have a year 2038 problem, 569 | // even though the nLockTime field in transactions 570 | // themselves is uint32 which only becomes meaningless 571 | // after the year 2106. 572 | // 573 | // Thus as a special case we tell CScriptNum to accept up 574 | // to 5-byte bignums, which are good until 2**39-1, well 575 | // beyond the 2**32-1 limit of the nLockTime field itself. 576 | let n = read_scriptint(&top, 5, self.opt.require_minimal)?; 577 | 578 | if n < 0 { 579 | return Err(ExecError::NegativeLocktime); 580 | } 581 | 582 | if !self.check_lock_time(n) { 583 | return Err(ExecError::UnsatisfiedLocktime); 584 | } 585 | } 586 | OP_CLTV => {} // otherwise nop 587 | 588 | OP_CSV if self.opt.verify_csv => { 589 | let top = self.stack.topstr(-1)?; 590 | 591 | // nSequence, like nLockTime, is a 32-bit unsigned integer 592 | // field. See the comment in CHECKLOCKTIMEVERIFY regarding 593 | // 5-byte numeric operands. 594 | let n = read_scriptint(&top, 5, self.opt.require_minimal)?; 595 | 596 | if n < 0 { 597 | return Err(ExecError::NegativeLocktime); 598 | } 599 | 600 | //TODO(stevenroose) check this logic 601 | //TODO(stevenroose) check if this cast is ok 602 | if n & SEQUENCE_LOCKTIME_DISABLE_FLAG as i64 == 0 && !self.check_sequence(n) { 603 | return Err(ExecError::UnsatisfiedLocktime); 604 | } 605 | } 606 | OP_CSV => {} // otherwise nop 607 | 608 | OP_NOP1 | OP_NOP4 | OP_NOP5 | OP_NOP6 | OP_NOP7 | OP_NOP8 | OP_NOP9 | OP_NOP10 => { 609 | // nops 610 | } 611 | 612 | OP_IF | OP_NOTIF => { 613 | if exec { 614 | let top = self.stack.topstr(-1)?; 615 | 616 | // Tapscript requires minimal IF/NOTIF inputs as a consensus rule. 617 | if self.ctx == ExecCtx::Tapscript { 618 | // The input argument to the OP_IF and OP_NOTIF opcodes must be either 619 | // exactly 0 (the empty vector) or exactly 1 (the one-byte vector with value 1). 620 | if top.len() > 1 || (top.len() == 1 && top[0] != 1) { 621 | return Err(ExecError::TapscriptMinimalIf); 622 | } 623 | } 624 | // Under segwit v0 only enabled as policy. 625 | if self.opt.verify_minimal_if 626 | && self.ctx == ExecCtx::SegwitV0 627 | && (top.len() > 1 || (top.len() == 1 && top[0] != 1)) 628 | { 629 | return Err(ExecError::TapscriptMinimalIf); 630 | } 631 | let b = if op == OP_NOTIF { 632 | !script::read_scriptbool(&top) 633 | } else { 634 | script::read_scriptbool(&top) 635 | }; 636 | self.stack.pop().unwrap(); 637 | self.cond_stack.push(b); 638 | } else { 639 | self.cond_stack.push(false); 640 | } 641 | } 642 | 643 | OP_ELSE => { 644 | if !self.cond_stack.toggle_top() { 645 | return Err(ExecError::UnbalancedConditional); 646 | } 647 | } 648 | 649 | OP_ENDIF => { 650 | if !self.cond_stack.pop() { 651 | return Err(ExecError::UnbalancedConditional); 652 | } 653 | } 654 | 655 | OP_VERIFY => { 656 | let top = self.stack.topstr(-1)?; 657 | 658 | if !script::read_scriptbool(&top) { 659 | return Err(ExecError::Verify); 660 | } else { 661 | self.stack.pop().unwrap(); 662 | } 663 | } 664 | 665 | OP_RETURN => return Err(ExecError::OpReturn), 666 | 667 | // 668 | // Stack operations 669 | OP_TOALTSTACK => { 670 | let top = self.stack.pop().ok_or(ExecError::InvalidStackOperation)?; 671 | self.altstack.push(top); 672 | } 673 | 674 | OP_FROMALTSTACK => { 675 | let top = self 676 | .altstack 677 | .pop() 678 | .ok_or(ExecError::InvalidStackOperation)?; 679 | self.stack.push(top); 680 | } 681 | 682 | OP_2DROP => { 683 | // (x1 x2 -- ) 684 | self.stack.needn(2)?; 685 | self.stack.popn(2).unwrap(); 686 | } 687 | 688 | OP_2DUP => { 689 | // (x1 x2 -- x1 x2 x1 x2) 690 | let x1 = self.stack.top(-2)?.clone(); 691 | let x2 = self.stack.top(-1)?.clone(); 692 | self.stack.push(x1); 693 | self.stack.push(x2); 694 | } 695 | 696 | OP_3DUP => { 697 | // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) 698 | let x1 = self.stack.top(-3)?.clone(); 699 | let x2 = self.stack.top(-2)?.clone(); 700 | let x3 = self.stack.top(-1)?.clone(); 701 | self.stack.push(x1); 702 | self.stack.push(x2); 703 | self.stack.push(x3); 704 | } 705 | 706 | OP_2OVER => { 707 | // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) 708 | self.stack.needn(4)?; 709 | let x1 = self.stack.top(-4)?.clone(); 710 | let x2 = self.stack.top(-3)?.clone(); 711 | self.stack.push(x1); 712 | self.stack.push(x2); 713 | } 714 | 715 | OP_2ROT => { 716 | // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) 717 | self.stack.needn(6)?; 718 | let x6 = self.stack.pop().unwrap(); 719 | let x5 = self.stack.pop().unwrap(); 720 | let x4 = self.stack.pop().unwrap(); 721 | let x3 = self.stack.pop().unwrap(); 722 | let x2 = self.stack.pop().unwrap(); 723 | let x1 = self.stack.pop().unwrap(); 724 | self.stack.push(x3); 725 | self.stack.push(x4); 726 | self.stack.push(x5); 727 | self.stack.push(x6); 728 | self.stack.push(x1); 729 | self.stack.push(x2); 730 | } 731 | 732 | OP_2SWAP => { 733 | // (x1 x2 x3 x4 -- x3 x4 x1 x2) 734 | self.stack.needn(4)?; 735 | let x4 = self.stack.pop().unwrap(); 736 | let x3 = self.stack.pop().unwrap(); 737 | let x2 = self.stack.pop().unwrap(); 738 | let x1 = self.stack.pop().unwrap(); 739 | self.stack.push(x3); 740 | self.stack.push(x4); 741 | self.stack.push(x1); 742 | self.stack.push(x2); 743 | } 744 | 745 | OP_IFDUP => { 746 | // (x - 0 | x x) 747 | let top = self.stack.topstr(-1)?; 748 | if script::read_scriptbool(&top) { 749 | self.stack.push(self.stack.top(-1)?.clone()); 750 | } 751 | } 752 | 753 | OP_DEPTH => { 754 | // -- stacksize 755 | self.stack.pushnum(self.stack.len() as i64); 756 | } 757 | 758 | OP_DROP => { 759 | // (x -- ) 760 | if self.stack.pop().is_none() { 761 | return Err(ExecError::InvalidStackOperation); 762 | } 763 | } 764 | 765 | OP_DUP => { 766 | // (x -- x x) 767 | let top = self.stack.top(-1)?.clone(); 768 | self.stack.push(top); 769 | } 770 | 771 | OP_NIP => { 772 | // (x1 x2 -- x2) 773 | self.stack.needn(2)?; 774 | let x2 = self.stack.pop().unwrap(); 775 | self.stack.pop().unwrap(); 776 | self.stack.push(x2); 777 | } 778 | 779 | OP_OVER => { 780 | // (x1 x2 -- x1 x2 x1) 781 | let under_top = self.stack.top(-2)?.clone(); 782 | self.stack.push(under_top); 783 | } 784 | 785 | OP_PICK | OP_ROLL => { 786 | // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) 787 | // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) 788 | let x = self.stack.topnum(-1, self.opt.require_minimal)?; 789 | if x < 0 || x >= self.stack.len() as i64 { 790 | return Err(ExecError::InvalidStackOperation); 791 | } 792 | self.stack.pop().unwrap(); 793 | let elem = self.stack.top(-x as isize - 1).unwrap().clone(); 794 | if op == OP_ROLL { 795 | self.stack.remove(self.stack.len() - x as usize - 1); 796 | } 797 | self.stack.push(elem); 798 | } 799 | 800 | OP_ROT => { 801 | // (x1 x2 x3 -- x2 x3 x1) 802 | self.stack.needn(3)?; 803 | let x3 = self.stack.pop().unwrap(); 804 | let x2 = self.stack.pop().unwrap(); 805 | let x1 = self.stack.pop().unwrap(); 806 | self.stack.push(x2); 807 | self.stack.push(x3); 808 | self.stack.push(x1); 809 | } 810 | 811 | OP_SWAP => { 812 | // (x1 x2 -- x2 x1) 813 | self.stack.needn(2)?; 814 | let x2 = self.stack.pop().unwrap(); 815 | let x1 = self.stack.pop().unwrap(); 816 | self.stack.push(x2); 817 | self.stack.push(x1); 818 | } 819 | 820 | OP_TUCK => { 821 | // (x1 x2 -- x2 x1 x2) 822 | self.stack.needn(2)?; 823 | let x2 = self.stack.pop().unwrap(); 824 | let x1 = self.stack.pop().unwrap(); 825 | self.stack.push(x2.clone()); 826 | self.stack.push(x1); 827 | self.stack.push(x2); 828 | } 829 | 830 | OP_CAT if self.opt.experimental.op_cat && self.ctx == ExecCtx::Tapscript => { 831 | // (x1 x2 -- x1|x2) 832 | self.stack.needn(2)?; 833 | let x2 = self.stack.popstr().unwrap(); 834 | let x1 = self.stack.popstr().unwrap(); 835 | let ret: Vec = x1.into_iter().chain(x2).collect(); 836 | if ret.len() > MAX_SCRIPT_ELEMENT_SIZE { 837 | return Err(ExecError::PushSize); 838 | } 839 | self.stack.pushstr(&ret); 840 | } 841 | 842 | OP_SIZE => { 843 | // (in -- in size) 844 | let top = self.stack.topstr(-1)?; 845 | self.stack.pushnum(top.len() as i64); 846 | } 847 | 848 | // 849 | // Bitwise logic 850 | OP_EQUAL | OP_EQUALVERIFY => { 851 | // (x1 x2 - bool) 852 | self.stack.needn(2)?; 853 | let x2 = self.stack.popstr().unwrap(); 854 | let x1 = self.stack.popstr().unwrap(); 855 | let equal = x1 == x2; 856 | if op == OP_EQUALVERIFY && !equal { 857 | return Err(ExecError::EqualVerify); 858 | } 859 | if op == OP_EQUAL { 860 | let item = if equal { 1 } else { 0 }; 861 | self.stack.pushnum(item); 862 | } 863 | } 864 | 865 | // 866 | // Numeric 867 | OP_1ADD | OP_1SUB | OP_NEGATE | OP_ABS | OP_NOT | OP_0NOTEQUAL => { 868 | // (in -- out) 869 | let x = self.stack.topnum(-1, self.opt.require_minimal)?; 870 | let res = match op { 871 | OP_1ADD => x 872 | .checked_add(1) 873 | .ok_or(ExecError::ScriptIntNumericOverflow)?, 874 | OP_1SUB => x 875 | .checked_sub(1) 876 | .ok_or(ExecError::ScriptIntNumericOverflow)?, 877 | OP_NEGATE => x.checked_neg().ok_or(ExecError::ScriptIntNumericOverflow)?, 878 | OP_ABS => x.abs(), 879 | OP_NOT => (x == 0) as i64, 880 | OP_0NOTEQUAL => (x != 0) as i64, 881 | _ => unreachable!(), 882 | }; 883 | self.stack.pop().unwrap(); 884 | self.stack.pushnum(res); 885 | } 886 | 887 | OP_ADD 888 | | OP_SUB 889 | | OP_BOOLAND 890 | | OP_BOOLOR 891 | | OP_NUMEQUAL 892 | | OP_NUMEQUALVERIFY 893 | | OP_NUMNOTEQUAL 894 | | OP_LESSTHAN 895 | | OP_GREATERTHAN 896 | | OP_LESSTHANOREQUAL 897 | | OP_GREATERTHANOREQUAL 898 | | OP_MIN 899 | | OP_MAX => { 900 | // (x1 x2 -- out) 901 | let x1 = self.stack.topnum(-2, self.opt.require_minimal)?; 902 | let x2 = self.stack.topnum(-1, self.opt.require_minimal)?; 903 | let res = match op { 904 | OP_ADD => x1 905 | .checked_add(x2) 906 | .ok_or(ExecError::ScriptIntNumericOverflow)?, 907 | OP_SUB => x1 908 | .checked_sub(x2) 909 | .ok_or(ExecError::ScriptIntNumericOverflow)?, 910 | OP_BOOLAND => (x1 != 0 && x2 != 0) as i64, 911 | OP_BOOLOR => (x1 != 0 || x2 != 0) as i64, 912 | OP_NUMEQUAL => (x1 == x2) as i64, 913 | OP_NUMEQUALVERIFY => (x1 == x2) as i64, 914 | OP_NUMNOTEQUAL => (x1 != x2) as i64, 915 | OP_LESSTHAN => (x1 < x2) as i64, 916 | OP_GREATERTHAN => (x1 > x2) as i64, 917 | OP_LESSTHANOREQUAL => (x1 <= x2) as i64, 918 | OP_GREATERTHANOREQUAL => (x1 >= x2) as i64, 919 | OP_MIN => cmp::min(x1, x2), 920 | OP_MAX => cmp::max(x1, x2), 921 | _ => unreachable!(), 922 | }; 923 | if op == OP_NUMEQUALVERIFY && res == 0 { 924 | return Err(ExecError::NumEqualVerify); 925 | } 926 | self.stack.popn(2).unwrap(); 927 | if op != OP_NUMEQUALVERIFY { 928 | self.stack.pushnum(res); 929 | } 930 | } 931 | 932 | OP_WITHIN => { 933 | // (x min max -- out) 934 | let x1 = self.stack.topnum(-3, self.opt.require_minimal)?; 935 | let x2 = self.stack.topnum(-2, self.opt.require_minimal)?; 936 | let x3 = self.stack.topnum(-1, self.opt.require_minimal)?; 937 | self.stack.popn(3).unwrap(); 938 | let res = x2 <= x1 && x1 < x3; 939 | let item = if res { 1 } else { 0 }; 940 | self.stack.pushnum(item); 941 | } 942 | 943 | // 944 | // Crypto 945 | 946 | // (in -- hash) 947 | OP_RIPEMD160 => { 948 | let top = self.stack.popstr()?; 949 | self.stack 950 | .pushstr(ripemd160::Hash::hash(&top[..]).to_byte_array().as_ref()); 951 | } 952 | OP_SHA1 => { 953 | let top = self.stack.popstr()?; 954 | self.stack 955 | .pushstr(sha1::Hash::hash(&top[..]).to_byte_array().as_ref()); 956 | } 957 | OP_SHA256 => { 958 | let top = self.stack.popstr()?; 959 | self.stack 960 | .pushstr(sha256::Hash::hash(&top[..]).to_byte_array().as_ref()); 961 | } 962 | OP_HASH160 => { 963 | let top = self.stack.popstr()?; 964 | self.stack 965 | .pushstr(hash160::Hash::hash(&top[..]).to_byte_array().as_ref()); 966 | } 967 | OP_HASH256 => { 968 | let top = self.stack.popstr()?; 969 | self.stack 970 | .pushstr(sha256d::Hash::hash(&top[..]).to_byte_array().as_ref()); 971 | } 972 | 973 | OP_CODESEPARATOR => { 974 | // Store this CODESEPARATOR position and update the scriptcode. 975 | self.last_codeseparator_pos = Some(self.current_position as u32); 976 | self.script_code = &self.script[self.current_position..]; 977 | } 978 | 979 | OP_CHECKSIG | OP_CHECKSIGVERIFY => { 980 | let sig = self.stack.topstr(-2)?.clone(); 981 | let pk = self.stack.topstr(-1)?.clone(); 982 | let res = self.check_sig(&sig, &pk)?; 983 | self.stack.popn(2).unwrap(); 984 | if op == OP_CHECKSIGVERIFY && !res { 985 | return Err(ExecError::CheckSigVerify); 986 | } 987 | if op == OP_CHECKSIG { 988 | let ret = if res { 1 } else { 0 }; 989 | self.stack.pushnum(ret); 990 | } 991 | } 992 | 993 | OP_CHECKSIGADD => { 994 | if self.ctx == ExecCtx::Legacy || self.ctx == ExecCtx::SegwitV0 { 995 | return Err(ExecError::BadOpcode); 996 | } 997 | let sig = self.stack.topstr(-3)?.clone(); 998 | let mut n = self.stack.topnum(-2, self.opt.require_minimal)?; 999 | let pk = self.stack.topstr(-1)?.clone(); 1000 | let res = self.check_sig(&sig, &pk)?; 1001 | self.stack.popn(3).unwrap(); 1002 | if res { 1003 | n += 1; 1004 | } 1005 | self.stack.pushnum(n); 1006 | } 1007 | 1008 | OP_CHECKMULTISIG | OP_CHECKMULTISIGVERIFY => { 1009 | unimplemented!(); 1010 | } 1011 | 1012 | // remainder 1013 | _ => return Err(ExecError::BadOpcode), 1014 | } 1015 | 1016 | if self.opt.enforce_stack_limit && self.stack.len() + self.altstack.len() > MAX_STACK_SIZE { 1017 | return Err(ExecError::StackSize); 1018 | } 1019 | 1020 | Ok(()) 1021 | } 1022 | 1023 | //////////////// 1024 | // STATISTICS // 1025 | //////////////// 1026 | 1027 | fn update_stats(&mut self) { 1028 | let stack_items = self.stack.len() + self.altstack.len(); 1029 | self.stats.max_nb_stack_items = cmp::max(self.stats.max_nb_stack_items, stack_items); 1030 | 1031 | self.stats.opcode_count = self.opcode_count; 1032 | self.stats.validation_weight = self.validation_weight; 1033 | } 1034 | } 1035 | 1036 | /// Decodes an interger in script format with flexible size limit. 1037 | /// 1038 | /// Note that in the majority of cases, you will want to use either 1039 | /// [`read_scriptint`] or [`read_scriptint_non_minimal`] instead. 1040 | /// 1041 | /// Panics if max_size exceeds 8. 1042 | pub fn read_scriptint_size(v: &[u8], max_size: usize, minimal: bool) -> Result { 1043 | assert!(max_size <= 8); 1044 | 1045 | if v.len() > max_size { 1046 | return Err(script::Error::NumericOverflow); 1047 | } 1048 | 1049 | if v.is_empty() { 1050 | return Ok(0); 1051 | } 1052 | 1053 | if minimal { 1054 | let last = match v.last() { 1055 | Some(last) => last, 1056 | None => return Ok(0), 1057 | }; 1058 | // Comment and code copied from Bitcoin Core: 1059 | // https://github.com/bitcoin/bitcoin/blob/447f50e4aed9a8b1d80e1891cda85801aeb80b4e/src/script/script.h#L247-L262 1060 | // If the most-significant-byte - excluding the sign bit - is zero 1061 | // then we're not minimal. Note how this test also rejects the 1062 | // negative-zero encoding, 0x80. 1063 | if (*last & 0x7f) == 0 { 1064 | // One exception: if there's more than one byte and the most 1065 | // significant bit of the second-most-significant-byte is set 1066 | // it would conflict with the sign bit. An example of this case 1067 | // is +-255, which encode to 0xff00 and 0xff80 respectively. 1068 | // (big-endian). 1069 | if v.len() <= 1 || (v[v.len() - 2] & 0x80) == 0 { 1070 | return Err(script::Error::NonMinimalPush); 1071 | } 1072 | } 1073 | } 1074 | 1075 | Ok(scriptint_parse(v)) 1076 | } 1077 | 1078 | /// Caller to guarantee that `v` is not empty. 1079 | fn scriptint_parse(v: &[u8]) -> i64 { 1080 | let (mut ret, sh) = v 1081 | .iter() 1082 | .fold((0, 0), |(acc, sh), n| (acc + ((*n as i64) << sh), sh + 8)); 1083 | if v[v.len() - 1] & 0x80 != 0 { 1084 | ret &= (1 << (sh - 1)) - 1; 1085 | ret = -ret; 1086 | } 1087 | ret 1088 | } 1089 | 1090 | fn read_scriptint(item: &[u8], size: usize, minimal: bool) -> Result { 1091 | read_scriptint_size(item, size, minimal).map_err(|e| match e { 1092 | script::Error::NonMinimalPush => ExecError::MinimalData, 1093 | // only possible if size is 4 or lower 1094 | script::Error::NumericOverflow => ExecError::ScriptIntNumericOverflow, 1095 | // should never happen 1096 | _ => unreachable!(), 1097 | }) 1098 | } 1099 | 1100 | /// Decodes an integer in script format without non-minimal error. 1101 | /// 1102 | /// The overflow error for slices over 4 bytes long is still there. 1103 | /// See [`read_scriptint`] for a description of some subtleties of 1104 | /// this function. 1105 | pub fn read_scriptint_non_minimal(v: &[u8]) -> Result { 1106 | read_scriptint_size(v, DEFAULT_MAX_SCRIPTINT_SIZE, false) 1107 | } 1108 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! or_else { 3 | ($e:expr, $($else:tt)+) => { 4 | if let Some(v) = $e { 5 | v 6 | } else { 7 | return $($else)+; 8 | } 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io::{self, Write}; 3 | use std::path::PathBuf; 4 | 5 | use bitcoin::hashes::Hash; 6 | use bitcoin::hex::DisplayHex; 7 | use bitcoin::taproot::TapLeafHash; 8 | use bitcoin::{ScriptBuf, Transaction}; 9 | use clap::Parser; 10 | 11 | use bitcoin_scriptexec::*; 12 | 13 | #[derive(Parser)] 14 | #[command(author = "Steven Roose ", version, about)] 15 | struct Args { 16 | /// filepath to script ASM file 17 | #[arg(required = true)] 18 | script_path: PathBuf, 19 | /// Whether to print debug info 20 | #[arg(long)] 21 | debug: bool, 22 | /// Whether to output result in JSON. 23 | #[arg(long)] 24 | json: bool, 25 | } 26 | 27 | /// A wrapper for the stack types to print them better. 28 | struct FmtStack<'a>(&'a Stack); 29 | impl fmt::Display for FmtStack<'_> { 30 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 31 | let mut iter = self.0.iter_str().rev().peekable(); 32 | while let Some(item) = iter.next() { 33 | write!(f, "<{}>", item.as_hex())?; 34 | if iter.peek().is_some() { 35 | write!(f, " ")?; 36 | } 37 | } 38 | Ok(()) 39 | } 40 | } 41 | 42 | fn inner_main() -> Result<(), String> { 43 | let args = Args::parse(); 44 | 45 | let script_asm = std::fs::read_to_string(args.script_path).expect("error reading script file"); 46 | let script = ScriptBuf::from_asm(&script_asm).expect("error parsing script"); 47 | println!("Script in hex: {}", script.as_bytes().to_lower_hex_string()); 48 | println!("Script size: {} bytes", script.as_bytes().len()); 49 | 50 | let start = std::time::Instant::now(); 51 | let mut exec = Exec::new( 52 | ExecCtx::Tapscript, 53 | Options::default(), 54 | TxTemplate { 55 | tx: Transaction { 56 | version: bitcoin::transaction::Version::TWO, 57 | lock_time: bitcoin::locktime::absolute::LockTime::ZERO, 58 | input: vec![], 59 | output: vec![], 60 | }, 61 | prevouts: vec![], 62 | input_idx: 0, 63 | taproot_annex_scriptleaf: Some((TapLeafHash::all_zeros(), None)), 64 | }, 65 | script, 66 | vec![], 67 | ) 68 | .expect("error creating exec"); 69 | 70 | const SEP: &str = "--------------------------------------------------"; 71 | 72 | let mut out = io::stdout(); 73 | println!("{}", SEP); 74 | loop { 75 | if args.debug { 76 | if args.json { 77 | let step = json::RunStep { 78 | remaining_script: exec.remaining_script(), 79 | stack: &exec.stack().iter_str().collect::>>(), 80 | altstack: &exec.altstack().iter_str().collect::>>(), 81 | stats: Some(exec.stats()), 82 | }; 83 | serde_json::to_writer(&out, &step).expect("I/O error"); 84 | out.write_all(&[b'\n']).expect("I/O error"); 85 | } else { 86 | println!( 87 | "Remaining script: {}", 88 | exec.remaining_script().to_asm_string() 89 | ); 90 | println!("Stack: {}", FmtStack(exec.stack())); 91 | println!("AltStack: {}", FmtStack(exec.altstack())); 92 | println!("{}", SEP); 93 | } 94 | } 95 | 96 | if exec.exec_next().is_err() { 97 | break; 98 | } 99 | } 100 | 101 | let res = exec.result().unwrap().clone(); 102 | if args.json { 103 | let ret = json::RunResult { 104 | success: res.success, 105 | error: res.error.map(|e| format!("{:?}", e)), //TODO(stevenroose) fmt::Display 106 | opcode: res.opcode, 107 | final_stack: &res.final_stack.iter_str().collect::>>(), 108 | stats: Some(exec.stats()), 109 | }; 110 | serde_json::to_writer(&out, &ret).expect("I/O error"); 111 | } else { 112 | println!("Execution ended. Success: {}", res.success); 113 | print!("Final stack: {}", FmtStack(&res.final_stack)); 114 | println!(); 115 | if !res.success { 116 | println!("Failed on opcode: {:?}", res.opcode); 117 | println!("Error: {:?}", res.error); 118 | } 119 | println!("Stats:\n{:#?}", exec.stats()); 120 | println!("Time elapsed: {}ms", start.elapsed().as_millis()); 121 | } 122 | Ok(()) 123 | } 124 | 125 | fn main() { 126 | if let Err(e) = inner_main() { 127 | eprintln!("ERROR: {}", e); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::opcodes::{all::*, OP_0, OP_FALSE, OP_NOP2, OP_NOP3, OP_TRUE}; 2 | use bitcoin::Opcode; 3 | 4 | /// Parses a string into a Bitcoin script opcode 5 | /// 6 | /// Accepts both the full opcode name (e.g. "OP_CHECKSIG") and the shorter version without the "OP_" prefix 7 | /// (e.g. "CHECKSIG") 8 | /// 9 | /// # Examples 10 | /// 11 | /// ``` 12 | /// use bitcoin::opcodes::all::*; 13 | /// # use bitcoin_scriptexec::parse_opcode; 14 | /// 15 | /// assert_eq!(parse_opcode("OP_CHECKSIG").unwrap(), OP_CHECKSIG); 16 | /// assert_eq!(parse_opcode("CHECKSIG").unwrap(), OP_CHECKSIG); 17 | /// assert_eq!(parse_opcode("OP_1").unwrap(), OP_PUSHNUM_1); 18 | /// assert!(parse_opcode("INVALID").is_err()); 19 | /// ``` 20 | /// Parses a string into a Bitcoin script [`Opcode`]. 21 | pub fn parse_opcode(s: &str) -> Result { 22 | match s { 23 | // Special cases with aliases 24 | "OP_0" => Ok(OP_0), 25 | "OP_TRUE" | "TRUE" => Ok(OP_TRUE), 26 | "OP_FALSE" | "FALSE" => Ok(OP_FALSE), 27 | "OP_NOP2" | "NOP2" => Ok(OP_NOP2), 28 | "OP_NOP3" | "NOP3" => Ok(OP_NOP3), 29 | "OP_1" => Ok(OP_PUSHNUM_1), 30 | "OP_2" => Ok(OP_PUSHNUM_2), 31 | "OP_3" => Ok(OP_PUSHNUM_3), 32 | "OP_4" => Ok(OP_PUSHNUM_4), 33 | "OP_5" => Ok(OP_PUSHNUM_5), 34 | "OP_6" => Ok(OP_PUSHNUM_6), 35 | "OP_7" => Ok(OP_PUSHNUM_7), 36 | "OP_8" => Ok(OP_PUSHNUM_8), 37 | "OP_9" => Ok(OP_PUSHNUM_9), 38 | "OP_10" => Ok(OP_PUSHNUM_10), 39 | "OP_11" => Ok(OP_PUSHNUM_11), 40 | "OP_12" => Ok(OP_PUSHNUM_12), 41 | "OP_13" => Ok(OP_PUSHNUM_13), 42 | "OP_14" => Ok(OP_PUSHNUM_14), 43 | "OP_15" => Ok(OP_PUSHNUM_15), 44 | "OP_16" => Ok(OP_PUSHNUM_16), 45 | 46 | // Match both with and without OP_ prefix for all other opcodes 47 | s => { 48 | let opcode_name = if s.starts_with("OP_") { 49 | s 50 | } else { 51 | &format!("OP_{}", s) 52 | }; 53 | 54 | match opcode_name { 55 | // PUSHBYTES variants 56 | "OP_PUSHBYTES_0" => Ok(OP_PUSHBYTES_0), 57 | "OP_PUSHBYTES_1" => Ok(OP_PUSHBYTES_1), 58 | "OP_PUSHBYTES_2" => Ok(OP_PUSHBYTES_2), 59 | "OP_PUSHBYTES_3" => Ok(OP_PUSHBYTES_3), 60 | "OP_PUSHBYTES_4" => Ok(OP_PUSHBYTES_4), 61 | "OP_PUSHBYTES_5" => Ok(OP_PUSHBYTES_5), 62 | "OP_PUSHBYTES_6" => Ok(OP_PUSHBYTES_6), 63 | "OP_PUSHBYTES_7" => Ok(OP_PUSHBYTES_7), 64 | "OP_PUSHBYTES_8" => Ok(OP_PUSHBYTES_8), 65 | "OP_PUSHBYTES_9" => Ok(OP_PUSHBYTES_9), 66 | "OP_PUSHBYTES_10" => Ok(OP_PUSHBYTES_10), 67 | "OP_PUSHBYTES_11" => Ok(OP_PUSHBYTES_11), 68 | "OP_PUSHBYTES_12" => Ok(OP_PUSHBYTES_12), 69 | "OP_PUSHBYTES_13" => Ok(OP_PUSHBYTES_13), 70 | "OP_PUSHBYTES_14" => Ok(OP_PUSHBYTES_14), 71 | "OP_PUSHBYTES_15" => Ok(OP_PUSHBYTES_15), 72 | "OP_PUSHBYTES_16" => Ok(OP_PUSHBYTES_16), 73 | "OP_PUSHBYTES_17" => Ok(OP_PUSHBYTES_17), 74 | "OP_PUSHBYTES_18" => Ok(OP_PUSHBYTES_18), 75 | "OP_PUSHBYTES_19" => Ok(OP_PUSHBYTES_19), 76 | "OP_PUSHBYTES_20" => Ok(OP_PUSHBYTES_20), 77 | "OP_PUSHBYTES_21" => Ok(OP_PUSHBYTES_21), 78 | "OP_PUSHBYTES_22" => Ok(OP_PUSHBYTES_22), 79 | "OP_PUSHBYTES_23" => Ok(OP_PUSHBYTES_23), 80 | "OP_PUSHBYTES_24" => Ok(OP_PUSHBYTES_24), 81 | "OP_PUSHBYTES_25" => Ok(OP_PUSHBYTES_25), 82 | "OP_PUSHBYTES_26" => Ok(OP_PUSHBYTES_26), 83 | "OP_PUSHBYTES_27" => Ok(OP_PUSHBYTES_27), 84 | "OP_PUSHBYTES_28" => Ok(OP_PUSHBYTES_28), 85 | "OP_PUSHBYTES_29" => Ok(OP_PUSHBYTES_29), 86 | "OP_PUSHBYTES_30" => Ok(OP_PUSHBYTES_30), 87 | "OP_PUSHBYTES_31" => Ok(OP_PUSHBYTES_31), 88 | "OP_PUSHBYTES_32" => Ok(OP_PUSHBYTES_32), 89 | "OP_PUSHBYTES_33" => Ok(OP_PUSHBYTES_33), 90 | "OP_PUSHBYTES_34" => Ok(OP_PUSHBYTES_34), 91 | "OP_PUSHBYTES_35" => Ok(OP_PUSHBYTES_35), 92 | "OP_PUSHBYTES_36" => Ok(OP_PUSHBYTES_36), 93 | "OP_PUSHBYTES_37" => Ok(OP_PUSHBYTES_37), 94 | "OP_PUSHBYTES_38" => Ok(OP_PUSHBYTES_38), 95 | "OP_PUSHBYTES_39" => Ok(OP_PUSHBYTES_39), 96 | "OP_PUSHBYTES_40" => Ok(OP_PUSHBYTES_40), 97 | "OP_PUSHBYTES_41" => Ok(OP_PUSHBYTES_41), 98 | "OP_PUSHBYTES_42" => Ok(OP_PUSHBYTES_42), 99 | "OP_PUSHBYTES_43" => Ok(OP_PUSHBYTES_43), 100 | "OP_PUSHBYTES_44" => Ok(OP_PUSHBYTES_44), 101 | "OP_PUSHBYTES_45" => Ok(OP_PUSHBYTES_45), 102 | "OP_PUSHBYTES_46" => Ok(OP_PUSHBYTES_46), 103 | "OP_PUSHBYTES_47" => Ok(OP_PUSHBYTES_47), 104 | "OP_PUSHBYTES_48" => Ok(OP_PUSHBYTES_48), 105 | "OP_PUSHBYTES_49" => Ok(OP_PUSHBYTES_49), 106 | "OP_PUSHBYTES_50" => Ok(OP_PUSHBYTES_50), 107 | "OP_PUSHBYTES_51" => Ok(OP_PUSHBYTES_51), 108 | "OP_PUSHBYTES_52" => Ok(OP_PUSHBYTES_52), 109 | "OP_PUSHBYTES_53" => Ok(OP_PUSHBYTES_53), 110 | "OP_PUSHBYTES_54" => Ok(OP_PUSHBYTES_54), 111 | "OP_PUSHBYTES_55" => Ok(OP_PUSHBYTES_55), 112 | "OP_PUSHBYTES_56" => Ok(OP_PUSHBYTES_56), 113 | "OP_PUSHBYTES_57" => Ok(OP_PUSHBYTES_57), 114 | "OP_PUSHBYTES_58" => Ok(OP_PUSHBYTES_58), 115 | "OP_PUSHBYTES_59" => Ok(OP_PUSHBYTES_59), 116 | "OP_PUSHBYTES_60" => Ok(OP_PUSHBYTES_60), 117 | "OP_PUSHBYTES_61" => Ok(OP_PUSHBYTES_61), 118 | "OP_PUSHBYTES_62" => Ok(OP_PUSHBYTES_62), 119 | "OP_PUSHBYTES_63" => Ok(OP_PUSHBYTES_63), 120 | "OP_PUSHBYTES_64" => Ok(OP_PUSHBYTES_64), 121 | "OP_PUSHBYTES_65" => Ok(OP_PUSHBYTES_65), 122 | "OP_PUSHBYTES_66" => Ok(OP_PUSHBYTES_66), 123 | "OP_PUSHBYTES_67" => Ok(OP_PUSHBYTES_67), 124 | "OP_PUSHBYTES_68" => Ok(OP_PUSHBYTES_68), 125 | "OP_PUSHBYTES_69" => Ok(OP_PUSHBYTES_69), 126 | "OP_PUSHBYTES_70" => Ok(OP_PUSHBYTES_70), 127 | "OP_PUSHBYTES_71" => Ok(OP_PUSHBYTES_71), 128 | "OP_PUSHBYTES_72" => Ok(OP_PUSHBYTES_72), 129 | "OP_PUSHBYTES_73" => Ok(OP_PUSHBYTES_73), 130 | "OP_PUSHBYTES_74" => Ok(OP_PUSHBYTES_74), 131 | "OP_PUSHBYTES_75" => Ok(OP_PUSHBYTES_75), 132 | 133 | // Regular opcodes 134 | "OP_PUSHDATA1" => Ok(OP_PUSHDATA1), 135 | "OP_PUSHDATA2" => Ok(OP_PUSHDATA2), 136 | "OP_PUSHDATA4" => Ok(OP_PUSHDATA4), 137 | "OP_PUSHNUM_NEG1" => Ok(OP_PUSHNUM_NEG1), 138 | "OP_RESERVED" => Ok(OP_RESERVED), 139 | "OP_NOP" => Ok(OP_NOP), 140 | "OP_VER" => Ok(OP_VER), 141 | "OP_IF" => Ok(OP_IF), 142 | "OP_NOTIF" => Ok(OP_NOTIF), 143 | "OP_VERIF" => Ok(OP_VERIF), 144 | "OP_VERNOTIF" => Ok(OP_VERNOTIF), 145 | "OP_ELSE" => Ok(OP_ELSE), 146 | "OP_ENDIF" => Ok(OP_ENDIF), 147 | "OP_VERIFY" => Ok(OP_VERIFY), 148 | "OP_RETURN" => Ok(OP_RETURN), 149 | "OP_TOALTSTACK" => Ok(OP_TOALTSTACK), 150 | "OP_FROMALTSTACK" => Ok(OP_FROMALTSTACK), 151 | "OP_2DROP" => Ok(OP_2DROP), 152 | "OP_2DUP" => Ok(OP_2DUP), 153 | "OP_3DUP" => Ok(OP_3DUP), 154 | "OP_2OVER" => Ok(OP_2OVER), 155 | "OP_2ROT" => Ok(OP_2ROT), 156 | "OP_2SWAP" => Ok(OP_2SWAP), 157 | "OP_IFDUP" => Ok(OP_IFDUP), 158 | "OP_DEPTH" => Ok(OP_DEPTH), 159 | "OP_DROP" => Ok(OP_DROP), 160 | "OP_DUP" => Ok(OP_DUP), 161 | "OP_NIP" => Ok(OP_NIP), 162 | "OP_OVER" => Ok(OP_OVER), 163 | "OP_PICK" => Ok(OP_PICK), 164 | "OP_ROLL" => Ok(OP_ROLL), 165 | "OP_ROT" => Ok(OP_ROT), 166 | "OP_SWAP" => Ok(OP_SWAP), 167 | "OP_TUCK" => Ok(OP_TUCK), 168 | "OP_CAT" => Ok(OP_CAT), 169 | "OP_SUBSTR" => Ok(OP_SUBSTR), 170 | "OP_LEFT" => Ok(OP_LEFT), 171 | "OP_RIGHT" => Ok(OP_RIGHT), 172 | "OP_SIZE" => Ok(OP_SIZE), 173 | "OP_INVERT" => Ok(OP_INVERT), 174 | "OP_AND" => Ok(OP_AND), 175 | "OP_OR" => Ok(OP_OR), 176 | "OP_XOR" => Ok(OP_XOR), 177 | "OP_EQUAL" => Ok(OP_EQUAL), 178 | "OP_EQUALVERIFY" => Ok(OP_EQUALVERIFY), 179 | "OP_RESERVED1" => Ok(OP_RESERVED1), 180 | "OP_RESERVED2" => Ok(OP_RESERVED2), 181 | "OP_1ADD" => Ok(OP_1ADD), 182 | "OP_1SUB" => Ok(OP_1SUB), 183 | "OP_2MUL" => Ok(OP_2MUL), 184 | "OP_2DIV" => Ok(OP_2DIV), 185 | "OP_NEGATE" => Ok(OP_NEGATE), 186 | "OP_ABS" => Ok(OP_ABS), 187 | "OP_NOT" => Ok(OP_NOT), 188 | "OP_0NOTEQUAL" => Ok(OP_0NOTEQUAL), 189 | "OP_ADD" => Ok(OP_ADD), 190 | "OP_SUB" => Ok(OP_SUB), 191 | "OP_MUL" => Ok(OP_MUL), 192 | "OP_DIV" => Ok(OP_DIV), 193 | "OP_MOD" => Ok(OP_MOD), 194 | "OP_LSHIFT" => Ok(OP_LSHIFT), 195 | "OP_RSHIFT" => Ok(OP_RSHIFT), 196 | "OP_BOOLAND" => Ok(OP_BOOLAND), 197 | "OP_BOOLOR" => Ok(OP_BOOLOR), 198 | "OP_NUMEQUAL" => Ok(OP_NUMEQUAL), 199 | "OP_NUMEQUALVERIFY" => Ok(OP_NUMEQUALVERIFY), 200 | "OP_NUMNOTEQUAL" => Ok(OP_NUMNOTEQUAL), 201 | "OP_LESSTHAN" => Ok(OP_LESSTHAN), 202 | "OP_GREATERTHAN" => Ok(OP_GREATERTHAN), 203 | "OP_LESSTHANOREQUAL" => Ok(OP_LESSTHANOREQUAL), 204 | "OP_GREATERTHANOREQUAL" => Ok(OP_GREATERTHANOREQUAL), 205 | "OP_MIN" => Ok(OP_MIN), 206 | "OP_MAX" => Ok(OP_MAX), 207 | "OP_WITHIN" => Ok(OP_WITHIN), 208 | "OP_RIPEMD160" => Ok(OP_RIPEMD160), 209 | "OP_SHA1" => Ok(OP_SHA1), 210 | "OP_SHA256" => Ok(OP_SHA256), 211 | "OP_HASH160" => Ok(OP_HASH160), 212 | "OP_HASH256" => Ok(OP_HASH256), 213 | "OP_CODESEPARATOR" => Ok(OP_CODESEPARATOR), 214 | "OP_CHECKSIG" => Ok(OP_CHECKSIG), 215 | "OP_CHECKSIGVERIFY" => Ok(OP_CHECKSIGVERIFY), 216 | "OP_CHECKMULTISIG" => Ok(OP_CHECKMULTISIG), 217 | "OP_CHECKMULTISIGVERIFY" => Ok(OP_CHECKMULTISIGVERIFY), 218 | "OP_NOP1" => Ok(OP_NOP1), 219 | "OP_CLTV" => Ok(OP_CLTV), 220 | "OP_CSV" => Ok(OP_CSV), 221 | "OP_NOP4" => Ok(OP_NOP4), 222 | "OP_NOP5" => Ok(OP_NOP5), 223 | "OP_NOP6" => Ok(OP_NOP6), 224 | "OP_NOP7" => Ok(OP_NOP7), 225 | "OP_NOP8" => Ok(OP_NOP8), 226 | "OP_NOP9" => Ok(OP_NOP9), 227 | "OP_NOP10" => Ok(OP_NOP10), 228 | "OP_CHECKSIGADD" => Ok(OP_CHECKSIGADD), 229 | 230 | // RETURN variants 231 | "OP_RETURN_187" => Ok(OP_RETURN_187), 232 | "OP_RETURN_188" => Ok(OP_RETURN_188), 233 | "OP_RETURN_189" => Ok(OP_RETURN_189), 234 | "OP_RETURN_190" => Ok(OP_RETURN_190), 235 | "OP_RETURN_191" => Ok(OP_RETURN_191), 236 | "OP_RETURN_192" => Ok(OP_RETURN_192), 237 | "OP_RETURN_193" => Ok(OP_RETURN_193), 238 | "OP_RETURN_194" => Ok(OP_RETURN_194), 239 | "OP_RETURN_195" => Ok(OP_RETURN_195), 240 | "OP_RETURN_196" => Ok(OP_RETURN_196), 241 | "OP_RETURN_197" => Ok(OP_RETURN_197), 242 | "OP_RETURN_198" => Ok(OP_RETURN_198), 243 | "OP_RETURN_199" => Ok(OP_RETURN_199), 244 | "OP_RETURN_200" => Ok(OP_RETURN_200), 245 | "OP_RETURN_201" => Ok(OP_RETURN_201), 246 | "OP_RETURN_202" => Ok(OP_RETURN_202), 247 | "OP_RETURN_203" => Ok(OP_RETURN_203), 248 | "OP_RETURN_204" => Ok(OP_RETURN_204), 249 | "OP_RETURN_205" => Ok(OP_RETURN_205), 250 | "OP_RETURN_206" => Ok(OP_RETURN_206), 251 | "OP_RETURN_207" => Ok(OP_RETURN_207), 252 | "OP_RETURN_208" => Ok(OP_RETURN_208), 253 | "OP_RETURN_209" => Ok(OP_RETURN_209), 254 | "OP_RETURN_210" => Ok(OP_RETURN_210), 255 | "OP_RETURN_211" => Ok(OP_RETURN_211), 256 | "OP_RETURN_212" => Ok(OP_RETURN_212), 257 | "OP_RETURN_213" => Ok(OP_RETURN_213), 258 | "OP_RETURN_214" => Ok(OP_RETURN_214), 259 | "OP_RETURN_215" => Ok(OP_RETURN_215), 260 | "OP_RETURN_216" => Ok(OP_RETURN_216), 261 | "OP_RETURN_217" => Ok(OP_RETURN_217), 262 | "OP_RETURN_218" => Ok(OP_RETURN_218), 263 | "OP_RETURN_219" => Ok(OP_RETURN_219), 264 | "OP_RETURN_220" => Ok(OP_RETURN_220), 265 | "OP_RETURN_221" => Ok(OP_RETURN_221), 266 | "OP_RETURN_222" => Ok(OP_RETURN_222), 267 | "OP_RETURN_223" => Ok(OP_RETURN_223), 268 | "OP_RETURN_224" => Ok(OP_RETURN_224), 269 | "OP_RETURN_225" => Ok(OP_RETURN_225), 270 | "OP_RETURN_226" => Ok(OP_RETURN_226), 271 | "OP_RETURN_227" => Ok(OP_RETURN_227), 272 | "OP_RETURN_228" => Ok(OP_RETURN_228), 273 | "OP_RETURN_229" => Ok(OP_RETURN_229), 274 | "OP_RETURN_230" => Ok(OP_RETURN_230), 275 | "OP_RETURN_231" => Ok(OP_RETURN_231), 276 | "OP_RETURN_232" => Ok(OP_RETURN_232), 277 | "OP_RETURN_233" => Ok(OP_RETURN_233), 278 | "OP_RETURN_234" => Ok(OP_RETURN_234), 279 | "OP_RETURN_235" => Ok(OP_RETURN_235), 280 | "OP_RETURN_236" => Ok(OP_RETURN_236), 281 | "OP_RETURN_237" => Ok(OP_RETURN_237), 282 | "OP_RETURN_238" => Ok(OP_RETURN_238), 283 | "OP_RETURN_239" => Ok(OP_RETURN_239), 284 | "OP_RETURN_240" => Ok(OP_RETURN_240), 285 | "OP_RETURN_241" => Ok(OP_RETURN_241), 286 | "OP_RETURN_242" => Ok(OP_RETURN_242), 287 | "OP_RETURN_243" => Ok(OP_RETURN_243), 288 | "OP_RETURN_244" => Ok(OP_RETURN_244), 289 | "OP_RETURN_245" => Ok(OP_RETURN_245), 290 | "OP_RETURN_246" => Ok(OP_RETURN_246), 291 | "OP_RETURN_247" => Ok(OP_RETURN_247), 292 | "OP_RETURN_248" => Ok(OP_RETURN_248), 293 | "OP_RETURN_249" => Ok(OP_RETURN_249), 294 | "OP_RETURN_250" => Ok(OP_RETURN_250), 295 | "OP_RETURN_251" => Ok(OP_RETURN_251), 296 | "OP_RETURN_252" => Ok(OP_RETURN_252), 297 | "OP_RETURN_253" => Ok(OP_RETURN_253), 298 | "OP_RETURN_254" => Ok(OP_RETURN_254), 299 | "OP_INVALIDOPCODE" => Ok(OP_INVALIDOPCODE), 300 | 301 | _ => Err(()), 302 | } 303 | } 304 | } 305 | } 306 | 307 | #[cfg(test)] 308 | mod tests { 309 | use super::*; 310 | 311 | #[test] 312 | fn test_basic_opcodes() { 313 | assert_eq!(parse_opcode("OP_0").unwrap(), OP_0); 314 | assert_eq!(parse_opcode("OP_FALSE").unwrap(), OP_FALSE); 315 | assert_eq!(parse_opcode("FALSE").unwrap(), OP_FALSE); 316 | assert_eq!(parse_opcode("OP_TRUE").unwrap(), OP_TRUE); 317 | assert_eq!(parse_opcode("TRUE").unwrap(), OP_TRUE); 318 | } 319 | 320 | #[test] 321 | fn test_numeric_opcodes() { 322 | assert_eq!(parse_opcode("OP_1").unwrap(), OP_PUSHNUM_1); 323 | assert_eq!(parse_opcode("OP_2").unwrap(), OP_PUSHNUM_2); 324 | assert_eq!(parse_opcode("OP_3").unwrap(), OP_PUSHNUM_3); 325 | assert_eq!(parse_opcode("OP_16").unwrap(), OP_PUSHNUM_16); 326 | } 327 | 328 | #[test] 329 | fn test_pushbytes() { 330 | assert_eq!(parse_opcode("OP_PUSHBYTES_0").unwrap(), OP_PUSHBYTES_0); 331 | assert_eq!(parse_opcode("OP_PUSHBYTES_1").unwrap(), OP_PUSHBYTES_1); 332 | assert_eq!(parse_opcode("OP_PUSHBYTES_75").unwrap(), OP_PUSHBYTES_75); 333 | } 334 | 335 | #[test] 336 | fn test_conditional_opcodes() { 337 | assert_eq!(parse_opcode("OP_IF").unwrap(), OP_IF); 338 | assert_eq!(parse_opcode("OP_NOTIF").unwrap(), OP_NOTIF); 339 | assert_eq!(parse_opcode("OP_ELSE").unwrap(), OP_ELSE); 340 | assert_eq!(parse_opcode("OP_ENDIF").unwrap(), OP_ENDIF); 341 | } 342 | 343 | #[test] 344 | fn test_stack_opcodes() { 345 | assert_eq!(parse_opcode("OP_DUP").unwrap(), OP_DUP); 346 | assert_eq!(parse_opcode("OP_DROP").unwrap(), OP_DROP); 347 | assert_eq!(parse_opcode("OP_SWAP").unwrap(), OP_SWAP); 348 | assert_eq!(parse_opcode("OP_RETURN").unwrap(), OP_RETURN); 349 | } 350 | 351 | #[test] 352 | fn test_alt_names() { 353 | assert_eq!(parse_opcode("DUP").unwrap(), OP_DUP); 354 | assert_eq!(parse_opcode("DROP").unwrap(), OP_DROP); 355 | assert_eq!(parse_opcode("SWAP").unwrap(), OP_SWAP); 356 | } 357 | 358 | #[test] 359 | fn test_invalid_opcodes() { 360 | assert!(parse_opcode("OP_INVALID").is_err()); 361 | assert!(parse_opcode("INVALID").is_err()); 362 | assert!(parse_opcode("").is_err()); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/signatures.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::secp256k1::{self, PublicKey, XOnlyPublicKey}; 2 | use bitcoin::sighash::{Annex, EcdsaSighashType, Prevouts, TapSighashType}; 3 | 4 | use crate::*; 5 | 6 | lazy_static::lazy_static! { 7 | static ref SECP: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); 8 | } 9 | 10 | impl Exec { 11 | pub fn check_sig_ecdsa(&mut self, sig: &[u8], pk: &[u8], script_code: &[u8]) -> bool { 12 | let pk = match PublicKey::from_slice(pk) { 13 | Ok(pk) => pk, 14 | Err(_) => return false, 15 | }; 16 | 17 | if sig.is_empty() { 18 | return false; 19 | } 20 | 21 | let hashtype = *sig.last().unwrap(); 22 | let sig = match secp256k1::ecdsa::Signature::from_der(&sig[0..sig.len() - 1]) { 23 | Ok(s) => s, 24 | Err(_) => return false, 25 | }; 26 | 27 | let sighash = if self.ctx == ExecCtx::SegwitV0 { 28 | self.sighashcache 29 | .p2wsh_signature_hash( 30 | self.tx.input_idx, 31 | Script::from_bytes(script_code), 32 | self.tx.prevouts[self.tx.input_idx].value, 33 | //TODO(stevenroose) this might not actually emulate consensus behavior 34 | EcdsaSighashType::from_consensus(hashtype as u32), 35 | ) 36 | .expect("only happens on prevout index out of bounds") 37 | .into() 38 | } else if self.ctx == ExecCtx::Legacy { 39 | self.sighashcache 40 | .legacy_signature_hash( 41 | self.tx.input_idx, 42 | Script::from_bytes(script_code), 43 | hashtype as u32, 44 | ) 45 | .expect("TODO(stevenroose) seems to only happen if prevout index out of bound") 46 | .into() 47 | } else { 48 | unreachable!(); 49 | }; 50 | 51 | SECP.verify_ecdsa(&sighash, &sig, &pk).is_ok() 52 | } 53 | 54 | /// [pk] should be passed as 32-bytes. 55 | pub fn check_sig_schnorr(&mut self, sig: &[u8], pk: &[u8]) -> Result<(), ExecError> { 56 | assert_eq!(pk.len(), 32); 57 | 58 | if sig.len() != 64 && sig.len() != 65 { 59 | return Err(ExecError::SchnorrSigSize); 60 | } 61 | 62 | let pk = XOnlyPublicKey::from_slice(pk).expect("TODO(stevenroose) what to do here?"); 63 | let (sig, hashtype) = if sig.len() == 65 { 64 | let b = *sig.last().unwrap(); 65 | let sig = secp256k1::schnorr::Signature::from_slice(&sig[0..sig.len() - 1]) 66 | .map_err(|_| ExecError::SchnorrSig)?; 67 | 68 | if b == TapSighashType::Default as u8 { 69 | return Err(ExecError::SchnorrSigHashtype); 70 | } 71 | //TODO(stevenroose) core does not error here 72 | let sht = 73 | TapSighashType::from_consensus_u8(b).map_err(|_| ExecError::SchnorrSigHashtype)?; 74 | (sig, sht) 75 | } else { 76 | let sig = secp256k1::schnorr::Signature::from_slice(sig) 77 | .map_err(|_| ExecError::SchnorrSig)?; 78 | (sig, TapSighashType::Default) 79 | }; 80 | 81 | let (leaf_hash, annex) = self.tx.taproot_annex_scriptleaf.as_ref().unwrap(); 82 | let sighash = self 83 | .sighashcache 84 | .taproot_signature_hash( 85 | self.tx.input_idx, 86 | &Prevouts::All(&self.tx.prevouts), 87 | annex 88 | .as_ref() 89 | .map(|a| Annex::new(a).expect("we checked annex prefix before")), 90 | Some((*leaf_hash, self.last_codeseparator_pos.unwrap_or(u32::MAX))), 91 | hashtype, 92 | ) 93 | .expect("TODO(stevenroose) seems to only happen if prevout index out of bound"); 94 | 95 | if SECP.verify_schnorr(&sig, &sighash.into(), &pk) != Ok(()) { 96 | return Err(ExecError::SchnorrSig); 97 | } 98 | 99 | Ok(()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | /// A data type to abstract out the condition stack during script execution. 2 | /// 3 | /// Conceptually it acts like a vector of booleans, one for each level of nested 4 | /// IF/THEN/ELSE, indicating whether we're in the active or inactive branch of 5 | /// each. 6 | /// 7 | /// The elements on the stack cannot be observed individually; we only need to 8 | /// expose whether the stack is empty and whether or not any false values are 9 | /// present at all. To implement OP_ELSE, a toggle_top modifier is added, which 10 | /// flips the last value without returning it. 11 | /// 12 | /// This uses an optimized implementation that does not materialize the 13 | /// actual stack. Instead, it just stores the size of the would-be stack, 14 | /// and the position of the first false value in it. 15 | pub struct ConditionStack { 16 | /// The size of the implied stack. 17 | size: usize, 18 | /// The position of the first false value on the implied stack, 19 | /// or NO_FALSE if all true. 20 | first_false_pos: usize, 21 | } 22 | 23 | impl ConditionStack { 24 | /// A constant for first_false_pos to indicate there are no falses. 25 | const NO_FALSE: usize = usize::MAX; 26 | 27 | pub fn new() -> Self { 28 | Self { 29 | size: 0, 30 | first_false_pos: Self::NO_FALSE, 31 | } 32 | } 33 | 34 | pub fn all_true(&self) -> bool { 35 | self.first_false_pos == Self::NO_FALSE 36 | } 37 | 38 | pub fn push(&mut self, v: bool) { 39 | if self.first_false_pos == Self::NO_FALSE && !v { 40 | // The stack consists of all true values, and a false is added. 41 | // The first false value will appear at the current size. 42 | self.first_false_pos = self.size; 43 | } 44 | self.size += 1; 45 | } 46 | 47 | /// Returns [false] if it was empty, [true] otherwise. 48 | /// 49 | /// Note that the popped value is not returned. 50 | pub fn pop(&mut self) -> bool { 51 | if self.size == 0 { 52 | false 53 | } else { 54 | self.size -= 1; 55 | if self.first_false_pos == self.size { 56 | // When popping off the first false value, everything becomes true. 57 | self.first_false_pos = Self::NO_FALSE; 58 | } 59 | true 60 | } 61 | } 62 | 63 | pub fn toggle_top(&mut self) -> bool { 64 | if self.size == 0 { 65 | false 66 | } else { 67 | if self.first_false_pos == Self::NO_FALSE { 68 | // The current stack is all true values; the first false will be the top. 69 | self.first_false_pos = self.size - 1; 70 | } else if self.first_false_pos == self.size - 1 { 71 | // The top is the first false value; toggling it will make everything true. 72 | self.first_false_pos = Self::NO_FALSE; 73 | } else { 74 | // There is a false value, but not on top. No action is needed as toggling 75 | // anything but the first false value is unobservable. 76 | } 77 | true 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/wasm.rs: -------------------------------------------------------------------------------- 1 | use bitcoin::hashes::Hash; 2 | use bitcoin::hex::{DisplayHex, FromHex}; 3 | use bitcoin::taproot::TapLeafHash; 4 | use bitcoin::{ScriptBuf, Transaction}; 5 | use serde_json::json; 6 | use wasm_bindgen::prelude::*; 7 | 8 | use crate::*; 9 | 10 | /// Compile ASM into script hex. 11 | #[wasm_bindgen] 12 | pub fn script_asm_to_hex(script_asm: &str) -> Result { 13 | let script = 14 | ScriptBuf::from_asm(script_asm).map_err(|e| format!("error parsing script: {:?}", e))?; 15 | Ok(script.as_bytes().as_hex().to_string()) 16 | } 17 | 18 | /// Decode compiled script hex into ASM. 19 | #[wasm_bindgen] 20 | pub fn script_hex_to_asm(script_hex: &str) -> Result { 21 | let script = ScriptBuf::from_hex(script_hex).map_err(|e| format!("invalid hex: {}", e))?; 22 | Ok(script.to_asm_string()) 23 | } 24 | 25 | /// Run the given script. 26 | /// 27 | /// Fields on the return value are: 28 | /// - success: bool 29 | /// - final_stack: list of hex stack items after execution 30 | /// - error: (optional) error that caused execution halt 31 | /// - last_opcode: (optional) last opcode run before error produced 32 | /// - stats: execution runtime statistics with following fields: 33 | /// - max_nb_stack_items 34 | /// - max_stack_size 35 | /// - max_stack_item_size 36 | /// - start_validation_weight 37 | /// - validation_weight 38 | #[wasm_bindgen] 39 | pub fn run_script(script_hex: &str, script_witness: Box<[JsValue]>) -> Result { 40 | console_error_panic_hook::set_once(); 41 | 42 | let script = 43 | ScriptBuf::from_hex(script_hex).map_err(|e| format!("invalid hex script: {:?}", e))?; 44 | let witness = { 45 | let mut ret = Vec::with_capacity(script_witness.len()); 46 | for item in script_witness.iter() { 47 | let hex = item 48 | .as_string() 49 | .ok_or("script witness must be list of hex strings")?; 50 | let bytes = Vec::from_hex(&hex).map_err(|_| "invalid hex in script witness")?; 51 | ret.push(bytes); 52 | } 53 | ret 54 | }; 55 | 56 | let mut exec = Exec::new( 57 | ExecCtx::Tapscript, 58 | Options::default(), 59 | TxTemplate { 60 | tx: Transaction { 61 | version: bitcoin::transaction::Version::TWO, 62 | lock_time: bitcoin::locktime::absolute::LockTime::ZERO, 63 | input: vec![], 64 | output: vec![], 65 | }, 66 | prevouts: vec![], 67 | input_idx: 0, 68 | taproot_annex_scriptleaf: Some((TapLeafHash::all_zeros(), None)), 69 | }, 70 | script, 71 | witness, 72 | ) 73 | .map_err(|e| format!("error creating exec: {:?}", e))?; 74 | 75 | loop { 76 | if let Err(res) = exec.exec_next() { 77 | let res = res.clone(); 78 | let mut ret = json!({ 79 | "success": res.success, 80 | "final_stack": res.final_stack.iter_str() 81 | .map(|i| i.as_hex().to_string()) 82 | .collect::>(), 83 | "stats": serde_json::to_value(exec.stats()).unwrap(), 84 | }); 85 | if !res.success { 86 | let obj = ret.as_object_mut().unwrap(); 87 | obj.insert( 88 | "last_opcode".into(), 89 | res.opcode.map(|o| o.to_string()).unwrap_or_default().into(), 90 | ); 91 | obj.insert( 92 | "error".into(), 93 | res.error 94 | .map(|o| format!("{:?}", o)) 95 | .unwrap_or_default() 96 | .into(), 97 | ); 98 | } 99 | 100 | return Ok(serde_wasm_bindgen::to_value(&ret).unwrap()); 101 | } 102 | } 103 | } 104 | --------------------------------------------------------------------------------