diff --git a/txscript/opcode.go b/txscript/opcode.go index 7383f881..893bebdf 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -2198,7 +2198,10 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error { // to sign itself. subScript = removeOpcodeByData(subScript, fullSigBytes) - hash = calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx) + hash, err = calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx) + if err != nil { + return err + } } pubKey, err := btcec.ParsePubKey(pkBytes, btcec.S256()) @@ -2467,7 +2470,10 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error { return err } } else { - hash = calcSignatureHash(script, hashType, &vm.tx, vm.txIdx) + hash, err = calcSignatureHash(script, hashType, &vm.tx, vm.txIdx) + if err != nil { + return err + } } var valid bool diff --git a/txscript/reference_test.go b/txscript/reference_test.go index 5015960b..6ac9b68f 100644 --- a/txscript/reference_test.go +++ b/txscript/reference_test.go @@ -863,8 +863,13 @@ func TestCalcSignatureHash(t *testing.T) { } hashType := SigHashType(testVecF64ToUint32(test[3].(float64))) - hash := calcSignatureHash(parsedScript, hashType, &tx, + hash, err := calcSignatureHash(parsedScript, hashType, &tx, int(test[2].(float64))) + if err != nil { + t.Errorf("TestCalcSignatureHash failed test #%d: "+ + "Failed to compute sighash: %v", i, err) + continue + } expectedHash, _ := chainhash.NewHashFromStr(test[4].(string)) if !bytes.Equal(hash, expectedHash[:]) { diff --git a/txscript/script.go b/txscript/script.go index b7c2ef96..52bb5b6b 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -572,13 +572,12 @@ func CalcSignatureHash(script []byte, hashType SigHashType, tx *wire.MsgTx, idx if err != nil { return nil, fmt.Errorf("cannot parse output script: %v", err) } - return calcSignatureHash(parsedScript, hashType, tx, idx), nil + return calcSignatureHash(parsedScript, hashType, tx, idx) } -// calcSignatureHash will, given a script and hash type for the current script -// engine instance, calculate the signature hash to be used for signing and -// verification. -func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.MsgTx, idx int) []byte { +// calcSignatureHashRaw computes the signature hash for the specified input of +// the target transaction observing the desired signature hash type. +func calcSignatureHashRaw(sigScript []byte, hashType SigHashType, tx *wire.MsgTx, idx int) []byte { // The SigHashSingle signature type signs only the corresponding input // and output (the output with the same index number as the input). // @@ -606,17 +605,24 @@ func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.Msg } // Remove all instances of OP_CODESEPARATOR from the script. - script = removeOpcode(script, OP_CODESEPARATOR) + filteredScript := make([]byte, 0, len(sigScript)) + const scriptVersion = 0 + tokenizer := MakeScriptTokenizer(scriptVersion, sigScript) + var prevOffset int32 + for tokenizer.Next() { + if tokenizer.Opcode() != OP_CODESEPARATOR { + filteredScript = append(filteredScript, + sigScript[prevOffset:tokenizer.ByteIndex()]...) + } + prevOffset = tokenizer.ByteIndex() + } // Make a shallow copy of the transaction, zeroing out the script for // all inputs that are not currently being processed. txCopy := shallowCopyTx(tx) for i := range txCopy.TxIn { if i == idx { - // UnparseScript cannot fail here because removeOpcode - // above only returns a valid script. - sigScript, _ := unparseScript(script) - txCopy.TxIn[idx].SignatureScript = sigScript + txCopy.TxIn[idx].SignatureScript = filteredScript } else { txCopy.TxIn[i].SignatureScript = nil } @@ -670,6 +676,21 @@ func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.Msg return chainhash.DoubleHashB(wbuf.Bytes()) } +// calcSignatureHash computes the signature hash for the specified input of the +// target transaction observing the desired signature hash type. +// +// DEPRECATED: Use calcSignatureHashRaw instead +func calcSignatureHash(prevOutScript []parsedOpcode, hashType SigHashType, + tx *wire.MsgTx, idx int) ([]byte, error) { + + sigScript, err := unparseScript(prevOutScript) + if err != nil { + return nil, err + } + + return calcSignatureHashRaw(sigScript, hashType, tx, idx), nil +} + // asSmallInt returns the passed opcode, which must be true according to // isSmallInt(), as an integer. func asSmallInt(op *opcode) int { diff --git a/txscript/sign.go b/txscript/sign.go index 42af9686..b9f8b2db 100644 --- a/txscript/sign.go +++ b/txscript/sign.go @@ -345,7 +345,10 @@ sigLoop: // however, assume no sigs etc are in the script since that // would make the transaction nonstandard and thus not // MultiSigTy, so we just need to hash the full thing. - hash := calcSignatureHash(pkPops, hashType, tx, idx) + hash, err := calcSignatureHash(pkPops, hashType, tx, idx) + if err != nil { + panic(fmt.Sprintf("cannot compute sighash: %v", err)) + } for _, addr := range addresses { // All multisig addresses should be pubkey addresses