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
This commit is contained in:
Conner Fromknecht 2019-04-18 20:11:36 -07:00 committed by Roy Lee
parent ce08988514
commit 07c1a9343d
4 changed files with 49 additions and 14 deletions

View file

@ -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

View file

@ -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[:]) {

View file

@ -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 {

View file

@ -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