// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/wire"
)

// 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))
	}

	// 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])
	} else {
		name = fmt.Sprintf("test ([%s, %s, %s])", test[0], test[1],
			test[2])
	}
	return name, nil
}

// 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.
		case "CHECKLOCKTIMEVERIFY":
			flags |= ScriptVerifyCheckLockTimeVerify
		case "CHECKSEQUENCEVERIFY":
			flags |= ScriptVerifyCheckSequenceVerify
		case "CLEANSTACK":
			flags |= ScriptVerifyCleanStack
		case "DISCOURAGE_UPGRADABLE_NOPS":
			flags |= ScriptDiscourageUpgradableNops
		case "NONE":
			// Nothing.
		case "SIGPUSHONLY":
			flags |= ScriptVerifySigPushOnly
		case "SHA256":
			flags |= ScriptVerifySHA256
		case "TREASURY":
			flags |= ScriptVerifyTreasury
		default:
			return flags, fmt.Errorf("invalid flag: %s", flag)
		}
	}
	return flags, nil
}

// parseExpectedResult parses the provided expected result string into allowed
// script error kinds.  An error is returned if the expected result string is
// not supported.
func parseExpectedResult(expected string) ([]ErrorKind, error) {
	switch expected {
	case "OK":
		return nil, nil
	case "ERR_EARLY_RETURN":
		return []ErrorKind{ErrEarlyReturn}, nil
	case "ERR_EMPTY_STACK":
		return []ErrorKind{ErrEmptyStack}, nil
	case "ERR_EVAL_FALSE":
		return []ErrorKind{ErrEvalFalse}, nil
	case "ERR_SCRIPT_SIZE":
		return []ErrorKind{ErrScriptTooBig}, nil
	case "ERR_PUSH_SIZE":
		return []ErrorKind{ErrElementTooBig}, nil
	case "ERR_OP_COUNT":
		return []ErrorKind{ErrTooManyOperations}, nil
	case "ERR_STACK_SIZE":
		return []ErrorKind{ErrStackOverflow}, nil
	case "ERR_PUBKEY_COUNT":
		return []ErrorKind{ErrInvalidPubKeyCount}, nil
	case "ERR_SIG_COUNT":
		return []ErrorKind{ErrInvalidSignatureCount}, nil
	case "ERR_OUT_OF_RANGE":
		return []ErrorKind{ErrNumOutOfRange}, nil
	case "ERR_VERIFY":
		return []ErrorKind{ErrVerify}, nil
	case "ERR_EQUAL_VERIFY":
		return []ErrorKind{ErrEqualVerify}, nil
	case "ERR_DISABLED_OPCODE":
		return []ErrorKind{ErrDisabledOpcode}, nil
	case "ERR_RESERVED_OPCODE":
		return []ErrorKind{ErrReservedOpcode}, nil
	case "ERR_P2SH_STAKE_OPCODES":
		return []ErrorKind{ErrP2SHStakeOpCodes}, nil
	case "ERR_MALFORMED_PUSH":
		return []ErrorKind{ErrMalformedPush}, nil
	case "ERR_INVALID_STACK_OPERATION", "ERR_INVALID_ALTSTACK_OPERATION":
		return []ErrorKind{ErrInvalidStackOperation}, nil
	case "ERR_UNBALANCED_CONDITIONAL":
		return []ErrorKind{ErrUnbalancedConditional}, nil
	case "ERR_NEGATIVE_SUBSTR_INDEX":
		return []ErrorKind{ErrNegativeSubstrIdx}, nil
	case "ERR_OVERFLOW_SUBSTR_INDEX":
		return []ErrorKind{ErrOverflowSubstrIdx}, nil
	case "ERR_NEGATIVE_ROTATION":
		return []ErrorKind{ErrNegativeRotation}, nil
	case "ERR_OVERFLOW_ROTATION":
		return []ErrorKind{ErrOverflowRotation}, nil
	case "ERR_DIVIDE_BY_ZERO":
		return []ErrorKind{ErrDivideByZero}, nil
	case "ERR_NEGATIVE_SHIFT":
		return []ErrorKind{ErrNegativeShift}, nil
	case "ERR_OVERFLOW_SHIFT":
		return []ErrorKind{ErrOverflowShift}, nil
	case "ERR_MINIMAL_DATA":
		return []ErrorKind{ErrMinimalData}, nil
	case "ERR_SIG_HASH_TYPE":
		return []ErrorKind{ErrInvalidSigHashType}, nil
	case "ERR_SIG_TOO_SHORT":
		return []ErrorKind{ErrSigTooShort}, nil
	case "ERR_SIG_TOO_LONG":
		return []ErrorKind{ErrSigTooLong}, nil
	case "ERR_SIG_INVALID_SEQ_ID":
		return []ErrorKind{ErrSigInvalidSeqID}, nil
	case "ERR_SIG_INVALID_DATA_LEN":
		return []ErrorKind{ErrSigInvalidDataLen}, nil
	case "ERR_SIG_MISSING_S_TYPE_ID":
		return []ErrorKind{ErrSigMissingSTypeID}, nil
	case "ERR_SIG_MISSING_S_LEN":
		return []ErrorKind{ErrSigMissingSLen}, nil
	case "ERR_SIG_INVALID_S_LEN":
		return []ErrorKind{ErrSigInvalidSLen}, nil
	case "ERR_SIG_INVALID_R_INT_ID":
		return []ErrorKind{ErrSigInvalidRIntID}, nil
	case "ERR_SIG_ZERO_R_LEN":
		return []ErrorKind{ErrSigZeroRLen}, nil
	case "ERR_SIG_NEGATIVE_R":
		return []ErrorKind{ErrSigNegativeR}, nil
	case "ERR_SIG_TOO_MUCH_R_PADDING":
		return []ErrorKind{ErrSigTooMuchRPadding}, nil
	case "ERR_SIG_INVALID_S_INT_ID":
		return []ErrorKind{ErrSigInvalidSIntID}, nil
	case "ERR_SIG_ZERO_S_LEN":
		return []ErrorKind{ErrSigZeroSLen}, nil
	case "ERR_SIG_NEGATIVE_S":
		return []ErrorKind{ErrSigNegativeS}, nil
	case "ERR_SIG_TOO_MUCH_S_PADDING":
		return []ErrorKind{ErrSigTooMuchSPadding}, nil
	case "ERR_SIG_HIGH_S":
		return []ErrorKind{ErrSigHighS}, nil
	case "ERR_SIG_PUSHONLY":
		return []ErrorKind{ErrNotPushOnly}, nil
	case "ERR_PUBKEY_TYPE":
		return []ErrorKind{ErrPubKeyType}, nil
	case "ERR_CLEAN_STACK":
		return []ErrorKind{ErrCleanStack}, nil
	case "ERR_DISCOURAGE_UPGRADABLE_NOPS":
		return []ErrorKind{ErrDiscourageUpgradableNOPs}, nil
	case "ERR_NEGATIVE_LOCKTIME":
		return []ErrorKind{ErrNegativeLockTime}, nil
	case "ERR_UNSATISFIED_LOCKTIME":
		return []ErrorKind{ErrUnsatisfiedLockTime}, nil
	}

	return nil, fmt.Errorf("unrecognized expected result in test data: %v",
		expected)
}

// createSpendTx generates a basic spending transaction given the passed
// signature and public key scripts.
func createSpendingTx(sigScript, pkScript []byte) *wire.MsgTx {
	coinbaseTx := wire.NewMsgTx()

	outPoint := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0),
		wire.TxTreeRegular)
	txIn := wire.NewTxIn(outPoint, 0, []byte{OP_0, OP_0})
	txOut := wire.NewTxOut(0, pkScript)
	coinbaseTx.AddTxIn(txIn)
	coinbaseTx.AddTxOut(txOut)

	spendingTx := wire.NewMsgTx()
	coinbaseTxHash := coinbaseTx.TxHash()
	outPoint = wire.NewOutPoint(&coinbaseTxHash, 0, wire.TxTreeRegular)
	txIn = wire.NewTxIn(outPoint, 0, sigScript)
	txOut = wire.NewTxOut(0, nil)

	spendingTx.AddTxIn(txIn)
	spendingTx.AddTxOut(txOut)

	return spendingTx
}

// 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 {
		var err error
		sigCache, err = NewSigCache(10)
		if err != nil {
			t.Fatalf("error creating NewSigCache: %v", err)
		}
	}

	// "Format is: [scriptSig, scriptPubKey, flags, expectedScriptError, ...
	//   comments]"
	for i, test := range tests {
		// Skip single line comments.
		if len(test) == 1 {
			continue
		}

		// 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 := parseShortFormV0(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 := parseShortFormV0(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 errors.
		// 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]
		allowErrorKinds, 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" {
			if err != nil {
				t.Errorf("%s failed to execute: %v", name, err)
			}
			continue
		}

		// At this point an error was expected so ensure the result of the
		// execution matches it.
		success := false
		for _, kind := range allowErrorKinds {
			if errors.Is(err, kind) {
				success = true
				break
			}
		}
		if !success {
			var serr Error
			if errors.As(err, &serr) {
				t.Errorf("%s: want error kinds %v, got %v", name,
					allowErrorKinds, serr.Err)
				continue
			}
			t.Errorf("%s: want error kinds %v, got err: %v (%T)", name,
				allowErrorKinds, err, err)
			continue
		}
	}
}

// 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 := os.ReadFile(filepath.Join(testDataPath, "script_tests.json"))
	if err != nil {
		t.Fatalf("TestScripts: %v\n", err)
	}

	var tests [][]string
	err = json.Unmarshal(file, &tests)
	if err != nil {
		t.Fatalf("TestScripts failed to unmarshal: %v", err)
	}

	// Run all script tests with and without the signature cache.
	testScripts(t, tests, true)
	testScripts(t, tests, false)
}

// 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
// doesn't result in the expected value on all platforms.  This function works
// 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))
}

// TestTxInvalidTests ensures all of the tests in tx_invalid.json fail as
// expected.
func TestTxInvalidTests(t *testing.T) {
	file, err := os.ReadFile(filepath.Join(testDataPath, "tx_invalid.json"))
	if err != nil {
		t.Errorf("TestTxInvalidTests: %v\n", err)
		return
	}

	var tests [][]interface{}
	err = json.Unmarshal(file, &tests)
	if err != nil {
		t.Errorf("TestTxInvalidTests couldn't Unmarshal: %v\n", err)
		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
		}

		var tx wire.MsgTx
		if err := tx.Deserialize(bytes.NewReader(serializedTx)); 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 {
				t.Errorf("bad test (%dth input hash not string)"+
					"%d: %v", j, i, test)
				continue testloop
			}

			prevhash, err := chainhash.NewHashFromStr(previoustx)
			if err != nil {
				t.Errorf("bad test (%dth input hash not hash %v)"+
					"%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
			}
			idx := testVecF64ToUint32(idxf)

			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 := parseShortFormV0(oscript)
			if err != nil {
				t.Errorf("bad test (%dth input script doesn't "+
					"parse %v) %d: %v", j, err, i, test)
				continue testloop
			}

			prevOuts[*wire.NewOutPoint(prevhash, idx, wire.TxTreeRegular)] = script
		}

		for k, txIn := range tx.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..
			vm, err := NewEngine(pkScript, &tx, k, flags, 0, nil)
			if err != nil {
				continue testloop
			}

			err = vm.Execute()
			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 := os.ReadFile(filepath.Join(testDataPath, "tx_valid.json"))
	if err != nil {
		t.Errorf("TestTxValidTests: %v\n", err)
		return
	}

	var tests [][]interface{}
	err = json.Unmarshal(file, &tests)
	if err != nil {
		t.Errorf("TestTxValidTests couldn't Unmarshal: %v\n", err)
		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
		}

		var tx wire.MsgTx
		if err := tx.Deserialize(bytes.NewReader(serializedTx)); 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 {
				t.Errorf("bad test (%dth input hash not string)"+
					"%d: %v", j, i, test)
				continue
			}

			prevhash, err := chainhash.NewHashFromStr(previoustx)
			if err != nil {
				t.Errorf("bad test (%dth input hash not hash %v)"+
					"%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
			}
			idx := testVecF64ToUint32(idxf)

			oscript, ok := input[2].(string)
			if !ok {
				t.Errorf("bad test (%dth input script not "+
					"string) %d: %v", j, i, test)
				continue
			}

			script, err := parseShortFormV0(oscript)
			if err != nil {
				t.Errorf("bad test (%dth input script doesn't "+
					"parse %v) %d: %v", j, err, i, test)
				continue
			}

			prevOuts[*wire.NewOutPoint(prevhash, idx, wire.TxTreeRegular)] = script
		}

		for k, txIn := range tx.TxIn {
			pkScript, ok := prevOuts[txIn.PreviousOutPoint]
			if !ok {
				t.Errorf("bad test (missing %dth input) %d:%v",
					k, i, test)
				continue testloop
			}
			vm, err := NewEngine(pkScript, &tx, k, flags, 0, nil)
			if err != nil {
				t.Errorf("test (%d:%v:%d) failed to create "+
					"script: %v", i, test, k, err)
				continue
			}

			err = vm.Execute()
			if err != nil {
				t.Errorf("test (%d:%v:%d) failed to execute: "+
					"%v", i, test, k, err)
				continue
			}
		}
	}
}

// parseSigHashExpectedResult parses the provided expected result string into
// allowed error kinds.  An error is returned if the expected result string is
// not supported.
func parseSigHashExpectedResult(expected string) (error, error) {
	switch expected {
	case "OK":
		return nil, nil
	case "SIGHASH_SINGLE_IDX":
		return ErrInvalidSigHashSingleIndex, nil
	}

	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 := os.ReadFile(filepath.Join(testDataPath, "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)
	}

	const scriptVersion = 0
	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
		}
		err = checkScriptParses(scriptVersion, subScript)
		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.
		hash, err := CalcSignatureHash(subScript, hashType, &tx,
			int(inputIdxF64), nil)
		if !errors.Is(err, expectedErr) {
			t.Errorf("Test #%d: want error kind %v, got err: %v (%T)", i,
				expectedErr, err, err)
			continue
		}
		if !bytes.Equal(hash, expectedHash) {
			t.Errorf("Test #%d: signature hash mismatch - got %x, want %x", i,
				hash, expectedHash)
			continue
		}
	}
}
