From 18aa1a59df38c2fb0d1a507bc3d7deb883ebdff9 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-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% --- txscript/script.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/txscript/script.go b/txscript/script.go index 52bb5b6b..112c24ac 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -567,12 +567,17 @@ func shallowCopyTx(tx *wire.MsgTx) wire.MsgTx { // 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. +// +// 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) { - parsedScript, err := parseScript(script) - if err != nil { - return nil, fmt.Errorf("cannot parse output script: %v", err) + const scriptVersion = 0 + if err := checkScriptParses(scriptVersion, script); err != nil { + 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 @@ -850,6 +855,15 @@ func getWitnessSigOps(pkScript []byte, witness wire.TxWitness) int { 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 // guaranteed to fail at execution. This allows inputs to be pruned instantly // when entering the UTXO set.