txscript: Optimize CalcSignatureHash.

This modifies the CalcSignatureHash function to make use of the new
signature hash calculation function that accepts raw scripts without
needing to first parse them.  Consequently, it also doubles as a slight
optimization to the execution time and a significant reduction in the
number of allocations.

In order to convert the CalcScriptHash function and keep the same
semantics, a new function named checkScriptParses is introduced which
will quickly determine if a script can be fully parsed without failure
and return the parse failure in the case it can't.

The following is a before and after comparison of analyzing a large
multiple input transaction:

benchmark                  old ns/op     new ns/op     delta
BenchmarkCalcSigHash-8     3627895       3619477       -0.23%

benchmark                  old allocs     new allocs     delta
BenchmarkCalcSigHash-8     1335           801            -40.00%

benchmark                  old bytes     new bytes     delta
BenchmarkCalcSigHash-8     1373812       1293354       -5.86%
This commit is contained in:
Dave Collins 2019-03-13 01:11:07 -05:00 committed by Roy Lee
parent 07c1a9343d
commit 18aa1a59df

View file

@ -567,12 +567,17 @@ func shallowCopyTx(tx *wire.MsgTx) wire.MsgTx {
// CalcSignatureHash will, given a script and hash type for the current script // CalcSignatureHash will, given a script and hash type for the current script
// engine instance, calculate the signature hash to be used for signing and // engine instance, calculate the signature hash to be used for signing and
// verification. // verification.
//
// NOTE: This function is only valid for version 0 scripts. Since the function
// does not accept a script version, the results are undefined for other script
// versions.
func CalcSignatureHash(script []byte, hashType SigHashType, tx *wire.MsgTx, idx int) ([]byte, error) { func CalcSignatureHash(script []byte, hashType SigHashType, tx *wire.MsgTx, idx int) ([]byte, error) {
parsedScript, err := parseScript(script) const scriptVersion = 0
if err != nil { if err := checkScriptParses(scriptVersion, script); err != nil {
return nil, fmt.Errorf("cannot parse output script: %v", err) return nil, err
} }
return calcSignatureHash(parsedScript, hashType, tx, idx)
return calcSignatureHashRaw(script, hashType, tx, idx), nil
} }
// calcSignatureHashRaw computes the signature hash for the specified input of // calcSignatureHashRaw computes the signature hash for the specified input of
@ -850,6 +855,15 @@ func getWitnessSigOps(pkScript []byte, witness wire.TxWitness) int {
return 0 return 0
} }
// checkScriptParses returns an error if the provided script fails to parse.
func checkScriptParses(scriptVersion uint16, script []byte) error {
tokenizer := MakeScriptTokenizer(scriptVersion, script)
for tokenizer.Next() {
// Nothing to do.
}
return tokenizer.Err()
}
// IsUnspendable returns whether the passed public key script is unspendable, or // IsUnspendable returns whether the passed public key script is unspendable, or
// guaranteed to fail at execution. This allows inputs to be pruned instantly // guaranteed to fail at execution. This allows inputs to be pruned instantly
// when entering the UTXO set. // when entering the UTXO set.