2018-06-29 17:46:14 +00:00
|
|
|
// Copyright (c) 2013-2017 The btcsuite developers
|
2019-03-13 06:11:08 +00:00
|
|
|
// Copyright (c) 2015-2019 The Decred developers
|
2015-04-20 19:16:46 +00:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2018-04-29 23:27:13 +00:00
|
|
|
package txscript
|
2015-04-20 19:16:46 +00:00
|
|
|
|
|
|
|
|
import (
|
2018-04-29 22:55:03 +00:00
|
|
|
"bytes"
|
2015-04-20 19:16:46 +00:00
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
2018-06-14 14:49:33 +00:00
|
|
|
"regexp"
|
2015-04-20 19:16:46 +00:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
2016-01-20 21:46:42 +00:00
|
|
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
2019-06-19 04:51:20 +00:00
|
|
|
"github.com/decred/dcrd/dcrutil/v2"
|
2016-01-20 21:46:42 +00:00
|
|
|
"github.com/decred/dcrd/wire"
|
2015-04-20 19:16:46 +00:00
|
|
|
)
|
|
|
|
|
|
2018-06-14 14:49:33 +00:00
|
|
|
var (
|
2018-06-15 04:25:35 +00:00
|
|
|
// tokenRE is a regular expression used to parse tokens from short form
|
|
|
|
|
// scripts. It splits on repeated tokens and spaces. Repeated tokens are
|
|
|
|
|
// denoted by being wrapped in angular brackets followed by a suffix which
|
|
|
|
|
// consists of a number inside braces.
|
|
|
|
|
tokenRE = regexp.MustCompile(`\<.+?\>\{[0-9]+\}|[^\s]+`)
|
|
|
|
|
|
|
|
|
|
// repTokenRE is a regular expression used to parse short form scripts
|
|
|
|
|
// for a series of tokens repeated a specified number of times.
|
|
|
|
|
repTokenRE = regexp.MustCompile(`^\<(.+)\>\{([0-9]+)\}$`)
|
|
|
|
|
|
2018-06-14 14:49:33 +00:00
|
|
|
// repRawRE is a regular expression used to parse short form scripts
|
|
|
|
|
// for raw data that is to be repeated a specified number of times.
|
|
|
|
|
repRawRE = regexp.MustCompile(`^(0[xX][0-9a-fA-F]+)\{([0-9]+)\}$`)
|
|
|
|
|
|
|
|
|
|
// repQuoteRE is a regular expression used to parse short form scripts for
|
|
|
|
|
// quoted data that is to be repeated a specified number of times.
|
|
|
|
|
repQuoteRE = regexp.MustCompile(`^'(.*)'\{([0-9]+)\}$`)
|
|
|
|
|
)
|
|
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// scriptTestName returns a descriptive test name for the given reference script
|
|
|
|
|
// test data.
|
|
|
|
|
func scriptTestName(test []string) (string, error) {
|
|
|
|
|
// The test must consist of at least a signature script, public key script,
|
|
|
|
|
// verification flags, and expected error. Finally, it may optionally
|
|
|
|
|
// contain a comment.
|
|
|
|
|
if len(test) < 4 || len(test) > 5 {
|
|
|
|
|
return "", fmt.Errorf("invalid test length %d", len(test))
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// Use the comment for the test name if one is specified, otherwise,
|
|
|
|
|
// construct the name based on the signature script, public key script,
|
|
|
|
|
// and flags.
|
|
|
|
|
var name string
|
|
|
|
|
if len(test) == 5 {
|
|
|
|
|
name = fmt.Sprintf("test (%s)", test[4])
|
2015-04-20 19:16:46 +00:00
|
|
|
} else {
|
|
|
|
|
name = fmt.Sprintf("test ([%s, %s, %s])", test[0], test[1],
|
|
|
|
|
test[2])
|
|
|
|
|
}
|
|
|
|
|
return name, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse hex string into a []byte.
|
|
|
|
|
func parseHex(tok string) ([]byte, error) {
|
|
|
|
|
if !strings.HasPrefix(tok, "0x") {
|
|
|
|
|
return nil, errors.New("not a hex number")
|
|
|
|
|
}
|
|
|
|
|
return hex.DecodeString(tok[2:])
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
// shortFormOps holds a map of opcode names to values for use in short form
|
|
|
|
|
// parsing. It is declared here so it only needs to be created once.
|
|
|
|
|
var shortFormOps map[string]byte
|
|
|
|
|
|
2019-08-16 22:37:58 +00:00
|
|
|
// parseShortForm parses a string as used in the reference tests into the
|
2017-08-04 06:40:55 +00:00
|
|
|
// script it came from.
|
2015-05-02 19:56:55 +00:00
|
|
|
//
|
|
|
|
|
// The format used for these tests is pretty simple if ad-hoc:
|
2018-06-14 14:49:33 +00:00
|
|
|
// - Opcodes other than the push opcodes and unknown are present as either
|
|
|
|
|
// OP_NAME or just NAME
|
2015-05-02 19:56:55 +00:00
|
|
|
// - Plain numbers are made into push operations
|
2018-06-14 14:49:33 +00:00
|
|
|
// - Numbers beginning with 0x are inserted into the []byte as-is (so 0x14 is
|
|
|
|
|
// OP_DATA_20)
|
|
|
|
|
// - Numbers beginning with 0x which have a suffix which consists of a number
|
|
|
|
|
// in braces (e.g. 0x6161{10}) repeat the raw bytes the specified number of
|
2018-06-15 04:25:35 +00:00
|
|
|
// times and are inserted as-is
|
2015-05-02 19:56:55 +00:00
|
|
|
// - Single quoted strings are pushed as data
|
2018-06-14 14:49:33 +00:00
|
|
|
// - Single quoted strings that have a suffix which consists of a number in
|
|
|
|
|
// braces (e.g. 'b'{10}) repeat the data the specified number of times and
|
|
|
|
|
// are pushed as a single data push
|
2018-06-15 04:25:35 +00:00
|
|
|
// - Tokens inside of angular brackets with a suffix which consists of a
|
|
|
|
|
// number in braces (e.g. <0 0 CHECKMULTSIG>{5}) is parsed as if the tokens
|
|
|
|
|
// inside the angular brackets were manually repeated the specified number
|
|
|
|
|
// of times
|
2015-05-02 19:56:55 +00:00
|
|
|
// - Anything else is an error
|
2015-04-20 19:16:46 +00:00
|
|
|
func parseShortForm(script string) ([]byte, error) {
|
2015-05-02 19:56:55 +00:00
|
|
|
// Only create the short form opcode map once.
|
|
|
|
|
if shortFormOps == nil {
|
|
|
|
|
ops := make(map[string]byte)
|
|
|
|
|
for opcodeName, opcodeValue := range OpcodeByName {
|
|
|
|
|
if strings.Contains(opcodeName, "OP_UNKNOWN") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ops[opcodeName] = opcodeValue
|
|
|
|
|
|
|
|
|
|
// The opcodes named OP_# can't have the OP_ prefix
|
|
|
|
|
// stripped or they would conflict with the plain
|
|
|
|
|
// numbers. Also, since OP_FALSE and OP_TRUE are
|
|
|
|
|
// aliases for the OP_0, and OP_1, respectively, they
|
|
|
|
|
// have the same value, so detect those by name and
|
|
|
|
|
// allow them.
|
|
|
|
|
if (opcodeName == "OP_FALSE" || opcodeName == "OP_TRUE") ||
|
|
|
|
|
(opcodeValue != OP_0 && (opcodeValue < OP_1 ||
|
|
|
|
|
opcodeValue > OP_16)) {
|
|
|
|
|
|
|
|
|
|
ops[strings.TrimPrefix(opcodeName, "OP_")] = opcodeValue
|
|
|
|
|
}
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
2015-05-02 19:56:55 +00:00
|
|
|
shortFormOps = ops
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder := NewScriptBuilder()
|
|
|
|
|
|
2018-06-15 04:25:35 +00:00
|
|
|
var handleToken func(tok string) error
|
|
|
|
|
handleToken = func(tok string) error {
|
|
|
|
|
// Multiple repeated tokens.
|
|
|
|
|
if m := repTokenRE.FindStringSubmatch(tok); m != nil {
|
|
|
|
|
count, err := strconv.ParseInt(m[2], 10, 32)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("bad token %q", tok)
|
|
|
|
|
}
|
|
|
|
|
tokens := tokenRE.FindAllStringSubmatch(m[1], -1)
|
|
|
|
|
for i := 0; i < int(count); i++ {
|
|
|
|
|
for _, t := range tokens {
|
|
|
|
|
if err := handleToken(t[0]); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
2018-06-15 04:25:35 +00:00
|
|
|
|
|
|
|
|
// Plain number.
|
2015-04-20 19:16:46 +00:00
|
|
|
if num, err := strconv.ParseInt(tok, 10, 64); err == nil {
|
|
|
|
|
builder.AddInt64(num)
|
2018-06-15 04:25:35 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Raw data.
|
|
|
|
|
if bts, err := parseHex(tok); err == nil {
|
2018-04-29 23:27:13 +00:00
|
|
|
// Concatenate the bytes manually since the test code
|
|
|
|
|
// intentionally creates scripts that are too large and
|
|
|
|
|
// would cause the builder to error otherwise.
|
|
|
|
|
if builder.err == nil {
|
|
|
|
|
builder.script = append(builder.script, bts...)
|
|
|
|
|
}
|
2018-06-15 04:25:35 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Repeated raw bytes.
|
|
|
|
|
if m := repRawRE.FindStringSubmatch(tok); m != nil {
|
2018-06-14 14:49:33 +00:00
|
|
|
bts, err := parseHex(m[1])
|
|
|
|
|
if err != nil {
|
2018-06-15 04:25:35 +00:00
|
|
|
return fmt.Errorf("bad token %q", tok)
|
2018-06-14 14:49:33 +00:00
|
|
|
}
|
|
|
|
|
count, err := strconv.ParseInt(m[2], 10, 32)
|
|
|
|
|
if err != nil {
|
2018-06-15 04:25:35 +00:00
|
|
|
return fmt.Errorf("bad token %q", tok)
|
2018-06-14 14:49:33 +00:00
|
|
|
}
|
2018-06-15 04:25:35 +00:00
|
|
|
|
2018-06-14 14:49:33 +00:00
|
|
|
// Concatenate the bytes manually since the test code
|
|
|
|
|
// intentionally creates scripts that are too large and
|
|
|
|
|
// would cause the builder to error otherwise.
|
2018-06-15 04:25:35 +00:00
|
|
|
bts = bytes.Repeat(bts, int(count))
|
2018-06-14 14:49:33 +00:00
|
|
|
if builder.err == nil {
|
|
|
|
|
builder.script = append(builder.script, bts...)
|
|
|
|
|
}
|
2018-06-15 04:25:35 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Quoted data.
|
|
|
|
|
if len(tok) >= 2 && tok[0] == '\'' && tok[len(tok)-1] == '\'' {
|
2015-04-20 19:16:46 +00:00
|
|
|
builder.AddFullData([]byte(tok[1 : len(tok)-1]))
|
2018-06-15 04:25:35 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Repeated quoted data.
|
|
|
|
|
if m := repQuoteRE.FindStringSubmatch(tok); m != nil {
|
2018-06-14 14:49:33 +00:00
|
|
|
count, err := strconv.ParseInt(m[2], 10, 32)
|
|
|
|
|
if err != nil {
|
2018-06-15 04:25:35 +00:00
|
|
|
return fmt.Errorf("bad token %q", tok)
|
2018-06-14 14:49:33 +00:00
|
|
|
}
|
|
|
|
|
data := strings.Repeat(m[1], int(count))
|
|
|
|
|
builder.AddFullData([]byte(data))
|
2018-06-15 04:25:35 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Named opcode.
|
|
|
|
|
if opcode, ok := shortFormOps[tok]; ok {
|
2015-04-21 06:40:59 +00:00
|
|
|
builder.AddOp(opcode)
|
2018-06-15 04:25:35 +00:00
|
|
|
return nil
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-15 04:25:35 +00:00
|
|
|
return fmt.Errorf("bad token %q", tok)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tokens := range tokenRE.FindAllStringSubmatch(script, -1) {
|
|
|
|
|
if err := handleToken(tokens[0]); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
return builder.Script()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseScriptFlags parses the provided flags string from the format used in the
|
|
|
|
|
// reference tests into ScriptFlags suitable for use in the script engine.
|
|
|
|
|
func parseScriptFlags(flagStr string) (ScriptFlags, error) {
|
|
|
|
|
var flags ScriptFlags
|
|
|
|
|
|
|
|
|
|
sFlags := strings.Split(flagStr, ",")
|
|
|
|
|
for _, flag := range sFlags {
|
|
|
|
|
switch flag {
|
|
|
|
|
case "":
|
|
|
|
|
// Nothing.
|
2016-01-20 21:46:42 +00:00
|
|
|
case "CHECKLOCKTIMEVERIFY":
|
|
|
|
|
flags |= ScriptVerifyCheckLockTimeVerify
|
2017-09-21 18:03:14 +00:00
|
|
|
case "CHECKSEQUENCEVERIFY":
|
|
|
|
|
flags |= ScriptVerifyCheckSequenceVerify
|
2015-04-20 19:16:46 +00:00
|
|
|
case "CLEANSTACK":
|
|
|
|
|
flags |= ScriptVerifyCleanStack
|
|
|
|
|
case "DISCOURAGE_UPGRADABLE_NOPS":
|
|
|
|
|
flags |= ScriptDiscourageUpgradableNops
|
|
|
|
|
case "NONE":
|
|
|
|
|
// Nothing.
|
|
|
|
|
case "SIGPUSHONLY":
|
|
|
|
|
flags |= ScriptVerifySigPushOnly
|
2017-09-08 00:52:36 +00:00
|
|
|
case "SHA256":
|
|
|
|
|
flags |= ScriptVerifySHA256
|
2015-04-20 19:16:46 +00:00
|
|
|
default:
|
|
|
|
|
return flags, fmt.Errorf("invalid flag: %s", flag)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return flags, nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// parseExpectedResult parses the provided expected result string into allowed
|
|
|
|
|
// script error codes. An error is returned if the expected result string is
|
|
|
|
|
// not supported.
|
|
|
|
|
func parseExpectedResult(expected string) ([]ErrorCode, error) {
|
|
|
|
|
switch expected {
|
|
|
|
|
case "OK":
|
|
|
|
|
return nil, nil
|
|
|
|
|
case "ERR_EARLY_RETURN":
|
|
|
|
|
return []ErrorCode{ErrEarlyReturn}, nil
|
|
|
|
|
case "ERR_EMPTY_STACK":
|
|
|
|
|
return []ErrorCode{ErrEmptyStack}, nil
|
|
|
|
|
case "ERR_EVAL_FALSE":
|
|
|
|
|
return []ErrorCode{ErrEvalFalse}, nil
|
|
|
|
|
case "ERR_SCRIPT_SIZE":
|
|
|
|
|
return []ErrorCode{ErrScriptTooBig}, nil
|
|
|
|
|
case "ERR_PUSH_SIZE":
|
|
|
|
|
return []ErrorCode{ErrElementTooBig}, nil
|
|
|
|
|
case "ERR_OP_COUNT":
|
|
|
|
|
return []ErrorCode{ErrTooManyOperations}, nil
|
|
|
|
|
case "ERR_STACK_SIZE":
|
|
|
|
|
return []ErrorCode{ErrStackOverflow}, nil
|
|
|
|
|
case "ERR_PUBKEY_COUNT":
|
|
|
|
|
return []ErrorCode{ErrInvalidPubKeyCount}, nil
|
|
|
|
|
case "ERR_SIG_COUNT":
|
|
|
|
|
return []ErrorCode{ErrInvalidSignatureCount}, nil
|
|
|
|
|
case "ERR_OUT_OF_RANGE":
|
|
|
|
|
return []ErrorCode{ErrNumOutOfRange}, nil
|
|
|
|
|
case "ERR_VERIFY":
|
|
|
|
|
return []ErrorCode{ErrVerify}, nil
|
|
|
|
|
case "ERR_EQUAL_VERIFY":
|
|
|
|
|
return []ErrorCode{ErrEqualVerify}, nil
|
|
|
|
|
case "ERR_DISABLED_OPCODE":
|
|
|
|
|
return []ErrorCode{ErrDisabledOpcode}, nil
|
|
|
|
|
case "ERR_RESERVED_OPCODE":
|
|
|
|
|
return []ErrorCode{ErrReservedOpcode}, nil
|
2018-09-11 20:21:42 +00:00
|
|
|
case "ERR_P2SH_STAKE_OPCODES":
|
|
|
|
|
return []ErrorCode{ErrP2SHStakeOpCodes}, nil
|
2018-06-29 17:46:14 +00:00
|
|
|
case "ERR_MALFORMED_PUSH":
|
|
|
|
|
return []ErrorCode{ErrMalformedPush}, nil
|
|
|
|
|
case "ERR_INVALID_STACK_OPERATION", "ERR_INVALID_ALTSTACK_OPERATION":
|
|
|
|
|
return []ErrorCode{ErrInvalidStackOperation}, nil
|
|
|
|
|
case "ERR_UNBALANCED_CONDITIONAL":
|
|
|
|
|
return []ErrorCode{ErrUnbalancedConditional}, nil
|
|
|
|
|
case "ERR_NEGATIVE_SUBSTR_INDEX":
|
|
|
|
|
return []ErrorCode{ErrNegativeSubstrIdx}, nil
|
|
|
|
|
case "ERR_OVERFLOW_SUBSTR_INDEX":
|
|
|
|
|
return []ErrorCode{ErrOverflowSubstrIdx}, nil
|
|
|
|
|
case "ERR_NEGATIVE_ROTATION":
|
|
|
|
|
return []ErrorCode{ErrNegativeRotation}, nil
|
|
|
|
|
case "ERR_OVERFLOW_ROTATION":
|
|
|
|
|
return []ErrorCode{ErrOverflowRotation}, nil
|
|
|
|
|
case "ERR_DIVIDE_BY_ZERO":
|
|
|
|
|
return []ErrorCode{ErrDivideByZero}, nil
|
|
|
|
|
case "ERR_NEGATIVE_SHIFT":
|
|
|
|
|
return []ErrorCode{ErrNegativeShift}, nil
|
|
|
|
|
case "ERR_OVERFLOW_SHIFT":
|
|
|
|
|
return []ErrorCode{ErrOverflowShift}, nil
|
|
|
|
|
case "ERR_MINIMAL_DATA":
|
|
|
|
|
return []ErrorCode{ErrMinimalData}, nil
|
|
|
|
|
case "ERR_SIG_HASH_TYPE":
|
|
|
|
|
return []ErrorCode{ErrInvalidSigHashType}, nil
|
|
|
|
|
case "ERR_SIG_TOO_SHORT":
|
|
|
|
|
return []ErrorCode{ErrSigTooShort}, nil
|
|
|
|
|
case "ERR_SIG_TOO_LONG":
|
|
|
|
|
return []ErrorCode{ErrSigTooLong}, nil
|
|
|
|
|
case "ERR_SIG_INVALID_SEQ_ID":
|
|
|
|
|
return []ErrorCode{ErrSigInvalidSeqID}, nil
|
|
|
|
|
case "ERR_SIG_INVALID_DATA_LEN":
|
|
|
|
|
return []ErrorCode{ErrSigInvalidDataLen}, nil
|
|
|
|
|
case "ERR_SIG_MISSING_S_TYPE_ID":
|
|
|
|
|
return []ErrorCode{ErrSigMissingSTypeID}, nil
|
|
|
|
|
case "ERR_SIG_MISSING_S_LEN":
|
|
|
|
|
return []ErrorCode{ErrSigMissingSLen}, nil
|
|
|
|
|
case "ERR_SIG_INVALID_S_LEN":
|
|
|
|
|
return []ErrorCode{ErrSigInvalidSLen}, nil
|
|
|
|
|
case "ERR_SIG_INVALID_R_INT_ID":
|
|
|
|
|
return []ErrorCode{ErrSigInvalidRIntID}, nil
|
|
|
|
|
case "ERR_SIG_ZERO_R_LEN":
|
|
|
|
|
return []ErrorCode{ErrSigZeroRLen}, nil
|
|
|
|
|
case "ERR_SIG_NEGATIVE_R":
|
|
|
|
|
return []ErrorCode{ErrSigNegativeR}, nil
|
|
|
|
|
case "ERR_SIG_TOO_MUCH_R_PADDING":
|
|
|
|
|
return []ErrorCode{ErrSigTooMuchRPadding}, nil
|
|
|
|
|
case "ERR_SIG_INVALID_S_INT_ID":
|
|
|
|
|
return []ErrorCode{ErrSigInvalidSIntID}, nil
|
|
|
|
|
case "ERR_SIG_ZERO_S_LEN":
|
|
|
|
|
return []ErrorCode{ErrSigZeroSLen}, nil
|
|
|
|
|
case "ERR_SIG_NEGATIVE_S":
|
|
|
|
|
return []ErrorCode{ErrSigNegativeS}, nil
|
|
|
|
|
case "ERR_SIG_TOO_MUCH_S_PADDING":
|
|
|
|
|
return []ErrorCode{ErrSigTooMuchSPadding}, nil
|
|
|
|
|
case "ERR_SIG_HIGH_S":
|
|
|
|
|
return []ErrorCode{ErrSigHighS}, nil
|
|
|
|
|
case "ERR_SIG_PUSHONLY":
|
|
|
|
|
return []ErrorCode{ErrNotPushOnly}, nil
|
|
|
|
|
case "ERR_PUBKEY_TYPE":
|
|
|
|
|
return []ErrorCode{ErrPubKeyType}, nil
|
|
|
|
|
case "ERR_CLEAN_STACK":
|
|
|
|
|
return []ErrorCode{ErrCleanStack}, nil
|
|
|
|
|
case "ERR_DISCOURAGE_UPGRADABLE_NOPS":
|
|
|
|
|
return []ErrorCode{ErrDiscourageUpgradableNOPs}, nil
|
|
|
|
|
case "ERR_NEGATIVE_LOCKTIME":
|
|
|
|
|
return []ErrorCode{ErrNegativeLockTime}, nil
|
|
|
|
|
case "ERR_UNSATISFIED_LOCKTIME":
|
|
|
|
|
return []ErrorCode{ErrUnsatisfiedLockTime}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unrecognized expected result in test data: %v",
|
|
|
|
|
expected)
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 19:16:46 +00:00
|
|
|
// createSpendTx generates a basic spending transaction given the passed
|
|
|
|
|
// signature and public key scripts.
|
|
|
|
|
func createSpendingTx(sigScript, pkScript []byte) *wire.MsgTx {
|
|
|
|
|
coinbaseTx := wire.NewMsgTx()
|
|
|
|
|
|
2016-01-20 21:46:42 +00:00
|
|
|
outPoint := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0),
|
2016-11-15 01:22:13 +00:00
|
|
|
wire.TxTreeRegular)
|
2018-06-14 23:12:52 +00:00
|
|
|
txIn := wire.NewTxIn(outPoint, 0, []byte{OP_0, OP_0})
|
2015-04-20 19:16:46 +00:00
|
|
|
txOut := wire.NewTxOut(0, pkScript)
|
|
|
|
|
coinbaseTx.AddTxIn(txIn)
|
|
|
|
|
coinbaseTx.AddTxOut(txOut)
|
|
|
|
|
|
|
|
|
|
spendingTx := wire.NewMsgTx()
|
2016-08-08 19:04:33 +00:00
|
|
|
coinbaseTxHash := coinbaseTx.TxHash()
|
2016-11-15 01:22:13 +00:00
|
|
|
outPoint = wire.NewOutPoint(&coinbaseTxHash, 0, wire.TxTreeRegular)
|
2018-06-14 23:12:52 +00:00
|
|
|
txIn = wire.NewTxIn(outPoint, 0, sigScript)
|
2015-04-20 19:16:46 +00:00
|
|
|
txOut = wire.NewTxOut(0, nil)
|
|
|
|
|
|
|
|
|
|
spendingTx.AddTxIn(txIn)
|
|
|
|
|
spendingTx.AddTxOut(txOut)
|
|
|
|
|
|
|
|
|
|
return spendingTx
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// testScripts ensures all of the passed script tests execute with the expected
|
|
|
|
|
// results with or without using a signature cache, as specified by the
|
|
|
|
|
// parameter.
|
|
|
|
|
func testScripts(t *testing.T, tests [][]string, useSigCache bool) {
|
|
|
|
|
// Create a signature cache to use only if requested.
|
|
|
|
|
var sigCache *SigCache
|
|
|
|
|
if useSigCache {
|
|
|
|
|
sigCache = NewSigCache(10)
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// "Format is: [scriptSig, scriptPubKey, flags, expectedScriptError, ...
|
|
|
|
|
// comments]"
|
|
|
|
|
for i, test := range tests {
|
|
|
|
|
// Skip single line comments.
|
|
|
|
|
if len(test) == 1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-04-14 02:56:10 +00:00
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// Construct a name for the test based on the comment and test data.
|
|
|
|
|
name, err := scriptTestName(test)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("TestScripts: invalid test #%d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the signature script from the test fields.
|
|
|
|
|
scriptSig, err := parseShortForm(test[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("%s: can't parse scriptSig; %v", name, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the public key script from the test fields.
|
|
|
|
|
scriptPubKey, err := parseShortForm(test[1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("%s: can't parse scriptPubkey; %v", name, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the script flags from the test fields.
|
|
|
|
|
flags, err := parseScriptFlags(test[2])
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("%s: %v", name, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the expected result from the test fields.
|
|
|
|
|
//
|
|
|
|
|
// Convert the expected result string into the allowed script error
|
|
|
|
|
// codes. This allows txscript to be more fine grained with its errors
|
|
|
|
|
// than the reference test data by allowing some of the test data errors
|
|
|
|
|
// to map to more than one possibility.
|
|
|
|
|
resultStr := test[3]
|
|
|
|
|
allowedErrorCodes, err := parseExpectedResult(resultStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("%s: %v", name, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate a transaction pair such that one spends from the other and
|
|
|
|
|
// the provided signature and public key scripts are used, then create a
|
|
|
|
|
// new engine to execute the scripts.
|
|
|
|
|
tx := createSpendingTx(scriptSig, scriptPubKey)
|
|
|
|
|
vm, err := NewEngine(scriptPubKey, tx, 0, flags, 0, sigCache)
|
|
|
|
|
if err == nil {
|
|
|
|
|
err = vm.Execute()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure there were no errors when the expected result is OK.
|
|
|
|
|
if resultStr == "OK" {
|
2015-09-24 23:22:00 +00:00
|
|
|
if err != nil {
|
2018-06-29 17:46:14 +00:00
|
|
|
t.Errorf("%s failed to execute: %v", name, err)
|
2015-09-24 23:22:00 +00:00
|
|
|
}
|
2018-06-29 17:46:14 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2015-09-24 23:22:00 +00:00
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// At this point an error was expected so ensure the result of the
|
|
|
|
|
// execution matches it.
|
|
|
|
|
success := false
|
|
|
|
|
for _, code := range allowedErrorCodes {
|
|
|
|
|
if IsErrorCode(err, code) {
|
|
|
|
|
success = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !success {
|
|
|
|
|
if serr, ok := err.(Error); ok {
|
|
|
|
|
t.Errorf("%s: want error codes %v, got %v", name,
|
|
|
|
|
allowedErrorCodes, serr.ErrorCode)
|
2015-09-24 23:22:00 +00:00
|
|
|
continue
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
2018-06-29 17:46:14 +00:00
|
|
|
t.Errorf("%s: want error codes %v, got err: %v (%T)", name,
|
|
|
|
|
allowedErrorCodes, err, err)
|
|
|
|
|
continue
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// TestScripts ensures all of the tests in script_tests.json execute with the
|
|
|
|
|
// expected results as defined in the test data.
|
|
|
|
|
func TestScripts(t *testing.T) {
|
|
|
|
|
file, err := ioutil.ReadFile("data/script_tests.json")
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
2018-06-29 17:46:14 +00:00
|
|
|
t.Fatalf("TestScripts: %v\n", err)
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tests [][]string
|
|
|
|
|
err = json.Unmarshal(file, &tests)
|
|
|
|
|
if err != nil {
|
2018-06-29 17:46:14 +00:00
|
|
|
t.Fatalf("TestScripts failed to unmarshal: %v", err)
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
2016-04-14 02:56:10 +00:00
|
|
|
|
2018-06-29 17:46:14 +00:00
|
|
|
// Run all script tests with and without the signature cache.
|
|
|
|
|
testScripts(t, tests, true)
|
|
|
|
|
testScripts(t, tests, false)
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
2016-01-17 04:35:40 +00:00
|
|
|
// testVecF64ToUint32 properly handles conversion of float64s read from the JSON
|
|
|
|
|
// test data to unsigned 32-bit integers. This is necessary because some of the
|
|
|
|
|
// test data uses -1 as a shortcut to mean max uint32 and direct conversion of a
|
|
|
|
|
// negative float to an unsigned int is implementation dependent and therefore
|
2019-08-16 22:37:58 +00:00
|
|
|
// doesn't result in the expected value on all platforms. This function works
|
2016-01-17 04:35:40 +00:00
|
|
|
// around that limitation by converting to a 32-bit signed integer first and
|
|
|
|
|
// then to a 32-bit unsigned integer which results in the expected behavior on
|
|
|
|
|
// all platforms.
|
|
|
|
|
func testVecF64ToUint32(f float64) uint32 {
|
|
|
|
|
return uint32(int32(f))
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 19:16:46 +00:00
|
|
|
// TestTxInvalidTests ensures all of the tests in tx_invalid.json fail as
|
|
|
|
|
// expected.
|
|
|
|
|
func TestTxInvalidTests(t *testing.T) {
|
|
|
|
|
file, err := ioutil.ReadFile("data/tx_invalid.json")
|
|
|
|
|
if err != nil {
|
2016-01-17 04:35:40 +00:00
|
|
|
t.Errorf("TestTxInvalidTests: %v\n", err)
|
2015-04-20 19:16:46 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tests [][]interface{}
|
|
|
|
|
err = json.Unmarshal(file, &tests)
|
|
|
|
|
if err != nil {
|
2016-01-17 04:35:40 +00:00
|
|
|
t.Errorf("TestTxInvalidTests couldn't Unmarshal: %v\n", err)
|
2015-04-20 19:16:46 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// form is either:
|
|
|
|
|
// ["this is a comment "]
|
|
|
|
|
// or:
|
|
|
|
|
// [[[previous hash, previous index, previous scriptPubKey]...,]
|
|
|
|
|
// serializedTransaction, verifyFlags]
|
|
|
|
|
testloop:
|
|
|
|
|
for i, test := range tests {
|
|
|
|
|
inputs, ok := test[0].([]interface{})
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(test) != 3 {
|
|
|
|
|
t.Errorf("bad test (bad length) %d: %v", i, test)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
serializedhex, ok := test[1].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (arg 2 not string) %d: %v", i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
serializedTx, err := hex.DecodeString(serializedhex)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test (arg 2 not hex %v) %d: %v", err, i,
|
|
|
|
|
test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-04 01:29:43 +00:00
|
|
|
tx, err := dcrutil.NewTxFromBytes(serializedTx)
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test (arg 2 not msgtx %v) %d: %v", err,
|
|
|
|
|
i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verifyFlags, ok := test[2].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (arg 3 not string) %d: %v", i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags, err := parseScriptFlags(verifyFlags)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test %d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prevOuts := make(map[wire.OutPoint][]byte)
|
|
|
|
|
for j, iinput := range inputs {
|
|
|
|
|
input, ok := iinput.([]interface{})
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (%dth input not array)"+
|
|
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(input) != 3 {
|
|
|
|
|
t.Errorf("bad test (%dth input wrong length)"+
|
|
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
previoustx, ok := input[0].(string)
|
|
|
|
|
if !ok {
|
2016-08-08 19:04:33 +00:00
|
|
|
t.Errorf("bad test (%dth input hash not string)"+
|
2015-04-20 19:16:46 +00:00
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-20 21:46:42 +00:00
|
|
|
prevhash, err := chainhash.NewHashFromStr(previoustx)
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
2016-08-08 19:04:33 +00:00
|
|
|
t.Errorf("bad test (%dth input hash not hash %v)"+
|
2015-04-20 19:16:46 +00:00
|
|
|
"%d: %v", j, err, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idxf, ok := input[1].(float64)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (%dth input idx not number)"+
|
|
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
2016-01-17 04:35:40 +00:00
|
|
|
idx := testVecF64ToUint32(idxf)
|
2015-04-20 19:16:46 +00:00
|
|
|
|
|
|
|
|
oscript, ok := input[2].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (%dth input script not "+
|
|
|
|
|
"string) %d: %v", j, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
script, err := parseShortForm(oscript)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test (%dth input script doesn't "+
|
|
|
|
|
"parse %v) %d: %v", j, err, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-15 01:22:13 +00:00
|
|
|
prevOuts[*wire.NewOutPoint(prevhash, idx, wire.TxTreeRegular)] = script
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for k, txin := range tx.MsgTx().TxIn {
|
|
|
|
|
pkScript, ok := prevOuts[txin.PreviousOutPoint]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (missing %dth input) %d:%v",
|
|
|
|
|
k, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
// These are meant to fail, so as soon as the first
|
|
|
|
|
// input fails the transaction has failed. (some of the
|
|
|
|
|
// test txns have good inputs, too..
|
2016-05-18 18:26:48 +00:00
|
|
|
vm, err := NewEngine(pkScript, tx.MsgTx(), k, flags, 0,
|
|
|
|
|
nil)
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 20:28:00 +00:00
|
|
|
err = vm.Execute()
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("test (%d:%v) succeeded when should fail",
|
|
|
|
|
i, test)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestTxValidTests ensures all of the tests in tx_valid.json pass as expected.
|
|
|
|
|
func TestTxValidTests(t *testing.T) {
|
|
|
|
|
file, err := ioutil.ReadFile("data/tx_valid.json")
|
|
|
|
|
if err != nil {
|
2016-01-17 04:35:40 +00:00
|
|
|
t.Errorf("TestTxValidTests: %v\n", err)
|
2015-04-20 19:16:46 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tests [][]interface{}
|
|
|
|
|
err = json.Unmarshal(file, &tests)
|
|
|
|
|
if err != nil {
|
2016-01-17 04:35:40 +00:00
|
|
|
t.Errorf("TestTxValidTests couldn't Unmarshal: %v\n", err)
|
2015-04-20 19:16:46 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// form is either:
|
|
|
|
|
// ["this is a comment "]
|
|
|
|
|
// or:
|
|
|
|
|
// [[[previous hash, previous index, previous scriptPubKey]...,]
|
|
|
|
|
// serializedTransaction, verifyFlags]
|
|
|
|
|
testloop:
|
|
|
|
|
for i, test := range tests {
|
|
|
|
|
inputs, ok := test[0].([]interface{})
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(test) != 3 {
|
|
|
|
|
t.Errorf("bad test (bad length) %d: %v", i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
serializedhex, ok := test[1].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (arg 2 not string) %d: %v", i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
serializedTx, err := hex.DecodeString(serializedhex)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test (arg 2 not hex %v) %d: %v", err, i,
|
|
|
|
|
test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-04 01:29:43 +00:00
|
|
|
tx, err := dcrutil.NewTxFromBytes(serializedTx)
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test (arg 2 not msgtx %v) %d: %v", err,
|
|
|
|
|
i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verifyFlags, ok := test[2].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (arg 3 not string) %d: %v", i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags, err := parseScriptFlags(verifyFlags)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test %d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prevOuts := make(map[wire.OutPoint][]byte)
|
|
|
|
|
for j, iinput := range inputs {
|
|
|
|
|
input, ok := iinput.([]interface{})
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (%dth input not array)"+
|
|
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(input) != 3 {
|
|
|
|
|
t.Errorf("bad test (%dth input wrong length)"+
|
|
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
previoustx, ok := input[0].(string)
|
|
|
|
|
if !ok {
|
2016-08-08 19:04:33 +00:00
|
|
|
t.Errorf("bad test (%dth input hash not string)"+
|
2015-04-20 19:16:46 +00:00
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-20 21:46:42 +00:00
|
|
|
prevhash, err := chainhash.NewHashFromStr(previoustx)
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
2016-08-08 19:04:33 +00:00
|
|
|
t.Errorf("bad test (%dth input hash not hash %v)"+
|
2015-04-20 19:16:46 +00:00
|
|
|
"%d: %v", j, err, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idxf, ok := input[1].(float64)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (%dth input idx not number)"+
|
|
|
|
|
"%d: %v", j, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-01-17 04:35:40 +00:00
|
|
|
idx := testVecF64ToUint32(idxf)
|
2015-04-20 19:16:46 +00:00
|
|
|
|
|
|
|
|
oscript, ok := input[2].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (%dth input script not "+
|
|
|
|
|
"string) %d: %v", j, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
script, err := parseShortForm(oscript)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("bad test (%dth input script doesn't "+
|
|
|
|
|
"parse %v) %d: %v", j, err, i, test)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-15 01:22:13 +00:00
|
|
|
prevOuts[*wire.NewOutPoint(prevhash, idx, wire.TxTreeRegular)] = script
|
2015-04-20 19:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for k, txin := range tx.MsgTx().TxIn {
|
|
|
|
|
pkScript, ok := prevOuts[txin.PreviousOutPoint]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("bad test (missing %dth input) %d:%v",
|
|
|
|
|
k, i, test)
|
|
|
|
|
continue testloop
|
|
|
|
|
}
|
2016-05-18 18:26:48 +00:00
|
|
|
vm, err := NewEngine(pkScript, tx.MsgTx(), k, flags, 0,
|
|
|
|
|
nil)
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("test (%d:%v:%d) failed to create "+
|
|
|
|
|
"script: %v", i, test, k, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-20 20:28:00 +00:00
|
|
|
err = vm.Execute()
|
2015-04-20 19:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("test (%d:%v:%d) failed to execute: "+
|
|
|
|
|
"%v", i, test, k, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-29 22:55:03 +00:00
|
|
|
|
|
|
|
|
// parseSigHashExpectedResult parses the provided expected result string into
|
|
|
|
|
// allowed error codes. An error is returned if the expected result string is
|
|
|
|
|
// not supported.
|
2018-06-29 16:58:31 +00:00
|
|
|
func parseSigHashExpectedResult(expected string) (*ErrorCode, error) {
|
2018-04-29 22:55:03 +00:00
|
|
|
switch expected {
|
|
|
|
|
case "OK":
|
|
|
|
|
return nil, nil
|
|
|
|
|
case "SIGHASH_SINGLE_IDX":
|
2018-06-29 16:58:31 +00:00
|
|
|
code := ErrInvalidSigHashSingleIndex
|
|
|
|
|
return &code, nil
|
2018-04-29 22:55:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unrecognized expected result in test data: %v",
|
|
|
|
|
expected)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestCalcSignatureHashReference runs the reference signature hash calculation
|
|
|
|
|
// tests in sighash.json.
|
|
|
|
|
func TestCalcSignatureHashReference(t *testing.T) {
|
|
|
|
|
file, err := ioutil.ReadFile("data/sighash.json")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("TestCalcSignatureHash: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tests [][]interface{}
|
|
|
|
|
err = json.Unmarshal(file, &tests)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("TestCalcSignatureHash couldn't Unmarshal: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 06:11:08 +00:00
|
|
|
const scriptVersion = 0
|
2018-04-29 22:55:03 +00:00
|
|
|
for i, test := range tests {
|
|
|
|
|
// Skip comment lines.
|
|
|
|
|
if len(test) == 1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure test is well formed.
|
|
|
|
|
if len(test) < 6 || len(test) > 7 {
|
|
|
|
|
t.Fatalf("Test #%d: wrong length %d", i, len(test))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the transaction from the test fields.
|
|
|
|
|
txHex, ok := test[0].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test #%d: transaction is not a string", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
rawTx, err := hex.DecodeString(txHex)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Test #%d: unable to parse transaction: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
var tx wire.MsgTx
|
|
|
|
|
err = tx.Deserialize(bytes.NewReader(rawTx))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Test #%d: unable to deserialize transaction: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the script from the test fields.
|
|
|
|
|
subScriptStr, ok := test[1].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test #%d: script is not a string", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
subScript, err := hex.DecodeString(subScriptStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Test #%d: unable to decode script: %v", i,
|
|
|
|
|
err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-03-13 06:11:08 +00:00
|
|
|
err = checkScriptParses(scriptVersion, subScript)
|
2018-04-29 22:55:03 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Test #%d: unable to parse script: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract the input index from the test fields.
|
|
|
|
|
inputIdxF64, ok := test[2].(float64)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test #%d: input idx is not numeric", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the hash type from the test fields.
|
|
|
|
|
hashTypeF64, ok := test[3].(float64)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test #%d: hash type is not numeric", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
hashType := SigHashType(testVecF64ToUint32(hashTypeF64))
|
|
|
|
|
|
|
|
|
|
// Extract and parse the signature hash from the test fields.
|
|
|
|
|
expectedHashStr, ok := test[4].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test #%d: signature hash is not a string", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
expectedHash, err := hex.DecodeString(expectedHashStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Test #%d: unable to sig hash: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract and parse the expected result from the test fields.
|
|
|
|
|
expectedErrStr, ok := test[5].(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test #%d: result field is not a string", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
expectedErr, err := parseSigHashExpectedResult(expectedErrStr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Errorf("Test #%d: %v", i, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate the signature hash and verify expected result.
|
2019-03-13 06:11:08 +00:00
|
|
|
hash, err := CalcSignatureHash(subScript, hashType, &tx,
|
2018-04-29 22:55:03 +00:00
|
|
|
int(inputIdxF64), nil)
|
2018-06-29 16:58:31 +00:00
|
|
|
if (err == nil) != (expectedErr == nil) ||
|
|
|
|
|
expectedErr != nil && !IsErrorCode(err, *expectedErr) {
|
|
|
|
|
|
|
|
|
|
if serr, ok := err.(Error); ok {
|
|
|
|
|
t.Errorf("Test #%d: want error code %v, got %v", i, expectedErr,
|
|
|
|
|
serr.ErrorCode)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
t.Errorf("Test #%d: want error code %v, got err: %v (%T)", i,
|
|
|
|
|
expectedErr, err, err)
|
2018-04-29 22:55:03 +00:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !bytes.Equal(hash, expectedHash) {
|
2018-06-29 16:58:31 +00:00
|
|
|
t.Errorf("Test #%d: signature hash mismatch - got %x, want %x", i,
|
|
|
|
|
hash, expectedHash)
|
2018-04-29 22:55:03 +00:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|