bitcoin_script_analyzer/
opcode.rs

1use core::fmt;
2
3#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
4#[repr(transparent)]
5pub struct Opcode {
6    pub opcode: u8,
7}
8
9impl fmt::Display for Opcode {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        write!(f, "{}", self.name().unwrap_or("UNKNOWN"))
12    }
13}
14
15macro_rules! opcodes {
16    (
17        $($k:ident:$v:literal,)*
18    ) => {
19        pub mod opcodes {
20            use super::Opcode;
21
22            $(
23                #[allow(dead_code)]
24                pub const $k: Opcode = Opcode { opcode: $v };
25            )*
26        }
27
28        impl Opcode {
29            pub fn from_name_exact(name: &str) -> Option<Self> {
30                $(
31                    if name == stringify!($k) {
32                        let op = Opcode { opcode: $v };
33
34                        if !op.is_internal() {
35                            Some(op)
36                        } else {
37                            None
38                        }
39                    } else
40                )* {
41                    None
42                }
43            }
44
45            pub fn name(self) -> Option<&'static str> {
46                if self.is_internal() {
47                    // TODO display internal opcodes?
48                    return None;
49                }
50                match self.opcode {
51                    $(
52                        #[allow(unreachable_patterns)]
53                        $v => Some(stringify!($k)),
54                    )*
55                    _ => None,
56                }
57            }
58        }
59    };
60}
61
62// Opcode names mapped to their opcode.
63opcodes! {
64    // https://github.com/bitcoin/bitcoin/blob/fa5c896724bb359b4b9a3f89580272bfe5980c1b/src/script/script.h#L65-L206
65
66    // push value
67    OP_0: 0x00,
68    OP_FALSE: 0x00,
69    OP_PUSHDATA1: 0x4c,
70    OP_PUSHDATA2: 0x4d,
71    OP_PUSHDATA4: 0x4e,
72    OP_1NEGATE: 0x4f,
73    OP_RESERVED: 0x50,
74    OP_1: 0x51,
75    OP_TRUE: 0x51,
76    OP_2: 0x52,
77    OP_3: 0x53,
78    OP_4: 0x54,
79    OP_5: 0x55,
80    OP_6: 0x56,
81    OP_7: 0x57,
82    OP_8: 0x58,
83    OP_9: 0x59,
84    OP_10: 0x5a,
85    OP_11: 0x5b,
86    OP_12: 0x5c,
87    OP_13: 0x5d,
88    OP_14: 0x5e,
89    OP_15: 0x5f,
90    OP_16: 0x60,
91
92    // control
93    OP_NOP: 0x61,
94    OP_VER: 0x62,
95    OP_IF: 0x63,
96    OP_NOTIF: 0x64,
97    OP_VERIF: 0x65,
98    OP_VERNOTIF: 0x66,
99    OP_ELSE: 0x67,
100    OP_ENDIF: 0x68,
101    OP_VERIFY: 0x69,
102    OP_RETURN: 0x6a,
103
104    // stack ops
105    OP_TOALTSTACK: 0x6b,
106    OP_FROMALTSTACK: 0x6c,
107    OP_2DROP: 0x6d,
108    OP_2DUP: 0x6e,
109    OP_3DUP: 0x6f,
110    OP_2OVER: 0x70,
111    OP_2ROT: 0x71,
112    OP_2SWAP: 0x72,
113    OP_IFDUP: 0x73,
114    OP_DEPTH: 0x74,
115    OP_DROP: 0x75,
116    OP_DUP: 0x76,
117    OP_NIP: 0x77,
118    OP_OVER: 0x78,
119    OP_PICK: 0x79,
120    OP_ROLL: 0x7a,
121    OP_ROT: 0x7b,
122    OP_SWAP: 0x7c,
123    OP_TUCK: 0x7d,
124
125    // splice ops
126    OP_CAT: 0x7e,
127    OP_SUBSTR: 0x7f,
128    OP_LEFT: 0x80,
129    OP_RIGHT: 0x81,
130    OP_SIZE: 0x82,
131
132    // bit logic
133    OP_INVERT: 0x83,
134    OP_AND: 0x84,
135    OP_OR: 0x85,
136    OP_XOR: 0x86,
137    OP_EQUAL: 0x87,
138    OP_EQUALVERIFY: 0x88,
139    OP_RESERVED1: 0x89,
140    OP_RESERVED2: 0x8a,
141
142    // numeric
143    OP_1ADD: 0x8b,
144    OP_1SUB: 0x8c,
145    OP_2MUL: 0x8d,
146    OP_2DIV: 0x8e,
147    OP_NEGATE: 0x8f,
148    OP_ABS: 0x90,
149    OP_NOT: 0x91,
150    OP_0NOTEQUAL: 0x92,
151
152    OP_ADD: 0x93,
153    OP_SUB: 0x94,
154    OP_MUL: 0x95,
155    OP_DIV: 0x96,
156    OP_MOD: 0x97,
157    OP_LSHIFT: 0x98,
158    OP_RSHIFT: 0x99,
159
160    OP_BOOLAND: 0x9a,
161    OP_BOOLOR: 0x9b,
162    OP_NUMEQUAL: 0x9c,
163    OP_NUMEQUALVERIFY: 0x9d,
164    OP_NUMNOTEQUAL: 0x9e,
165    OP_LESSTHAN: 0x9f,
166    OP_GREATERTHAN: 0xa0,
167    OP_LESSTHANOREQUAL: 0xa1,
168    OP_GREATERTHANOREQUAL: 0xa2,
169    OP_MIN: 0xa3,
170    OP_MAX: 0xa4,
171
172    OP_WITHIN: 0xa5,
173
174    // crypto
175    OP_RIPEMD160: 0xa6,
176    OP_SHA1: 0xa7,
177    OP_SHA256: 0xa8,
178    OP_HASH160: 0xa9,
179    OP_HASH256: 0xaa,
180    OP_CODESEPARATOR: 0xab,
181    OP_CHECKSIG: 0xac,
182    OP_CHECKSIGVERIFY: 0xad,
183    OP_CHECKMULTISIG: 0xae,
184    OP_CHECKMULTISIGVERIFY: 0xaf,
185
186    // expansion
187    OP_NOP1: 0xb0,
188    OP_CHECKLOCKTIMEVERIFY: 0xb1,
189    OP_NOP2: 0xb1,
190    OP_CHECKSEQUENCEVERIFY: 0xb2,
191    OP_NOP3: 0xb2,
192    OP_NOP4: 0xb3,
193    OP_NOP5: 0xb4,
194    OP_NOP6: 0xb5,
195    OP_NOP7: 0xb6,
196    OP_NOP8: 0xb7,
197    OP_NOP9: 0xb8,
198    OP_NOP10: 0xb9,
199
200    // Opcode added by BIP 342 (Tapscript)
201    OP_CHECKSIGADD: 0xba,
202
203    OP_INVALIDOPCODE: 0xff,
204
205    // aliases
206    OP_CLTV: 0xb1,
207    OP_CSV: 0xb2,
208
209    // internal opcodes (not used in bitcoin core)
210    // Unlike OP_NOT, INTERNAL_NOT does not require the input to be max 4 bytes.
211    // Equivalent to IF 0 ELSE 1 ENDIF without minimal if
212    OP_INTERNAL_NOT: 0xfe,
213}
214
215impl Opcode {
216    /// Negative numbers are used for _internal_ opcodes.
217    /// These opcodes **do not** exist in bitcoin and are used in scriptanalyzer to accurately mimic the behavior of other opcodes.
218    pub fn is_internal(&self) -> bool {
219        matches!(*self, opcodes::OP_INTERNAL_NOT)
220    }
221
222    /// Opcodes that were disabled because of CVE-2010-5137
223    pub fn is_disabled(&self) -> bool {
224        matches!(
225            *self,
226            opcodes::OP_CAT
227                | opcodes::OP_SUBSTR
228                | opcodes::OP_LEFT
229                | opcodes::OP_RIGHT
230                | opcodes::OP_INVERT
231                | opcodes::OP_AND
232                | opcodes::OP_OR
233                | opcodes::OP_XOR
234                | opcodes::OP_2MUL
235                | opcodes::OP_2DIV
236                | opcodes::OP_MUL
237                | opcodes::OP_DIV
238                | opcodes::OP_MOD
239                | opcodes::OP_LSHIFT
240                | opcodes::OP_RSHIFT
241        )
242    }
243
244    /// Opcodes that push data mapped to the length of the following number that indicated the push size.
245    /// Returns Some(length) for OP_PUSHDATA(1|2|4) and None for others.
246    pub fn pushdata_length(&self) -> Option<usize> {
247        Some(match *self {
248            opcodes::OP_PUSHDATA1 => 1,
249            opcodes::OP_PUSHDATA2 => 2,
250            opcodes::OP_PUSHDATA4 => 4,
251            _ => return None,
252        })
253    }
254
255    /// Opcodes that return <> or <01>
256    pub fn returns_boolean(&self) -> bool {
257        matches!(
258            *self,
259            opcodes::OP_EQUAL
260                | opcodes::OP_NOT
261                | opcodes::OP_0NOTEQUAL
262                | opcodes::OP_BOOLAND
263                | opcodes::OP_BOOLOR
264                | opcodes::OP_NUMEQUAL
265                | opcodes::OP_NUMNOTEQUAL
266                | opcodes::OP_LESSTHAN
267                | opcodes::OP_GREATERTHAN
268                | opcodes::OP_LESSTHANOREQUAL
269                | opcodes::OP_GREATERTHANOREQUAL
270                | opcodes::OP_WITHIN
271                | opcodes::OP_CHECKSIG
272                | opcodes::OP_CHECKMULTISIG
273                | opcodes::OP_INTERNAL_NOT
274        )
275    }
276
277    /// Opcodes that return max a number (5 bytes)
278    pub fn returns_number(&self) -> bool {
279        if self.returns_boolean() {
280            return true;
281        }
282
283        matches!(
284            *self,
285            opcodes::OP_SIZE
286                | opcodes::OP_NEGATE
287                | opcodes::OP_ABS
288                | opcodes::OP_ADD
289                | opcodes::OP_SUB
290                | opcodes::OP_MIN
291                | opcodes::OP_MAX
292        )
293    }
294
295    pub fn can_reorder_args(&self) -> bool {
296        !matches!(
297            *self,
298            opcodes::OP_SUB
299                | opcodes::OP_LESSTHAN
300                | opcodes::OP_GREATERTHAN
301                | opcodes::OP_LESSTHANOREQUAL
302                | opcodes::OP_GREATERTHANOREQUAL
303                | opcodes::OP_WITHIN
304                | opcodes::OP_CHECKSIG
305                | opcodes::OP_CHECKMULTISIG
306        )
307    }
308
309    pub fn from_name(name: &str) -> Option<Self> {
310        // TODO replace with more efficient implementation of to_uppercase
311        let name_upper = name.to_uppercase();
312        if let Some(opcode) = Self::from_name_exact(&name_upper) {
313            return Some(opcode);
314        }
315
316        Self::from_name_exact(&("OP_".to_string() + &name_upper))
317    }
318}
319
320pub enum OpcodeType {
321    Data,
322    Number,
323    Constant,
324    Flow,
325    Stack,
326    Splice,
327    Bitwise,
328    Arithmetic,
329    Crypto,
330    Locktime,
331    Disabled,
332    Invalid,
333}
334
335impl Opcode {
336    pub fn opcode_type(&self) -> OpcodeType {
337        let op = *self;
338        if op.is_disabled() {
339            OpcodeType::Disabled
340        } else if op == opcodes::OP_VER || op == opcodes::OP_VERIF || op == opcodes::OP_VERNOTIF {
341            OpcodeType::Invalid
342        } else if op >= opcodes::OP_0 && op <= opcodes::OP_PUSHDATA4 {
343            OpcodeType::Constant
344        } else if op >= opcodes::OP_NOP && op <= opcodes::OP_RETURN {
345            OpcodeType::Flow
346        } else if op >= opcodes::OP_TOALTSTACK && op <= opcodes::OP_TUCK {
347            OpcodeType::Stack
348        } else if op >= opcodes::OP_CAT && op <= opcodes::OP_SIZE {
349            OpcodeType::Splice
350        } else if op >= opcodes::OP_INVERT && op <= opcodes::OP_EQUALVERIFY {
351            OpcodeType::Bitwise
352        } else if op >= opcodes::OP_1ADD && op <= opcodes::OP_WITHIN {
353            OpcodeType::Arithmetic
354        } else if (op >= opcodes::OP_RIPEMD160 && op <= opcodes::OP_CHECKMULTISIGVERIFY)
355            || op == opcodes::OP_CHECKSIGADD
356        {
357            OpcodeType::Crypto
358        } else if op >= opcodes::OP_CHECKLOCKTIMEVERIFY && op <= opcodes::OP_CHECKSEQUENCEVERIFY {
359            OpcodeType::Locktime
360        } else {
361            OpcodeType::Invalid
362        }
363    }
364}