From c57dc2d6b0d67890dc4ea33ab1bdb97048846571 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 13 Mar 2019 01:11:07 -0500 Subject: [PATCH] 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 2792057 2760042 -1.15% benchmark old allocs new allocs delta ------------------------------------------------------- BenchmarkCalcSigHash 1691 1068 -36.84% benchmark old bytes new bytes delta ------------------------------------------------------- BenchmarkCalcSigHash 521673 438604 -15.92% --- txscript/script.go | 9 +++++++++ txscript/sighash.go | 10 +++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/txscript/script.go b/txscript/script.go index bd44c6d2ab..e2a8223322 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -416,6 +416,15 @@ func GetPreciseSigOpCount(scriptSig, scriptPubKey []byte, bip16 bool) int { return getSigOpCount(shPops, true) } +// 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 // guaranteed to fail at execution. This allows inputs to be pruned instantly // when entering the UTXO set. In Decred, all zero value outputs are unspendable. diff --git a/txscript/sighash.go b/txscript/sighash.go index 401c716a5e..5ef7f70e63 100644 --- a/txscript/sighash.go +++ b/txscript/sighash.go @@ -460,11 +460,15 @@ func calcSignatureHash(prevOutScript []parsedOpcode, hashType SigHashType, tx *w // cached prefix parameter allows the caller to optimize the calculation by // providing the prefix hash to be reused in the case of SigHashAll without the // SigHashAnyOneCanPay flag set. +// +// 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, cachedPrefix *chainhash.Hash) ([]byte, error) { - pops, err := parseScript(script) - if err != nil { + const scriptVersion = 0 + if err := checkScriptParses(scriptVersion, script); err != nil { return nil, err } - return calcSignatureHash(pops, hashType, tx, idx, cachedPrefix) + return calcSignatureHashRaw(script, hashType, tx, idx, cachedPrefix) }