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 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
62opcodes! {
64 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 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 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 OP_CAT: 0x7e,
127 OP_SUBSTR: 0x7f,
128 OP_LEFT: 0x80,
129 OP_RIGHT: 0x81,
130 OP_SIZE: 0x82,
131
132 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 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 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 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 OP_CHECKSIGADD: 0xba,
202
203 OP_INVALIDOPCODE: 0xff,
204
205 OP_CLTV: 0xb1,
207 OP_CSV: 0xb2,
208
209 OP_INTERNAL_NOT: 0xfe,
213}
214
215impl Opcode {
216 pub fn is_internal(&self) -> bool {
219 matches!(*self, opcodes::OP_INTERNAL_NOT)
220 }
221
222 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 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 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 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 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}