You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
6.4 KiB
186 lines
6.4 KiB
// Copyright (c) 2013-2015 The btcsuite developers |
|
// Use of this source code is governed by an ISC |
|
// license that can be found in the LICENSE file. |
|
|
|
package txscript |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"strconv" |
|
"strings" |
|
"testing" |
|
) |
|
|
|
// TestOpcodeDisabled tests the opcodeDisabled function manually because all |
|
// disabled opcodes result in a script execution failure when executed normally, |
|
// so the function is not called under normal circumstances. |
|
func TestOpcodeDisabled(t *testing.T) { |
|
t.Parallel() |
|
|
|
tests := []byte{OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT, |
|
OP_AND, OP_OR, OP_2MUL, OP_2DIV, OP_MUL, OP_DIV, OP_MOD, |
|
OP_LSHIFT, OP_RSHIFT, |
|
} |
|
for _, opcodeVal := range tests { |
|
pop := parsedOpcode{opcode: &opcodeArray[opcodeVal], data: nil} |
|
if err := opcodeDisabled(&pop, nil); err != ErrStackOpDisabled { |
|
t.Errorf("opcodeDisabled: unexpected error - got %v, "+ |
|
"want %v", err, ErrStackOpDisabled) |
|
return |
|
} |
|
} |
|
} |
|
|
|
// TestOpcodeDisasm tests the print function for all opcodes in both the oneline |
|
// and full modes to ensure it provides the expected disassembly. |
|
func TestOpcodeDisasm(t *testing.T) { |
|
t.Parallel() |
|
|
|
// First, test the oneline disassembly. |
|
|
|
// The expected strings for the data push opcodes are replaced in the |
|
// test loops below since they involve repeating bytes. Also, the |
|
// OP_NOP# and OP_UNKNOWN# are replaced below too, since it's easier |
|
// than manually listing them here. |
|
oneBytes := []byte{0x01} |
|
oneStr := "01" |
|
expectedStrings := [256]string{0x00: "0", 0x4f: "-1", |
|
0x50: "OP_RESERVED", 0x61: "OP_NOP", 0x62: "OP_VER", |
|
0x63: "OP_IF", 0x64: "OP_NOTIF", 0x65: "OP_VERIF", |
|
0x66: "OP_VERNOTIF", 0x67: "OP_ELSE", 0x68: "OP_ENDIF", |
|
0x69: "OP_VERIFY", 0x6a: "OP_RETURN", 0x6b: "OP_TOALTSTACK", |
|
0x6c: "OP_FROMALTSTACK", 0x6d: "OP_2DROP", 0x6e: "OP_2DUP", |
|
0x6f: "OP_3DUP", 0x70: "OP_2OVER", 0x71: "OP_2ROT", |
|
0x72: "OP_2SWAP", 0x73: "OP_IFDUP", 0x74: "OP_DEPTH", |
|
0x75: "OP_DROP", 0x76: "OP_DUP", 0x77: "OP_NIP", |
|
0x78: "OP_OVER", 0x79: "OP_PICK", 0x7a: "OP_ROLL", |
|
0x7b: "OP_ROT", 0x7c: "OP_SWAP", 0x7d: "OP_TUCK", |
|
0x7e: "OP_CAT", 0x7f: "OP_SUBSTR", 0x80: "OP_LEFT", |
|
0x81: "OP_RIGHT", 0x82: "OP_SIZE", 0x83: "OP_INVERT", |
|
0x84: "OP_AND", 0x85: "OP_OR", 0x86: "OP_XOR", |
|
0x87: "OP_EQUAL", 0x88: "OP_EQUALVERIFY", 0x89: "OP_RESERVED1", |
|
0x8a: "OP_RESERVED2", 0x8b: "OP_1ADD", 0x8c: "OP_1SUB", |
|
0x8d: "OP_2MUL", 0x8e: "OP_2DIV", 0x8f: "OP_NEGATE", |
|
0x90: "OP_ABS", 0x91: "OP_NOT", 0x92: "OP_0NOTEQUAL", |
|
0x93: "OP_ADD", 0x94: "OP_SUB", 0x95: "OP_MUL", 0x96: "OP_DIV", |
|
0x97: "OP_MOD", 0x98: "OP_LSHIFT", 0x99: "OP_RSHIFT", |
|
0x9a: "OP_BOOLAND", 0x9b: "OP_BOOLOR", 0x9c: "OP_NUMEQUAL", |
|
0x9d: "OP_NUMEQUALVERIFY", 0x9e: "OP_NUMNOTEQUAL", |
|
0x9f: "OP_LESSTHAN", 0xa0: "OP_GREATERTHAN", |
|
0xa1: "OP_LESSTHANOREQUAL", 0xa2: "OP_GREATERTHANOREQUAL", |
|
0xa3: "OP_MIN", 0xa4: "OP_MAX", 0xa5: "OP_WITHIN", |
|
0xa6: "OP_RIPEMD160", 0xa7: "OP_SHA1", 0xa8: "OP_SHA256", |
|
0xa9: "OP_HASH160", 0xaa: "OP_HASH256", 0xab: "OP_CODESEPARATOR", |
|
0xac: "OP_CHECKSIG", 0xad: "OP_CHECKSIGVERIFY", |
|
0xae: "OP_CHECKMULTISIG", 0xaf: "OP_CHECKMULTISIGVERIFY", |
|
0xf9: "OP_SMALLDATA", 0xfa: "OP_SMALLINTEGER", |
|
0xfb: "OP_PUBKEYS", 0xfd: "OP_PUBKEYHASH", 0xfe: "OP_PUBKEY", |
|
0xff: "OP_INVALIDOPCODE", |
|
} |
|
for opcodeVal, expectedStr := range expectedStrings { |
|
var data []byte |
|
switch { |
|
// OP_DATA_1 through OP_DATA_65 display the pushed data. |
|
case opcodeVal >= 0x01 && opcodeVal < 0x4c: |
|
data = bytes.Repeat(oneBytes, opcodeVal) |
|
expectedStr = strings.Repeat(oneStr, opcodeVal) |
|
|
|
// OP_PUSHDATA1. |
|
case opcodeVal == 0x4c: |
|
data = bytes.Repeat(oneBytes, 1) |
|
expectedStr = strings.Repeat(oneStr, 1) |
|
|
|
// OP_PUSHDATA2. |
|
case opcodeVal == 0x4d: |
|
data = bytes.Repeat(oneBytes, 2) |
|
expectedStr = strings.Repeat(oneStr, 2) |
|
|
|
// OP_PUSHDATA4. |
|
case opcodeVal == 0x4e: |
|
data = bytes.Repeat(oneBytes, 3) |
|
expectedStr = strings.Repeat(oneStr, 3) |
|
|
|
// OP_1 through OP_16 display the numbers themselves. |
|
case opcodeVal >= 0x51 && opcodeVal <= 0x60: |
|
val := byte(opcodeVal - (0x51 - 1)) |
|
data = []byte{val} |
|
expectedStr = strconv.Itoa(int(val)) |
|
|
|
// OP_NOP1 through OP_NOP10. |
|
case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: |
|
val := byte(opcodeVal - (0xb0 - 1)) |
|
expectedStr = "OP_NOP" + strconv.Itoa(int(val)) |
|
|
|
// OP_UNKNOWN#. |
|
case opcodeVal >= 0xba && opcodeVal <= 0xf8 || opcodeVal == 0xfc: |
|
expectedStr = "OP_UNKNOWN" + strconv.Itoa(int(opcodeVal)) |
|
} |
|
|
|
pop := parsedOpcode{opcode: &opcodeArray[opcodeVal], data: data} |
|
gotStr := pop.print(true) |
|
if gotStr != expectedStr { |
|
t.Errorf("pop.print (opcode %x): Unexpected disasm "+ |
|
"string - got %v, want %v", opcodeVal, gotStr, |
|
expectedStr) |
|
continue |
|
} |
|
} |
|
|
|
// Now, replace the relevant fields and test the full disassembly. |
|
expectedStrings[0x00] = "OP_0" |
|
expectedStrings[0x4f] = "OP_1NEGATE" |
|
for opcodeVal, expectedStr := range expectedStrings { |
|
var data []byte |
|
switch { |
|
// OP_DATA_1 through OP_DATA_65 display the opcode followed by |
|
// the pushed data. |
|
case opcodeVal >= 0x01 && opcodeVal < 0x4c: |
|
data = bytes.Repeat(oneBytes, opcodeVal) |
|
expectedStr = fmt.Sprintf("OP_DATA_%d 0x%s", opcodeVal, |
|
strings.Repeat(oneStr, opcodeVal)) |
|
|
|
// OP_PUSHDATA1. |
|
case opcodeVal == 0x4c: |
|
data = bytes.Repeat(oneBytes, 1) |
|
expectedStr = fmt.Sprintf("OP_PUSHDATA1 0x%02x 0x%s", |
|
len(data), strings.Repeat(oneStr, 1)) |
|
|
|
// OP_PUSHDATA2. |
|
case opcodeVal == 0x4d: |
|
data = bytes.Repeat(oneBytes, 2) |
|
expectedStr = fmt.Sprintf("OP_PUSHDATA2 0x%04x 0x%s", |
|
len(data), strings.Repeat(oneStr, 2)) |
|
|
|
// OP_PUSHDATA4. |
|
case opcodeVal == 0x4e: |
|
data = bytes.Repeat(oneBytes, 3) |
|
expectedStr = fmt.Sprintf("OP_PUSHDATA4 0x%08x 0x%s", |
|
len(data), strings.Repeat(oneStr, 3)) |
|
|
|
// OP_1 through OP_16. |
|
case opcodeVal >= 0x51 && opcodeVal <= 0x60: |
|
val := byte(opcodeVal - (0x51 - 1)) |
|
data = []byte{val} |
|
expectedStr = "OP_" + strconv.Itoa(int(val)) |
|
|
|
// OP_NOP1 through OP_NOP10. |
|
case opcodeVal >= 0xb0 && opcodeVal <= 0xb9: |
|
val := byte(opcodeVal - (0xb0 - 1)) |
|
expectedStr = "OP_NOP" + strconv.Itoa(int(val)) |
|
|
|
// OP_UNKNOWN#. |
|
case opcodeVal >= 0xba && opcodeVal <= 0xf8 || opcodeVal == 0xfc: |
|
expectedStr = "OP_UNKNOWN" + strconv.Itoa(int(opcodeVal)) |
|
} |
|
|
|
pop := parsedOpcode{opcode: &opcodeArray[opcodeVal], data: data} |
|
gotStr := pop.print(false) |
|
if gotStr != expectedStr { |
|
t.Errorf("pop.print (opcode %x): Unexpected disasm "+ |
|
"string - got %v, want %v", opcodeVal, gotStr, |
|
expectedStr) |
|
continue |
|
} |
|
} |
|
}
|
|
|