From af757d3d0d21468c994401da82816837a9314f62 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 18 Apr 2019 20:11:36 -0700 Subject: [PATCH] txscript: Introduce raw script sighash calc func. This introduces a new function named calcSignatureHashRaw which accepts the raw script bytes to calculate the script hash versus requiring the parsed opcode only to unparse them later in order to make it more flexible for working with raw scripts. Since there are several places in the rest of the code that currently only have access to the parsed opcodes, this modifies the existing calcSignatureHash to first unparse the script before calling the new function. Backport of decred/dcrd:f306a72a16eaabfb7054a26f9d9f850b87b00279 --- txscript/opcode.go | 10 ++++++++-- txscript/reference_test.go | 7 ++++++- txscript/script.go | 41 ++++++++++++++++++++++++++++---------- txscript/sign.go | 5 ++++- 4 files changed, 49 insertions(+), 14 deletions(-) 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