package txscript import ( "bytes" "encoding/hex" "encoding/json" "errors" "fmt" "github.com/p9c/p9/pkg/amt" "io/ioutil" "strconv" "strings" "testing" "github.com/p9c/p9/pkg/chainhash" "github.com/p9c/p9/pkg/util" "github.com/p9c/p9/pkg/wire" ) // scriptTestName returns a descriptive test name for the given reference script test data. func scriptTestName(test []interface{}) (string, error) { // Account for any optional leading witness data. var witnessOffset int if _, ok := test[0].([]interface{}); ok { witnessOffset++ } // In addition to the optional leading witness data, the test must consist of at // least a signature script, public key script, flags, and expected error. // Finally, it may optionally contain a comment. if len(test) < witnessOffset+4 || len(test) > witnessOffset+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) == witnessOffset+5 { name = fmt.Sprintf("test (%s)", test[witnessOffset+4]) } else { name = fmt.Sprintf( "test ([%s, %s, %s])", test[witnessOffset], test[witnessOffset+1], test[witnessOffset+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:]) } // parseWitnessStack parses a json array of witness items encoded as hex into a // slice of witness elements. func parseWitnessStack(elements []interface{}) ([][]byte, error) { witness := make([][]byte, len(elements)) for i, e := range elements { witElement, e := hex.DecodeString(e.(string)) if e != nil { return nil, e } witness[i] = witElement } return witness, nil } // 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 // parseShortForm parses a string as as used in the Bitcoin Core reference tests into the script it came from. // // The format used for these tests is pretty simple if ad-hoc: // // - Opcodes other than the push opcodes and unknown are present as either OP_NAME or just NAME // - Plain numbers are made into push operations // - Numbers beginning with 0x are inserted into the []byte as-is (so 0x14 is OP_DATA_20) // - Single quoted strings are pushed as data // - Anything else is an error func parseShortForm(script string) ([]byte, error) { // 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 } } shortFormOps = ops } // Split only does one separator so convert all \n and tab into space. script = strings.Replace(script, "\n", " ", -1) script = strings.Replace(script, "\t", " ", -1) tokens := strings.Split(script, " ") builder := NewScriptBuilder() for _, tok := range tokens { if len(tok) == 0 { continue } // if parses as a plain number if num, e := strconv.ParseInt(tok, 10, 64); e == nil { builder.AddInt64(num) continue } else if bts, e := parseHex(tok); e == nil { // 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...) } } else if len(tok) >= 2 && tok[0] == '\'' && tok[len(tok)-1] == '\'' { builder.AddFullData([]byte(tok[1 : len(tok)-1])) } else if opcode, ok := shortFormOps[tok]; ok { builder.AddOp(opcode) } else { return nil, fmt.Errorf("bad token %q", tok) } } 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. case "CHECKLOCKTIMEVERIFY": flags |= ScriptVerifyCheckLockTimeVerify case "CHECKSEQUENCEVERIFY": flags |= ScriptVerifyCheckSequenceVerify case "CLEANSTACK": flags |= ScriptVerifyCleanStack case "DERSIG": flags |= ScriptVerifyDERSignatures case "DISCOURAGE_UPGRADABLE_NOPS": flags |= ScriptDiscourageUpgradableNops case "LOW_S": flags |= ScriptVerifyLowS case "MINIMALDATA": flags |= ScriptVerifyMinimalData case "NONE": // Nothing. case "NULLDUMMY": flags |= ScriptStrictMultiSig case "NULLFAIL": flags |= ScriptVerifyNullFail case "P2SH": flags |= ScriptBip16 case "SIGPUSHONLY": flags |= ScriptVerifySigPushOnly case "STRICTENC": flags |= ScriptVerifyStrictEncoding case "WITNESS": flags |= ScriptVerifyWitness case "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM": flags |= ScriptVerifyDiscourageUpgradeableWitnessProgram case "MINIMALIF": flags |= ScriptVerifyMinimalIf case "WITNESS_PUBKEYTYPE": flags |= ScriptVerifyWitnessPubKeyType default: return flags, fmt.Errorf("invalid flag: %s", flag) } } return flags, nil } // 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 "UNKNOWN_ERROR": return []ErrorCode{ErrNumberTooBig, ErrMinimalData}, nil case "PUBKEYTYPE": return []ErrorCode{ErrPubKeyType}, nil case "SIG_DER": return []ErrorCode{ ErrSigTooShort, ErrSigTooLong, ErrSigInvalidSeqID, ErrSigInvalidDataLen, ErrSigMissingSTypeID, ErrSigMissingSLen, ErrSigInvalidSLen, ErrSigInvalidRIntID, ErrSigZeroRLen, ErrSigNegativeR, ErrSigTooMuchRPadding, ErrSigInvalidSIntID, ErrSigZeroSLen, ErrSigNegativeS, ErrSigTooMuchSPadding, ErrInvalidSigHashType, }, nil case "EVAL_FALSE": return []ErrorCode{ErrEvalFalse, ErrEmptyStack}, nil case "EQUALVERIFY": return []ErrorCode{ErrEqualVerify}, nil case "NULLFAIL": return []ErrorCode{ErrNullFail}, nil case "SIG_HIGH_S": return []ErrorCode{ErrSigHighS}, nil case "SIG_HASHTYPE": return []ErrorCode{ErrInvalidSigHashType}, nil case "SIG_NULLDUMMY": return []ErrorCode{ErrSigNullDummy}, nil case "SIG_PUSHONLY": return []ErrorCode{ErrNotPushOnly}, nil case "CLEANSTACK": return []ErrorCode{ErrCleanStack}, nil case "BAD_OPCODE": return []ErrorCode{ErrReservedOpcode, ErrMalformedPush}, nil case "UNBALANCED_CONDITIONAL": return []ErrorCode{ ErrUnbalancedConditional, ErrInvalidStackOperation, }, nil case "OP_RETURN": return []ErrorCode{ErrEarlyReturn}, nil case "VERIFY": return []ErrorCode{ErrVerify}, nil case "INVALID_STACK_OPERATION", "INVALID_ALTSTACK_OPERATION": return []ErrorCode{ErrInvalidStackOperation}, nil case "DISABLED_OPCODE": return []ErrorCode{ErrDisabledOpcode}, nil case "DISCOURAGE_UPGRADABLE_NOPS": return []ErrorCode{ErrDiscourageUpgradableNOPs}, nil case "PUSH_SIZE": return []ErrorCode{ErrElementTooBig}, nil case "OP_COUNT": return []ErrorCode{ErrTooManyOperations}, nil case "STACK_SIZE": return []ErrorCode{ErrStackOverflow}, nil case "SCRIPT_SIZE": return []ErrorCode{ErrScriptTooBig}, nil case "PUBKEY_COUNT": return []ErrorCode{ErrInvalidPubKeyCount}, nil case "SIG_COUNT": return []ErrorCode{ErrInvalidSignatureCount}, nil case "MINIMALDATA": return []ErrorCode{ErrMinimalData}, nil case "NEGATIVE_LOCKTIME": return []ErrorCode{ErrNegativeLockTime}, nil case "UNSATISFIED_LOCKTIME": return []ErrorCode{ErrUnsatisfiedLockTime}, nil case "MINIMALIF": return []ErrorCode{ErrMinimalIf}, nil case "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM": return []ErrorCode{ErrDiscourageUpgradableWitnessProgram}, nil case "WITNESS_PROGRAM_WRONG_LENGTH": return []ErrorCode{ErrWitnessProgramWrongLength}, nil case "WITNESS_PROGRAM_WITNESS_EMPTY": return []ErrorCode{ErrWitnessProgramEmpty}, nil case "WITNESS_PROGRAM_MISMATCH": return []ErrorCode{ErrWitnessProgramMismatch}, nil case "WITNESS_MALLEATED": return []ErrorCode{ErrWitnessMalleated}, nil case "WITNESS_MALLEATED_P2SH": return []ErrorCode{ErrWitnessMalleatedP2SH}, nil case "WITNESS_UNEXPECTED": return []ErrorCode{ErrWitnessUnexpected}, nil case "WITNESS_PUBKEYTYPE": return []ErrorCode{ErrWitnessPubKeyType}, nil } return nil, fmt.Errorf( "unrecognized expected result in test data: %v", expected, ) } // createSpendTx generates a basic spending transaction given the passed // signature, witness and public key scripts. func createSpendingTx( witness [][]byte, sigScript, pkScript []byte, outputValue int64, ) *wire.MsgTx { coinbaseTx := wire.NewMsgTx(wire.TxVersion) outPoint := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0)) txIn := wire.NewTxIn(outPoint, []byte{OP_0, OP_0}, nil) txOut := wire.NewTxOut(outputValue, pkScript) coinbaseTx.AddTxIn(txIn) coinbaseTx.AddTxOut(txOut) spendingTx := wire.NewMsgTx(wire.TxVersion) coinbaseTxSha := coinbaseTx.TxHash() outPoint = wire.NewOutPoint(&coinbaseTxSha, 0) txIn = wire.NewTxIn(outPoint, sigScript, witness) txOut = wire.NewTxOut(outputValue, nil) spendingTx.AddTxIn(txIn) spendingTx.AddTxOut(txOut) return spendingTx } // scriptWithInputVal wraps a target pkScript with the value of the output in // which it is contained. The inputVal is necessary in order to properly // validate inputs which spend nested, or native witness programs. type scriptWithInputVal struct { inputVal int64 pkScript []byte } // 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 [][]interface{}, useSigCache bool) { // Create a signature cache to use only if requested. var sigCache *SigCache if useSigCache { sigCache = NewSigCache(10) } for i, test := range tests { // "Format is: [[wit..., amount]?, scriptSig, scriptPubKey, // flags, expected_scripterror, ... comments]" // Skip single line comments. if len(test) == 1 { continue } // Construct a name for the test based on the comment and test data. name, e := scriptTestName(test) if e != nil { t.Errorf("TestScripts: invalid test #%d: %v", i, e) continue } var ( witness wire.TxWitness inputAmt amt.Amount ) // When the first field of the test data is a slice it contains witness data and // everything else is offset by 1 as a result. witnessOffset := 0 if witnessData, ok := test[0].([]interface{}); ok { witnessOffset++ // If this is a witness test, then the final element within the slice is the // input amount, so we ignore all but the last element in order to parse the // witness stack. strWitnesses := witnessData[:len(witnessData)-1] witness, e = parseWitnessStack(strWitnesses) if e != nil { t.Errorf("%s: can't parse witness; %v", name, e) continue } inputAmt, e = amt.NewAmount(witnessData[len(witnessData)-1].(float64)) if e != nil { t.Errorf( "%s: can't parse input amt: %v", name, e, ) continue } } // Extract and parse the signature script from the test fields. scriptSigStr, ok := test[witnessOffset].(string) if !ok { t.Errorf("%s: signature script is not a string", name) continue } scriptSig, e := parseShortForm(scriptSigStr) if e != nil { t.Errorf( "%s: can't parse signature script: %v", name, e, ) continue } // Extract and parse the public key script from the test fields. scriptPubKeyStr, ok := test[witnessOffset+1].(string) if !ok { t.Errorf("%s: public key script is not a string", name) continue } scriptPubKey, e := parseShortForm(scriptPubKeyStr) if e != nil { t.Errorf( "%s: can't parse public key script: %v", name, e, ) continue } // Extract and parse the script flags from the test fields. flagsStr, ok := test[witnessOffset+2].(string) if !ok { t.Errorf("%s: flags field is not a string", name) continue } flags, e := parseScriptFlags(flagsStr) if e != nil { t.Errorf("%s: %v", name, e) continue } // Extract and parse the expected result from the test fields. Convert the expected result string into the // allowed script error codes. This is necessary because txscript is more fine grained with its errors than the // reference test data, so some of the reference test data errors map to more than one possibility. resultStr, ok := test[witnessOffset+3].(string) if !ok { t.Errorf("%s: result field is not a string", name) continue } allowedErrorCodes, e := parseExpectedResult(resultStr) if e != nil { t.Errorf("%s: %v", name, e) 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( witness, scriptSig, scriptPubKey, int64(inputAmt), ) vm, e := NewEngine( scriptPubKey, tx, 0, flags, sigCache, nil, int64(inputAmt), ) if e == nil { e = vm.Execute() } // Ensure there were no errors when the expected result is OK. if resultStr == "OK" { if e != nil { t.Errorf("%s failed to execute: %v", name, e) } continue } // At this point an error was expected so ensure the result of the execution matches it. success := false for _, code := range allowedErrorCodes { if IsErrorCode(e, code) { success = true break } } if !success { if serr, ok := e.(ScriptError); ok { t.Errorf( "%s: want error codes %v, got %v", name, allowedErrorCodes, serr.ErrorCode, ) continue } t.Errorf( "%s: want error codes %v, got e: %v (%T)", name, allowedErrorCodes, e, e, ) 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, e := ioutil.ReadFile("data/script_tests.json") if e != nil { t.Fatalf("TestScripts: %v\n", e) } var tests [][]interface{} e = json.Unmarshal(file, &tests) if e != nil { t.Fatalf("TestScripts couldn't Unmarshal: %v", e) } // 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 woks 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, e := ioutil.ReadFile("data/tx_invalid.json") if e != nil { t.Fatalf("TestTxInvalidTests: %v\n", e) } var tests [][]interface{} e = json.Unmarshal(file, &tests) if e != nil { t.Fatalf("TestTxInvalidTests couldn't Unmarshal: %v\n", e) } // 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, e := hex.DecodeString(serializedhex) if e != nil { t.Errorf( "bad test (arg 2 not hex %v) %d: %v", e, i, test, ) continue } tx, e := util.NewTxFromBytes(serializedTx) if e != nil { t.Errorf( "bad test (arg 2 not msgtx %v) %d: %v", e, i, test, ) continue } verifyFlags, ok := test[2].(string) if !ok { t.Errorf("bad test (arg 3 not string) %d: %v", i, test) continue } flags, e := parseScriptFlags(verifyFlags) if e != nil { t.Errorf("bad test %d: %v", i, e) continue } prevOuts := make(map[wire.OutPoint]scriptWithInputVal) 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 || len(input) > 4 { 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, e := chainhash.NewHashFromStr(previoustx) if e != nil { t.Errorf( "bad test (%dth input hash not hash %v)"+ "%d: %v", j, e, 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, e := parseShortForm(oscript) if e != nil { t.Errorf( "bad test (%dth input script doesn't "+ "parse %v) %d: %v", j, e, i, test, ) continue testloop } var inputValue float64 if len(input) == 4 { inputValue, ok = input[3].(float64) if !ok { t.Errorf( "bad test (%dth input value not int) "+ "%d: %v", j, i, test, ) continue } } v := scriptWithInputVal{ inputVal: int64(inputValue), pkScript: script, } prevOuts[*wire.NewOutPoint(prevhash, idx)] = v } for k, txin := range tx.MsgTx().TxIn { prevOut, 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, e := NewEngine( prevOut.pkScript, tx.MsgTx(), k, flags, nil, nil, prevOut.inputVal, ) if e != nil { continue testloop } e = vm.Execute() if e != 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, e := ioutil.ReadFile("data/tx_valid.json") if e != nil { t.Fatalf("TestTxValidTests: %v\n", e) } var tests [][]interface{} e = json.Unmarshal(file, &tests) if e != nil { t.Fatalf("TestTxValidTests couldn't Unmarshal: %v\n", e) } // form is either: // ["this is a comment "] // or: // [[[previous hash, previous index, previous scriptPubKey, input value]...,] // 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, e := hex.DecodeString(serializedhex) if e != nil { t.Errorf( "bad test (arg 2 not hex %v) %d: %v", e, i, test, ) continue } tx, e := util.NewTxFromBytes(serializedTx) if e != nil { t.Errorf( "bad test (arg 2 not msgtx %v) %d: %v", e, i, test, ) continue } verifyFlags, ok := test[2].(string) if !ok { t.Errorf("bad test (arg 3 not string) %d: %v", i, test) continue } flags, e := parseScriptFlags(verifyFlags) if e != nil { t.Errorf("bad test %d: %v", i, e) continue } prevOuts := make(map[wire.OutPoint]scriptWithInputVal) 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 || len(input) > 4 { 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, e := chainhash.NewHashFromStr(previoustx) if e != nil { t.Errorf( "bad test (%dth input hash not hash %v)"+ "%d: %v", j, e, 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, e := parseShortForm(oscript) if e != nil { t.Errorf( "bad test (%dth input script doesn't "+ "parse %v) %d: %v", j, e, i, test, ) continue } var inputValue float64 if len(input) == 4 { inputValue, ok = input[3].(float64) if !ok { t.Errorf( "bad test (%dth input value not int) "+ "%d: %v", j, i, test, ) continue } } v := scriptWithInputVal{ inputVal: int64(inputValue), pkScript: script, } prevOuts[*wire.NewOutPoint(prevhash, idx)] = v } for k, txin := range tx.MsgTx().TxIn { prevOut, ok := prevOuts[txin.PreviousOutPoint] if !ok { t.Errorf( "bad test (missing %dth input) %d:%v", k, i, test, ) continue testloop } vm, e := NewEngine( prevOut.pkScript, tx.MsgTx(), k, flags, nil, nil, prevOut.inputVal, ) if e != nil { t.Errorf( "test (%d:%v:%d) failed to create "+ "script: %v", i, test, k, e, ) continue } e = vm.Execute() if e != nil { t.Errorf( "test (%d:%v:%d) failed to execute: "+ "%v", i, test, k, e, ) continue } } } } // TestCalcSignatureHash runs the Bitcoin Core signature hash calculation tests in sighash.json. // https://github.com/bitcoin/bitcoin/blob/master/src/test/data/sighash.json func TestCalcSignatureHash(t *testing.T) { file, e := ioutil.ReadFile("data/sighash.json") if e != nil { t.Fatalf("TestCalcSignatureHash: %v\n", e) } var tests [][]interface{} e = json.Unmarshal(file, &tests) if e != nil { t.Fatalf( "TestCalcSignatureHash couldn't Unmarshal: %v\n", e, ) } for i, test := range tests { if i == 0 { // Skip first line -- contains comments only. continue } if len(test) != 5 { t.Fatalf( "TestCalcSignatureHash: Test #%d has "+ "wrong length.", i, ) } var tx wire.MsgTx rawTx, _ := hex.DecodeString(test[0].(string)) e := tx.Deserialize(bytes.NewReader(rawTx)) if e != nil { t.Errorf( "TestCalcSignatureHash failed test #%d: "+ "Failed to parse transaction: %v", i, e, ) continue } subScript, _ := hex.DecodeString(test[1].(string)) parsedScript, e := parseScript(subScript) if e != nil { t.Errorf( "TestCalcSignatureHash failed test #%d: "+ "Failed to parse sub-script: %v", i, e, ) continue } hashType := SigHashType(testVecF64ToUint32(test[3].(float64))) hash := calcSignatureHash( parsedScript, hashType, &tx, int(test[2].(float64)), ) expectedHash, _ := chainhash.NewHashFromStr(test[4].(string)) if !bytes.Equal(hash, expectedHash[:]) { t.Errorf( "TestCalcSignatureHash failed test #%d: "+ "Signature hash mismatch.", i, ) } } }