opcode_test.go raw
1 package txscript
2
3 import (
4 "bytes"
5 "fmt"
6 "strconv"
7 "strings"
8 "testing"
9 )
10
11 // TestOpcodeDisabled tests the opcodeDisabled function manually because all disabled opcodes result in a script
12 // execution failure when executed normally, so the function is not called under normal circumstances.
13 func TestOpcodeDisabled(t *testing.T) {
14 t.Parallel()
15 tests := []byte{OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT,
16 OP_AND, OP_OR, OP_2MUL, OP_2DIV, OP_MUL, OP_DIV, OP_MOD,
17 OP_LSHIFT, OP_RSHIFT,
18 }
19 for _, opcodeVal := range tests {
20 pop := parsedOpcode{opcode: &OpcodeArray[opcodeVal], data: nil}
21 e := opcodeDisabled(&pop, nil)
22 if !IsErrorCode(e, ErrDisabledOpcode) {
23 t.Errorf("opcodeDisabled: unexpected error - got %v, "+
24 "want %v", e, ErrDisabledOpcode,
25 )
26 continue
27 }
28 }
29 }
30
31 // TestOpcodeDisasm tests the print function for all opcodes in both the oneline and full modes to ensure it provides
32 // the expected disassembly.
33 func TestOpcodeDisasm(t *testing.T) {
34 t.Parallel()
35 // First, test the oneline disassembly. The expected strings for the data push opcodes are replaced in the test
36 // loops below since they involve repeating bytes. Also, the OP_NOP# and OP_UNKNOWN# are replaced below too, since
37 // it's easier than manually listing them here.
38 oneBytes := []byte{0x01}
39 oneStr := "01"
40 expectedStrings := [256]string{0x00: "0", 0x4f: "-1",
41 0x50: "OP_RESERVED", 0x61: "OP_NOP", 0x62: "OP_VER",
42 0x63: "OP_IF", 0x64: "OP_NOTIF", 0x65: "OP_VERIF",
43 0x66: "OP_VERNOTIF", 0x67: "OP_ELSE", 0x68: "OP_ENDIF",
44 0x69: "OP_VERIFY", 0x6a: "OP_RETURN", 0x6b: "OP_TOALTSTACK",
45 0x6c: "OP_FROMALTSTACK", 0x6d: "OP_2DROP", 0x6e: "OP_2DUP",
46 0x6f: "OP_3DUP", 0x70: "OP_2OVER", 0x71: "OP_2ROT",
47 0x72: "OP_2SWAP", 0x73: "OP_IFDUP", 0x74: "OP_DEPTH",
48 0x75: "OP_DROP", 0x76: "OP_DUP", 0x77: "OP_NIP",
49 0x78: "OP_OVER", 0x79: "OP_PICK", 0x7a: "OP_ROLL",
50 0x7b: "OP_ROT", 0x7c: "OP_SWAP", 0x7d: "OP_TUCK",
51 0x7e: "OP_CAT", 0x7f: "OP_SUBSTR", 0x80: "OP_LEFT",
52 0x81: "OP_RIGHT", 0x82: "OP_SIZE", 0x83: "OP_INVERT",
53 0x84: "OP_AND", 0x85: "OP_OR", 0x86: "OP_XOR",
54 0x87: "OP_EQUAL", 0x88: "OP_EQUALVERIFY", 0x89: "OP_RESERVED1",
55 0x8a: "OP_RESERVED2", 0x8b: "OP_1ADD", 0x8c: "OP_1SUB",
56 0x8d: "OP_2MUL", 0x8e: "OP_2DIV", 0x8f: "OP_NEGATE",
57 0x90: "OP_ABS", 0x91: "OP_NOT", 0x92: "OP_0NOTEQUAL",
58 0x93: "OP_ADD", 0x94: "OP_SUB", 0x95: "OP_MUL", 0x96: "OP_DIV",
59 0x97: "OP_MOD", 0x98: "OP_LSHIFT", 0x99: "OP_RSHIFT",
60 0x9a: "OP_BOOLAND", 0x9b: "OP_BOOLOR", 0x9c: "OP_NUMEQUAL",
61 0x9d: "OP_NUMEQUALVERIFY", 0x9e: "OP_NUMNOTEQUAL",
62 0x9f: "OP_LESSTHAN", 0xa0: "OP_GREATERTHAN",
63 0xa1: "OP_LESSTHANOREQUAL", 0xa2: "OP_GREATERTHANOREQUAL",
64 0xa3: "OP_MIN", 0xa4: "OP_MAX", 0xa5: "OP_WITHIN",
65 0xa6: "OP_RIPEMD160", 0xa7: "OP_SHA1", 0xa8: "OP_SHA256",
66 0xa9: "OP_HASH160", 0xaa: "OP_HASH256", 0xab: "OP_CODESEPARATOR",
67 0xac: "OP_CHECKSIG", 0xad: "OP_CHECKSIGVERIFY",
68 0xae: "OP_CHECKMULTISIG", 0xaf: "OP_CHECKMULTISIGVERIFY",
69 0xfa: "OP_SMALLINTEGER", 0xfb: "OP_PUBKEYS",
70 0xfd: "OP_PUBKEYHASH", 0xfe: "OP_PUBKEY",
71 0xff: "OP_INVALIDOPCODE",
72 }
73 for opcodeVal, expectedStr := range expectedStrings {
74 var data []byte
75 switch {
76 // OP_DATA_1 through OP_DATA_65 display the pushed data.
77 case opcodeVal >= 0x01 && opcodeVal < 0x4c:
78 data = bytes.Repeat(oneBytes, opcodeVal)
79 expectedStr = strings.Repeat(oneStr, opcodeVal)
80 // OP_PUSHDATA1.
81 case opcodeVal == 0x4c:
82 data = bytes.Repeat(oneBytes, 1)
83 expectedStr = strings.Repeat(oneStr, 1)
84 // OP_PUSHDATA2.
85 case opcodeVal == 0x4d:
86 data = bytes.Repeat(oneBytes, 2)
87 expectedStr = strings.Repeat(oneStr, 2)
88 // OP_PUSHDATA4.
89 case opcodeVal == 0x4e:
90 data = bytes.Repeat(oneBytes, 3)
91 expectedStr = strings.Repeat(oneStr, 3)
92 // OP_1 through OP_16 display the numbers themselves.
93 case opcodeVal >= 0x51 && opcodeVal <= 0x60:
94 val := byte(opcodeVal - (0x51 - 1))
95 data = []byte{val}
96 expectedStr = strconv.Itoa(int(val))
97 // OP_NOP1 through OP_NOP10.
98 case opcodeVal >= 0xb0 && opcodeVal <= 0xb9:
99 switch opcodeVal {
100 case 0xb1:
101 // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY
102 expectedStr = "OP_CHECKLOCKTIMEVERIFY"
103 case 0xb2:
104 // OP_NOP3 is an alias of OP_CHECKSEQUENCEVERIFY
105 expectedStr = "OP_CHECKSEQUENCEVERIFY"
106 default:
107 val := byte(opcodeVal - (0xb0 - 1))
108 expectedStr = "OP_NOP" + strconv.Itoa(int(val))
109 }
110 // OP_UNKNOWN#.
111 case opcodeVal >= 0xba && opcodeVal <= 0xf9 || opcodeVal == 0xfc:
112 expectedStr = "OP_UNKNOWN" + strconv.Itoa(opcodeVal)
113 }
114 pop := parsedOpcode{opcode: &OpcodeArray[opcodeVal], data: data}
115 gotStr := pop.print(true)
116 if gotStr != expectedStr {
117 t.Errorf("pop.print (opcode %x): Unexpected disasm "+
118 "string - got %v, want %v", opcodeVal, gotStr,
119 expectedStr,
120 )
121 continue
122 }
123 }
124 // Now, replace the relevant fields and test the full disassembly.
125 expectedStrings[0x00] = "OP_0"
126 expectedStrings[0x4f] = "OP_1NEGATE"
127 for opcodeVal, expectedStr := range expectedStrings {
128 var data []byte
129 switch {
130 // OP_DATA_1 through OP_DATA_65 display the opcode followed by the pushed data.
131 case opcodeVal >= 0x01 && opcodeVal < 0x4c:
132 data = bytes.Repeat(oneBytes, opcodeVal)
133 expectedStr = fmt.Sprintf("OP_DATA_%d 0x%s", opcodeVal,
134 strings.Repeat(oneStr, opcodeVal),
135 )
136 // OP_PUSHDATA1.
137 case opcodeVal == 0x4c:
138 data = bytes.Repeat(oneBytes, 1)
139 expectedStr = fmt.Sprintf("OP_PUSHDATA1 0x%02x 0x%s",
140 len(data), strings.Repeat(oneStr, 1),
141 )
142 // OP_PUSHDATA2.
143 case opcodeVal == 0x4d:
144 data = bytes.Repeat(oneBytes, 2)
145 expectedStr = fmt.Sprintf("OP_PUSHDATA2 0x%04x 0x%s",
146 len(data), strings.Repeat(oneStr, 2),
147 )
148 // OP_PUSHDATA4.
149 case opcodeVal == 0x4e:
150 data = bytes.Repeat(oneBytes, 3)
151 expectedStr = fmt.Sprintf("OP_PUSHDATA4 0x%08x 0x%s",
152 len(data), strings.Repeat(oneStr, 3),
153 )
154 // OP_1 through OP_16.
155 case opcodeVal >= 0x51 && opcodeVal <= 0x60:
156 val := byte(opcodeVal - (0x51 - 1))
157 data = []byte{val}
158 expectedStr = "OP_" + strconv.Itoa(int(val))
159 // OP_NOP1 through OP_NOP10.
160 case opcodeVal >= 0xb0 && opcodeVal <= 0xb9:
161 switch opcodeVal {
162 case 0xb1:
163 // OP_NOP2 is an alias of OP_CHECKLOCKTIMEVERIFY
164 expectedStr = "OP_CHECKLOCKTIMEVERIFY"
165 case 0xb2:
166 // OP_NOP3 is an alias of OP_CHECKSEQUENCEVERIFY
167 expectedStr = "OP_CHECKSEQUENCEVERIFY"
168 default:
169 val := byte(opcodeVal - (0xb0 - 1))
170 expectedStr = "OP_NOP" + strconv.Itoa(int(val))
171 }
172 // OP_UNKNOWN#.
173 case opcodeVal >= 0xba && opcodeVal <= 0xf9 || opcodeVal == 0xfc:
174 expectedStr = "OP_UNKNOWN" + strconv.Itoa(opcodeVal)
175 }
176 pop := parsedOpcode{opcode: &OpcodeArray[opcodeVal], data: data}
177 gotStr := pop.print(false)
178 if gotStr != expectedStr {
179 t.Errorf("pop.print (opcode %x): Unexpected disasm "+
180 "string - got %v, want %v", opcodeVal, gotStr,
181 expectedStr,
182 )
183 continue
184 }
185 }
186 }
187