Skip to main content

bsv/script/
op.rs

1//! Bitcoin Script opcodes.
2//!
3//! All 214 BSV opcodes as a `#[repr(u8)]` enum. Unknown byte values
4//! map to `OpInvalidOpcode` via `From<u8>`.
5
6/// All BSV script opcodes with their byte values.
7///
8/// Variant names use TS-SDK style: `Op::OpDup`, `Op::OpCheckSig`, etc.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[repr(u8)]
11#[allow(non_camel_case_types)]
12pub enum Op {
13    // push value
14    Op0 = 0x00,
15    OpPushData1 = 0x4c,
16    OpPushData2 = 0x4d,
17    OpPushData4 = 0x4e,
18    Op1Negate = 0x4f,
19    OpReserved = 0x50,
20    Op1 = 0x51,
21    Op2 = 0x52,
22    Op3 = 0x53,
23    Op4 = 0x54,
24    Op5 = 0x55,
25    Op6 = 0x56,
26    Op7 = 0x57,
27    Op8 = 0x58,
28    Op9 = 0x59,
29    Op10 = 0x5a,
30    Op11 = 0x5b,
31    Op12 = 0x5c,
32    Op13 = 0x5d,
33    Op14 = 0x5e,
34    Op15 = 0x5f,
35    Op16 = 0x60,
36
37    // control
38    OpNop = 0x61,
39    OpVer = 0x62,
40    OpIf = 0x63,
41    OpNotIf = 0x64,
42    OpVerIf = 0x65,
43    OpVerNotIf = 0x66,
44    OpElse = 0x67,
45    OpEndIf = 0x68,
46    OpVerify = 0x69,
47    OpReturn = 0x6a,
48
49    // stack ops
50    OpToAltStack = 0x6b,
51    OpFromAltStack = 0x6c,
52    Op2Drop = 0x6d,
53    Op2Dup = 0x6e,
54    Op3Dup = 0x6f,
55    Op2Over = 0x70,
56    Op2Rot = 0x71,
57    Op2Swap = 0x72,
58    OpIfDup = 0x73,
59    OpDepth = 0x74,
60    OpDrop = 0x75,
61    OpDup = 0x76,
62    OpNip = 0x77,
63    OpOver = 0x78,
64    OpPick = 0x79,
65    OpRoll = 0x7a,
66    OpRot = 0x7b,
67    OpSwap = 0x7c,
68    OpTuck = 0x7d,
69
70    // data manipulation ops (BSV-restored)
71    OpCat = 0x7e,
72    OpSplit = 0x7f,
73    OpNum2Bin = 0x80,
74    OpBin2Num = 0x81,
75    OpSize = 0x82,
76
77    // bit logic
78    OpInvert = 0x83,
79    OpAnd = 0x84,
80    OpOr = 0x85,
81    OpXor = 0x86,
82    OpEqual = 0x87,
83    OpEqualVerify = 0x88,
84    OpReserved1 = 0x89,
85    OpReserved2 = 0x8a,
86
87    // numeric
88    Op1Add = 0x8b,
89    Op1Sub = 0x8c,
90    Op2Mul = 0x8d,
91    Op2Div = 0x8e,
92    OpNegate = 0x8f,
93    OpAbs = 0x90,
94    OpNot = 0x91,
95    Op0NotEqual = 0x92,
96    OpAdd = 0x93,
97    OpSub = 0x94,
98    OpMul = 0x95,
99    OpDiv = 0x96,
100    OpMod = 0x97,
101    OpLShift = 0x98,
102    OpRShift = 0x99,
103    OpBoolAnd = 0x9a,
104    OpBoolOr = 0x9b,
105    OpNumEqual = 0x9c,
106    OpNumEqualVerify = 0x9d,
107    OpNumNotEqual = 0x9e,
108    OpLessThan = 0x9f,
109    OpGreaterThan = 0xa0,
110    OpLessThanOrEqual = 0xa1,
111    OpGreaterThanOrEqual = 0xa2,
112    OpMin = 0xa3,
113    OpMax = 0xa4,
114    OpWithin = 0xa5,
115
116    // crypto
117    OpRipemd160 = 0xa6,
118    OpSha1 = 0xa7,
119    OpSha256 = 0xa8,
120    OpHash160 = 0xa9,
121    OpHash256 = 0xaa,
122    OpCodeSeparator = 0xab,
123    OpCheckSig = 0xac,
124    OpCheckSigVerify = 0xad,
125    OpCheckMultiSig = 0xae,
126    OpCheckMultiSigVerify = 0xaf,
127
128    // expansion
129    OpNop1 = 0xb0,
130    OpNop2 = 0xb1,
131    OpNop3 = 0xb2,
132    // Chronicle 2026 opcodes (share byte values with NOP4-NOP8)
133    OpSubstr = 0xb3,
134    OpLeft = 0xb4,
135    OpRight = 0xb5,
136    OpLShiftNum = 0xb6,
137    OpRShiftNum = 0xb7,
138    OpNop9 = 0xb8,
139    OpNop10 = 0xb9,
140    OpNop11 = 0xba,
141    OpNop12 = 0xbb,
142    OpNop13 = 0xbc,
143    OpNop14 = 0xbd,
144    OpNop15 = 0xbe,
145    OpNop16 = 0xbf,
146    OpNop17 = 0xc0,
147    OpNop18 = 0xc1,
148    OpNop19 = 0xc2,
149    OpNop20 = 0xc3,
150    OpNop21 = 0xc4,
151    OpNop22 = 0xc5,
152    OpNop23 = 0xc6,
153    OpNop24 = 0xc7,
154    OpNop25 = 0xc8,
155    OpNop26 = 0xc9,
156    OpNop27 = 0xca,
157    OpNop28 = 0xcb,
158    OpNop29 = 0xcc,
159    OpNop30 = 0xcd,
160    OpNop31 = 0xce,
161    OpNop32 = 0xcf,
162    OpNop33 = 0xd0,
163    OpNop34 = 0xd1,
164    OpNop35 = 0xd2,
165    OpNop36 = 0xd3,
166    OpNop37 = 0xd4,
167    OpNop38 = 0xd5,
168    OpNop39 = 0xd6,
169    OpNop40 = 0xd7,
170    OpNop41 = 0xd8,
171    OpNop42 = 0xd9,
172    OpNop43 = 0xda,
173    OpNop44 = 0xdb,
174    OpNop45 = 0xdc,
175    OpNop46 = 0xdd,
176    OpNop47 = 0xde,
177    OpNop48 = 0xdf,
178    OpNop49 = 0xe0,
179    OpNop50 = 0xe1,
180    OpNop51 = 0xe2,
181    OpNop52 = 0xe3,
182    OpNop53 = 0xe4,
183    OpNop54 = 0xe5,
184    OpNop55 = 0xe6,
185    OpNop56 = 0xe7,
186    OpNop57 = 0xe8,
187    OpNop58 = 0xe9,
188    OpNop59 = 0xea,
189    OpNop60 = 0xeb,
190    OpNop61 = 0xec,
191    OpNop62 = 0xed,
192    OpNop63 = 0xee,
193    OpNop64 = 0xef,
194    OpNop65 = 0xf0,
195    OpNop66 = 0xf1,
196    OpNop67 = 0xf2,
197    OpNop68 = 0xf3,
198    OpNop69 = 0xf4,
199    OpNop70 = 0xf5,
200    OpNop71 = 0xf6,
201    OpNop72 = 0xf7,
202    OpNop73 = 0xf8,
203
204    // template matching params
205    OpSmallData = 0xf9,
206    OpSmallInteger = 0xfa,
207    OpPubKeys = 0xfb,
208    OpNop77 = 0xfc,
209    OpPubKeyHash = 0xfd,
210    OpPubKey = 0xfe,
211
212    OpInvalidOpcode = 0xff,
213}
214
215impl Op {
216    /// Return the canonical name string, e.g. "OP_DUP", "OP_CHECKSIG".
217    pub fn to_name(&self) -> &'static str {
218        match self {
219            Op::Op0 => "OP_0",
220            Op::OpPushData1 => "OP_PUSHDATA1",
221            Op::OpPushData2 => "OP_PUSHDATA2",
222            Op::OpPushData4 => "OP_PUSHDATA4",
223            Op::Op1Negate => "OP_1NEGATE",
224            Op::OpReserved => "OP_RESERVED",
225            Op::Op1 => "OP_1",
226            Op::Op2 => "OP_2",
227            Op::Op3 => "OP_3",
228            Op::Op4 => "OP_4",
229            Op::Op5 => "OP_5",
230            Op::Op6 => "OP_6",
231            Op::Op7 => "OP_7",
232            Op::Op8 => "OP_8",
233            Op::Op9 => "OP_9",
234            Op::Op10 => "OP_10",
235            Op::Op11 => "OP_11",
236            Op::Op12 => "OP_12",
237            Op::Op13 => "OP_13",
238            Op::Op14 => "OP_14",
239            Op::Op15 => "OP_15",
240            Op::Op16 => "OP_16",
241            Op::OpNop => "OP_NOP",
242            Op::OpVer => "OP_VER",
243            Op::OpIf => "OP_IF",
244            Op::OpNotIf => "OP_NOTIF",
245            Op::OpVerIf => "OP_VERIF",
246            Op::OpVerNotIf => "OP_VERNOTIF",
247            Op::OpElse => "OP_ELSE",
248            Op::OpEndIf => "OP_ENDIF",
249            Op::OpVerify => "OP_VERIFY",
250            Op::OpReturn => "OP_RETURN",
251            Op::OpToAltStack => "OP_TOALTSTACK",
252            Op::OpFromAltStack => "OP_FROMALTSTACK",
253            Op::Op2Drop => "OP_2DROP",
254            Op::Op2Dup => "OP_2DUP",
255            Op::Op3Dup => "OP_3DUP",
256            Op::Op2Over => "OP_2OVER",
257            Op::Op2Rot => "OP_2ROT",
258            Op::Op2Swap => "OP_2SWAP",
259            Op::OpIfDup => "OP_IFDUP",
260            Op::OpDepth => "OP_DEPTH",
261            Op::OpDrop => "OP_DROP",
262            Op::OpDup => "OP_DUP",
263            Op::OpNip => "OP_NIP",
264            Op::OpOver => "OP_OVER",
265            Op::OpPick => "OP_PICK",
266            Op::OpRoll => "OP_ROLL",
267            Op::OpRot => "OP_ROT",
268            Op::OpSwap => "OP_SWAP",
269            Op::OpTuck => "OP_TUCK",
270            Op::OpCat => "OP_CAT",
271            Op::OpSplit => "OP_SPLIT",
272            Op::OpNum2Bin => "OP_NUM2BIN",
273            Op::OpBin2Num => "OP_BIN2NUM",
274            Op::OpSize => "OP_SIZE",
275            Op::OpInvert => "OP_INVERT",
276            Op::OpAnd => "OP_AND",
277            Op::OpOr => "OP_OR",
278            Op::OpXor => "OP_XOR",
279            Op::OpEqual => "OP_EQUAL",
280            Op::OpEqualVerify => "OP_EQUALVERIFY",
281            Op::OpReserved1 => "OP_RESERVED1",
282            Op::OpReserved2 => "OP_RESERVED2",
283            Op::Op1Add => "OP_1ADD",
284            Op::Op1Sub => "OP_1SUB",
285            Op::Op2Mul => "OP_2MUL",
286            Op::Op2Div => "OP_2DIV",
287            Op::OpNegate => "OP_NEGATE",
288            Op::OpAbs => "OP_ABS",
289            Op::OpNot => "OP_NOT",
290            Op::Op0NotEqual => "OP_0NOTEQUAL",
291            Op::OpAdd => "OP_ADD",
292            Op::OpSub => "OP_SUB",
293            Op::OpMul => "OP_MUL",
294            Op::OpDiv => "OP_DIV",
295            Op::OpMod => "OP_MOD",
296            Op::OpLShift => "OP_LSHIFT",
297            Op::OpRShift => "OP_RSHIFT",
298            Op::OpBoolAnd => "OP_BOOLAND",
299            Op::OpBoolOr => "OP_BOOLOR",
300            Op::OpNumEqual => "OP_NUMEQUAL",
301            Op::OpNumEqualVerify => "OP_NUMEQUALVERIFY",
302            Op::OpNumNotEqual => "OP_NUMNOTEQUAL",
303            Op::OpLessThan => "OP_LESSTHAN",
304            Op::OpGreaterThan => "OP_GREATERTHAN",
305            Op::OpLessThanOrEqual => "OP_LESSTHANOREQUAL",
306            Op::OpGreaterThanOrEqual => "OP_GREATERTHANOREQUAL",
307            Op::OpMin => "OP_MIN",
308            Op::OpMax => "OP_MAX",
309            Op::OpWithin => "OP_WITHIN",
310            Op::OpRipemd160 => "OP_RIPEMD160",
311            Op::OpSha1 => "OP_SHA1",
312            Op::OpSha256 => "OP_SHA256",
313            Op::OpHash160 => "OP_HASH160",
314            Op::OpHash256 => "OP_HASH256",
315            Op::OpCodeSeparator => "OP_CODESEPARATOR",
316            Op::OpCheckSig => "OP_CHECKSIG",
317            Op::OpCheckSigVerify => "OP_CHECKSIGVERIFY",
318            Op::OpCheckMultiSig => "OP_CHECKMULTISIG",
319            Op::OpCheckMultiSigVerify => "OP_CHECKMULTISIGVERIFY",
320            Op::OpNop1 => "OP_NOP1",
321            Op::OpNop2 => "OP_NOP2",
322            Op::OpNop3 => "OP_NOP3",
323            Op::OpSubstr => "OP_SUBSTR",
324            Op::OpLeft => "OP_LEFT",
325            Op::OpRight => "OP_RIGHT",
326            Op::OpLShiftNum => "OP_LSHIFTNUM",
327            Op::OpRShiftNum => "OP_RSHIFTNUM",
328            Op::OpNop9 => "OP_NOP9",
329            Op::OpNop10 => "OP_NOP10",
330            Op::OpNop11 => "OP_NOP11",
331            Op::OpNop12 => "OP_NOP12",
332            Op::OpNop13 => "OP_NOP13",
333            Op::OpNop14 => "OP_NOP14",
334            Op::OpNop15 => "OP_NOP15",
335            Op::OpNop16 => "OP_NOP16",
336            Op::OpNop17 => "OP_NOP17",
337            Op::OpNop18 => "OP_NOP18",
338            Op::OpNop19 => "OP_NOP19",
339            Op::OpNop20 => "OP_NOP20",
340            Op::OpNop21 => "OP_NOP21",
341            Op::OpNop22 => "OP_NOP22",
342            Op::OpNop23 => "OP_NOP23",
343            Op::OpNop24 => "OP_NOP24",
344            Op::OpNop25 => "OP_NOP25",
345            Op::OpNop26 => "OP_NOP26",
346            Op::OpNop27 => "OP_NOP27",
347            Op::OpNop28 => "OP_NOP28",
348            Op::OpNop29 => "OP_NOP29",
349            Op::OpNop30 => "OP_NOP30",
350            Op::OpNop31 => "OP_NOP31",
351            Op::OpNop32 => "OP_NOP32",
352            Op::OpNop33 => "OP_NOP33",
353            Op::OpNop34 => "OP_NOP34",
354            Op::OpNop35 => "OP_NOP35",
355            Op::OpNop36 => "OP_NOP36",
356            Op::OpNop37 => "OP_NOP37",
357            Op::OpNop38 => "OP_NOP38",
358            Op::OpNop39 => "OP_NOP39",
359            Op::OpNop40 => "OP_NOP40",
360            Op::OpNop41 => "OP_NOP41",
361            Op::OpNop42 => "OP_NOP42",
362            Op::OpNop43 => "OP_NOP43",
363            Op::OpNop44 => "OP_NOP44",
364            Op::OpNop45 => "OP_NOP45",
365            Op::OpNop46 => "OP_NOP46",
366            Op::OpNop47 => "OP_NOP47",
367            Op::OpNop48 => "OP_NOP48",
368            Op::OpNop49 => "OP_NOP49",
369            Op::OpNop50 => "OP_NOP50",
370            Op::OpNop51 => "OP_NOP51",
371            Op::OpNop52 => "OP_NOP52",
372            Op::OpNop53 => "OP_NOP53",
373            Op::OpNop54 => "OP_NOP54",
374            Op::OpNop55 => "OP_NOP55",
375            Op::OpNop56 => "OP_NOP56",
376            Op::OpNop57 => "OP_NOP57",
377            Op::OpNop58 => "OP_NOP58",
378            Op::OpNop59 => "OP_NOP59",
379            Op::OpNop60 => "OP_NOP60",
380            Op::OpNop61 => "OP_NOP61",
381            Op::OpNop62 => "OP_NOP62",
382            Op::OpNop63 => "OP_NOP63",
383            Op::OpNop64 => "OP_NOP64",
384            Op::OpNop65 => "OP_NOP65",
385            Op::OpNop66 => "OP_NOP66",
386            Op::OpNop67 => "OP_NOP67",
387            Op::OpNop68 => "OP_NOP68",
388            Op::OpNop69 => "OP_NOP69",
389            Op::OpNop70 => "OP_NOP70",
390            Op::OpNop71 => "OP_NOP71",
391            Op::OpNop72 => "OP_NOP72",
392            Op::OpNop73 => "OP_NOP73",
393            Op::OpSmallData => "OP_SMALLDATA",
394            Op::OpSmallInteger => "OP_SMALLINTEGER",
395            Op::OpPubKeys => "OP_PUBKEYS",
396            Op::OpNop77 => "OP_NOP77",
397            Op::OpPubKeyHash => "OP_PUBKEYHASH",
398            Op::OpPubKey => "OP_PUBKEY",
399            Op::OpInvalidOpcode => "OP_INVALIDOPCODE",
400        }
401    }
402
403    /// Return the raw byte value of this opcode.
404    pub fn to_byte(&self) -> u8 {
405        *self as u8
406    }
407
408    /// Look up an Op by its canonical name string (e.g. "OP_DUP").
409    /// Returns None if not found. Also handles aliases like "OP_FALSE"
410    /// and "OP_TRUE".
411    pub fn from_name(name: &str) -> Option<Op> {
412        match name {
413            "OP_0" | "OP_FALSE" => Some(Op::Op0),
414            "OP_PUSHDATA1" => Some(Op::OpPushData1),
415            "OP_PUSHDATA2" => Some(Op::OpPushData2),
416            "OP_PUSHDATA4" => Some(Op::OpPushData4),
417            "OP_1NEGATE" => Some(Op::Op1Negate),
418            "OP_RESERVED" => Some(Op::OpReserved),
419            "OP_1" | "OP_TRUE" => Some(Op::Op1),
420            "OP_2" => Some(Op::Op2),
421            "OP_3" => Some(Op::Op3),
422            "OP_4" => Some(Op::Op4),
423            "OP_5" => Some(Op::Op5),
424            "OP_6" => Some(Op::Op6),
425            "OP_7" => Some(Op::Op7),
426            "OP_8" => Some(Op::Op8),
427            "OP_9" => Some(Op::Op9),
428            "OP_10" => Some(Op::Op10),
429            "OP_11" => Some(Op::Op11),
430            "OP_12" => Some(Op::Op12),
431            "OP_13" => Some(Op::Op13),
432            "OP_14" => Some(Op::Op14),
433            "OP_15" => Some(Op::Op15),
434            "OP_16" => Some(Op::Op16),
435            "OP_NOP" => Some(Op::OpNop),
436            "OP_VER" => Some(Op::OpVer),
437            "OP_IF" => Some(Op::OpIf),
438            "OP_NOTIF" => Some(Op::OpNotIf),
439            "OP_VERIF" => Some(Op::OpVerIf),
440            "OP_VERNOTIF" => Some(Op::OpVerNotIf),
441            "OP_ELSE" => Some(Op::OpElse),
442            "OP_ENDIF" => Some(Op::OpEndIf),
443            "OP_VERIFY" => Some(Op::OpVerify),
444            "OP_RETURN" => Some(Op::OpReturn),
445            "OP_TOALTSTACK" => Some(Op::OpToAltStack),
446            "OP_FROMALTSTACK" => Some(Op::OpFromAltStack),
447            "OP_2DROP" => Some(Op::Op2Drop),
448            "OP_2DUP" => Some(Op::Op2Dup),
449            "OP_3DUP" => Some(Op::Op3Dup),
450            "OP_2OVER" => Some(Op::Op2Over),
451            "OP_2ROT" => Some(Op::Op2Rot),
452            "OP_2SWAP" => Some(Op::Op2Swap),
453            "OP_IFDUP" => Some(Op::OpIfDup),
454            "OP_DEPTH" => Some(Op::OpDepth),
455            "OP_DROP" => Some(Op::OpDrop),
456            "OP_DUP" => Some(Op::OpDup),
457            "OP_NIP" => Some(Op::OpNip),
458            "OP_OVER" => Some(Op::OpOver),
459            "OP_PICK" => Some(Op::OpPick),
460            "OP_ROLL" => Some(Op::OpRoll),
461            "OP_ROT" => Some(Op::OpRot),
462            "OP_SWAP" => Some(Op::OpSwap),
463            "OP_TUCK" => Some(Op::OpTuck),
464            "OP_CAT" => Some(Op::OpCat),
465            "OP_SPLIT" => Some(Op::OpSplit),
466            "OP_NUM2BIN" => Some(Op::OpNum2Bin),
467            "OP_BIN2NUM" => Some(Op::OpBin2Num),
468            "OP_SIZE" => Some(Op::OpSize),
469            "OP_INVERT" => Some(Op::OpInvert),
470            "OP_AND" => Some(Op::OpAnd),
471            "OP_OR" => Some(Op::OpOr),
472            "OP_XOR" => Some(Op::OpXor),
473            "OP_EQUAL" => Some(Op::OpEqual),
474            "OP_EQUALVERIFY" => Some(Op::OpEqualVerify),
475            "OP_RESERVED1" => Some(Op::OpReserved1),
476            "OP_RESERVED2" => Some(Op::OpReserved2),
477            "OP_1ADD" => Some(Op::Op1Add),
478            "OP_1SUB" => Some(Op::Op1Sub),
479            "OP_2MUL" => Some(Op::Op2Mul),
480            "OP_2DIV" => Some(Op::Op2Div),
481            "OP_NEGATE" => Some(Op::OpNegate),
482            "OP_ABS" => Some(Op::OpAbs),
483            "OP_NOT" => Some(Op::OpNot),
484            "OP_0NOTEQUAL" => Some(Op::Op0NotEqual),
485            "OP_ADD" => Some(Op::OpAdd),
486            "OP_SUB" => Some(Op::OpSub),
487            "OP_MUL" => Some(Op::OpMul),
488            "OP_DIV" => Some(Op::OpDiv),
489            "OP_MOD" => Some(Op::OpMod),
490            "OP_LSHIFT" => Some(Op::OpLShift),
491            "OP_RSHIFT" => Some(Op::OpRShift),
492            "OP_BOOLAND" => Some(Op::OpBoolAnd),
493            "OP_BOOLOR" => Some(Op::OpBoolOr),
494            "OP_NUMEQUAL" => Some(Op::OpNumEqual),
495            "OP_NUMEQUALVERIFY" => Some(Op::OpNumEqualVerify),
496            "OP_NUMNOTEQUAL" => Some(Op::OpNumNotEqual),
497            "OP_LESSTHAN" => Some(Op::OpLessThan),
498            "OP_GREATERTHAN" => Some(Op::OpGreaterThan),
499            "OP_LESSTHANOREQUAL" => Some(Op::OpLessThanOrEqual),
500            "OP_GREATERTHANOREQUAL" => Some(Op::OpGreaterThanOrEqual),
501            "OP_MIN" => Some(Op::OpMin),
502            "OP_MAX" => Some(Op::OpMax),
503            "OP_WITHIN" => Some(Op::OpWithin),
504            "OP_RIPEMD160" => Some(Op::OpRipemd160),
505            "OP_SHA1" => Some(Op::OpSha1),
506            "OP_SHA256" => Some(Op::OpSha256),
507            "OP_HASH160" => Some(Op::OpHash160),
508            "OP_HASH256" => Some(Op::OpHash256),
509            "OP_CODESEPARATOR" => Some(Op::OpCodeSeparator),
510            "OP_CHECKSIG" => Some(Op::OpCheckSig),
511            "OP_CHECKSIGVERIFY" => Some(Op::OpCheckSigVerify),
512            "OP_CHECKMULTISIG" => Some(Op::OpCheckMultiSig),
513            "OP_CHECKMULTISIGVERIFY" => Some(Op::OpCheckMultiSigVerify),
514            "OP_NOP1" => Some(Op::OpNop1),
515            "OP_NOP2" => Some(Op::OpNop2),
516            "OP_NOP3" => Some(Op::OpNop3),
517            "OP_NOP4" | "OP_SUBSTR" => Some(Op::OpSubstr),
518            "OP_NOP5" | "OP_LEFT" => Some(Op::OpLeft),
519            "OP_NOP6" | "OP_RIGHT" => Some(Op::OpRight),
520            "OP_NOP7" | "OP_LSHIFTNUM" => Some(Op::OpLShiftNum),
521            "OP_NOP8" | "OP_RSHIFTNUM" => Some(Op::OpRShiftNum),
522            "OP_INVALIDOPCODE" => Some(Op::OpInvalidOpcode),
523            _ => {
524                // Handle OP_NOP9..OP_NOP77 and template ops
525                if let Some(suffix) = name.strip_prefix("OP_NOP") {
526                    if let Ok(n) = suffix.parse::<u8>() {
527                        return Some(Op::from(0xb8 + n.saturating_sub(9)));
528                    }
529                }
530                None
531            }
532        }
533    }
534}
535
536impl From<u8> for Op {
537    fn from(byte: u8) -> Self {
538        match byte {
539            0x00 => Op::Op0,
540            // Direct push opcodes 0x01..=0x4b are NOT enum variants;
541            // they are handled by the parser as push-length indicators.
542            // From<u8> maps them to OpInvalidOpcode (they should never
543            // appear as standalone opcode values in ScriptChunk.op).
544            0x4c => Op::OpPushData1,
545            0x4d => Op::OpPushData2,
546            0x4e => Op::OpPushData4,
547            0x4f => Op::Op1Negate,
548            0x50 => Op::OpReserved,
549            0x51 => Op::Op1,
550            0x52 => Op::Op2,
551            0x53 => Op::Op3,
552            0x54 => Op::Op4,
553            0x55 => Op::Op5,
554            0x56 => Op::Op6,
555            0x57 => Op::Op7,
556            0x58 => Op::Op8,
557            0x59 => Op::Op9,
558            0x5a => Op::Op10,
559            0x5b => Op::Op11,
560            0x5c => Op::Op12,
561            0x5d => Op::Op13,
562            0x5e => Op::Op14,
563            0x5f => Op::Op15,
564            0x60 => Op::Op16,
565            0x61 => Op::OpNop,
566            0x62 => Op::OpVer,
567            0x63 => Op::OpIf,
568            0x64 => Op::OpNotIf,
569            0x65 => Op::OpVerIf,
570            0x66 => Op::OpVerNotIf,
571            0x67 => Op::OpElse,
572            0x68 => Op::OpEndIf,
573            0x69 => Op::OpVerify,
574            0x6a => Op::OpReturn,
575            0x6b => Op::OpToAltStack,
576            0x6c => Op::OpFromAltStack,
577            0x6d => Op::Op2Drop,
578            0x6e => Op::Op2Dup,
579            0x6f => Op::Op3Dup,
580            0x70 => Op::Op2Over,
581            0x71 => Op::Op2Rot,
582            0x72 => Op::Op2Swap,
583            0x73 => Op::OpIfDup,
584            0x74 => Op::OpDepth,
585            0x75 => Op::OpDrop,
586            0x76 => Op::OpDup,
587            0x77 => Op::OpNip,
588            0x78 => Op::OpOver,
589            0x79 => Op::OpPick,
590            0x7a => Op::OpRoll,
591            0x7b => Op::OpRot,
592            0x7c => Op::OpSwap,
593            0x7d => Op::OpTuck,
594            0x7e => Op::OpCat,
595            0x7f => Op::OpSplit,
596            0x80 => Op::OpNum2Bin,
597            0x81 => Op::OpBin2Num,
598            0x82 => Op::OpSize,
599            0x83 => Op::OpInvert,
600            0x84 => Op::OpAnd,
601            0x85 => Op::OpOr,
602            0x86 => Op::OpXor,
603            0x87 => Op::OpEqual,
604            0x88 => Op::OpEqualVerify,
605            0x89 => Op::OpReserved1,
606            0x8a => Op::OpReserved2,
607            0x8b => Op::Op1Add,
608            0x8c => Op::Op1Sub,
609            0x8d => Op::Op2Mul,
610            0x8e => Op::Op2Div,
611            0x8f => Op::OpNegate,
612            0x90 => Op::OpAbs,
613            0x91 => Op::OpNot,
614            0x92 => Op::Op0NotEqual,
615            0x93 => Op::OpAdd,
616            0x94 => Op::OpSub,
617            0x95 => Op::OpMul,
618            0x96 => Op::OpDiv,
619            0x97 => Op::OpMod,
620            0x98 => Op::OpLShift,
621            0x99 => Op::OpRShift,
622            0x9a => Op::OpBoolAnd,
623            0x9b => Op::OpBoolOr,
624            0x9c => Op::OpNumEqual,
625            0x9d => Op::OpNumEqualVerify,
626            0x9e => Op::OpNumNotEqual,
627            0x9f => Op::OpLessThan,
628            0xa0 => Op::OpGreaterThan,
629            0xa1 => Op::OpLessThanOrEqual,
630            0xa2 => Op::OpGreaterThanOrEqual,
631            0xa3 => Op::OpMin,
632            0xa4 => Op::OpMax,
633            0xa5 => Op::OpWithin,
634            0xa6 => Op::OpRipemd160,
635            0xa7 => Op::OpSha1,
636            0xa8 => Op::OpSha256,
637            0xa9 => Op::OpHash160,
638            0xaa => Op::OpHash256,
639            0xab => Op::OpCodeSeparator,
640            0xac => Op::OpCheckSig,
641            0xad => Op::OpCheckSigVerify,
642            0xae => Op::OpCheckMultiSig,
643            0xaf => Op::OpCheckMultiSigVerify,
644            0xb0 => Op::OpNop1,
645            0xb1 => Op::OpNop2,
646            0xb2 => Op::OpNop3,
647            0xb3 => Op::OpSubstr,
648            0xb4 => Op::OpLeft,
649            0xb5 => Op::OpRight,
650            0xb6 => Op::OpLShiftNum,
651            0xb7 => Op::OpRShiftNum,
652            0xb8 => Op::OpNop9,
653            0xb9 => Op::OpNop10,
654            0xba => Op::OpNop11,
655            0xbb => Op::OpNop12,
656            0xbc => Op::OpNop13,
657            0xbd => Op::OpNop14,
658            0xbe => Op::OpNop15,
659            0xbf => Op::OpNop16,
660            0xc0 => Op::OpNop17,
661            0xc1 => Op::OpNop18,
662            0xc2 => Op::OpNop19,
663            0xc3 => Op::OpNop20,
664            0xc4 => Op::OpNop21,
665            0xc5 => Op::OpNop22,
666            0xc6 => Op::OpNop23,
667            0xc7 => Op::OpNop24,
668            0xc8 => Op::OpNop25,
669            0xc9 => Op::OpNop26,
670            0xca => Op::OpNop27,
671            0xcb => Op::OpNop28,
672            0xcc => Op::OpNop29,
673            0xcd => Op::OpNop30,
674            0xce => Op::OpNop31,
675            0xcf => Op::OpNop32,
676            0xd0 => Op::OpNop33,
677            0xd1 => Op::OpNop34,
678            0xd2 => Op::OpNop35,
679            0xd3 => Op::OpNop36,
680            0xd4 => Op::OpNop37,
681            0xd5 => Op::OpNop38,
682            0xd6 => Op::OpNop39,
683            0xd7 => Op::OpNop40,
684            0xd8 => Op::OpNop41,
685            0xd9 => Op::OpNop42,
686            0xda => Op::OpNop43,
687            0xdb => Op::OpNop44,
688            0xdc => Op::OpNop45,
689            0xdd => Op::OpNop46,
690            0xde => Op::OpNop47,
691            0xdf => Op::OpNop48,
692            0xe0 => Op::OpNop49,
693            0xe1 => Op::OpNop50,
694            0xe2 => Op::OpNop51,
695            0xe3 => Op::OpNop52,
696            0xe4 => Op::OpNop53,
697            0xe5 => Op::OpNop54,
698            0xe6 => Op::OpNop55,
699            0xe7 => Op::OpNop56,
700            0xe8 => Op::OpNop57,
701            0xe9 => Op::OpNop58,
702            0xea => Op::OpNop59,
703            0xeb => Op::OpNop60,
704            0xec => Op::OpNop61,
705            0xed => Op::OpNop62,
706            0xee => Op::OpNop63,
707            0xef => Op::OpNop64,
708            0xf0 => Op::OpNop65,
709            0xf1 => Op::OpNop66,
710            0xf2 => Op::OpNop67,
711            0xf3 => Op::OpNop68,
712            0xf4 => Op::OpNop69,
713            0xf5 => Op::OpNop70,
714            0xf6 => Op::OpNop71,
715            0xf7 => Op::OpNop72,
716            0xf8 => Op::OpNop73,
717            0xf9 => Op::OpSmallData,
718            0xfa => Op::OpSmallInteger,
719            0xfb => Op::OpPubKeys,
720            0xfc => Op::OpNop77,
721            0xfd => Op::OpPubKeyHash,
722            0xfe => Op::OpPubKey,
723            0xff => Op::OpInvalidOpcode,
724            // Direct-push bytes (0x01..=0x4b) are not standalone opcodes.
725            // Unknown / gap bytes map to OpInvalidOpcode.
726            _ => Op::OpInvalidOpcode,
727        }
728    }
729}
730
731#[cfg(test)]
732mod tests {
733    use super::*;
734
735    #[test]
736    fn test_from_u8_roundtrip_known_opcodes() {
737        // Test that all known enum values survive the From<u8> roundtrip
738        let known: Vec<(u8, Op)> = vec![
739            (0x00, Op::Op0),
740            (0x4c, Op::OpPushData1),
741            (0x4d, Op::OpPushData2),
742            (0x4e, Op::OpPushData4),
743            (0x4f, Op::Op1Negate),
744            (0x50, Op::OpReserved),
745            (0x51, Op::Op1),
746            (0x60, Op::Op16),
747            (0x61, Op::OpNop),
748            (0x63, Op::OpIf),
749            (0x68, Op::OpEndIf),
750            (0x6a, Op::OpReturn),
751            (0x76, Op::OpDup),
752            (0x7e, Op::OpCat),
753            (0x7f, Op::OpSplit),
754            (0x80, Op::OpNum2Bin),
755            (0x81, Op::OpBin2Num),
756            (0x87, Op::OpEqual),
757            (0x88, Op::OpEqualVerify),
758            (0x93, Op::OpAdd),
759            (0x95, Op::OpMul),
760            (0x96, Op::OpDiv),
761            (0x97, Op::OpMod),
762            (0x98, Op::OpLShift),
763            (0x99, Op::OpRShift),
764            (0xa9, Op::OpHash160),
765            (0xac, Op::OpCheckSig),
766            (0xae, Op::OpCheckMultiSig),
767            (0xb0, Op::OpNop1),
768            (0xb3, Op::OpSubstr),
769            (0xb4, Op::OpLeft),
770            (0xb5, Op::OpRight),
771            (0xb6, Op::OpLShiftNum),
772            (0xb7, Op::OpRShiftNum),
773            (0xff, Op::OpInvalidOpcode),
774        ];
775
776        for (byte, expected) in known {
777            let op = Op::from(byte);
778            assert_eq!(op, expected, "From<u8> for byte {:#04x} failed", byte);
779            assert_eq!(op.to_byte(), byte, "to_byte for {:#04x} failed", byte);
780        }
781    }
782
783    #[test]
784    fn test_unknown_bytes_map_to_invalid() {
785        // Bytes 0x01..=0x4b are direct-push, should map to OpInvalidOpcode
786        for byte in 0x01..=0x4bu8 {
787            assert_eq!(
788                Op::from(byte),
789                Op::OpInvalidOpcode,
790                "Direct-push byte {:#04x} should map to OpInvalidOpcode",
791                byte
792            );
793        }
794    }
795
796    #[test]
797    fn test_to_name_common_opcodes() {
798        assert_eq!(Op::Op0.to_name(), "OP_0");
799        assert_eq!(Op::OpDup.to_name(), "OP_DUP");
800        assert_eq!(Op::OpCheckSig.to_name(), "OP_CHECKSIG");
801        assert_eq!(Op::OpReturn.to_name(), "OP_RETURN");
802        assert_eq!(Op::OpHash160.to_name(), "OP_HASH160");
803        assert_eq!(Op::OpEqualVerify.to_name(), "OP_EQUALVERIFY");
804        assert_eq!(Op::Op1.to_name(), "OP_1");
805        assert_eq!(Op::Op16.to_name(), "OP_16");
806        assert_eq!(Op::OpCat.to_name(), "OP_CAT");
807        assert_eq!(Op::OpMul.to_name(), "OP_MUL");
808        assert_eq!(Op::OpSubstr.to_name(), "OP_SUBSTR");
809        assert_eq!(Op::OpInvalidOpcode.to_name(), "OP_INVALIDOPCODE");
810    }
811
812    #[test]
813    fn test_from_name_roundtrip() {
814        let names = [
815            "OP_0",
816            "OP_DUP",
817            "OP_CHECKSIG",
818            "OP_RETURN",
819            "OP_HASH160",
820            "OP_EQUALVERIFY",
821            "OP_1",
822            "OP_16",
823            "OP_CAT",
824            "OP_MUL",
825            "OP_SUBSTR",
826            "OP_INVALIDOPCODE",
827            "OP_FALSE",
828            "OP_TRUE",
829        ];
830        for name in &names {
831            let op = Op::from_name(name).unwrap_or_else(|| panic!("from_name failed for {}", name));
832            // For aliases, the canonical name may differ
833            let canonical = op.to_name();
834            let op2 = Op::from_name(canonical)
835                .unwrap_or_else(|| panic!("from_name failed for canonical {}", canonical));
836            assert_eq!(op, op2);
837        }
838    }
839
840    #[test]
841    fn test_all_nop_variants_exist() {
842        // NOP9 through NOP73 should all be valid
843        for byte in 0xb8..=0xf8u8 {
844            let op = Op::from(byte);
845            assert_ne!(
846                op,
847                Op::OpInvalidOpcode,
848                "Byte {:#04x} should not be OpInvalidOpcode",
849                byte
850            );
851        }
852    }
853
854    #[test]
855    fn test_opcode_count() {
856        // Count all unique opcodes by scanning byte range
857        let mut count = 0u32;
858        for byte in 0x00..=0xffu16 {
859            let op = Op::from(byte as u8);
860            if op != Op::OpInvalidOpcode || byte == 0xff {
861                count += 1;
862            }
863        }
864        // 0x00, 0x4c..=0xff minus the 0x01..=0x4b gap = 1 + (0xff - 0x4c + 1) = 1 + 180 = 181
865        // But many of those are nop/known, and gap bytes are invalid.
866        // Let's just verify it's at least 130 (the ~180 named opcodes minus direct-push gap)
867        assert!(
868            count >= 130,
869            "Expected at least 130 named opcodes, got {}",
870            count
871        );
872    }
873}