From 814f0bae891da288f1207ea178b5296dceb1ead8 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 13 Mar 2019 01:12:21 -0500 Subject: [PATCH] txscript: Optimize IsUnspendable. This converts the IsUnspendable function to make use of a combination of raw script analysis and the new tokenizer instead of the far less efficient parseScript thereby significantly optimizing the function. It is important to note that this new implementation intentionally has a semantic difference from the existing implementation in that it will now report scripts that are larger than the max allowed script size are unspendable as well. Finally, the comment is modified to explicitly call out the script version semantics. Note: this function was recently optimized in master, so the gains here are less noticable than other optimizations. The following is a before and after comparison of analyzing a large script: benchmark old ns/op new ns/op delta BenchmarkIsUnspendable-8 656 584 -10.98% benchmark old allocs new allocs delta BenchmarkIsUnspendable-8 1 0 -100.00% benchmark old bytes new bytes delta BenchmarkIsUnspendable-8 1 0 -100.00% --- txscript/script.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/txscript/script.go b/txscript/script.go index 691e31cc..e933167d 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -917,15 +917,22 @@ func checkScriptParses(scriptVersion uint16, script []byte) error { // 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. +// +// 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 IsUnspendable(pkScript []byte) bool { - // Not provably unspendable - if len(pkScript) == 0 { - return false - } - firstOpcode, err := checkScriptTemplateParseable(pkScript, &opcodeArray) - if err != nil { + // The script is unspendable if starts with OP_RETURN or is guaranteed + // to fail at execution due to being larger than the max allowed script + // size. + switch { + case len(pkScript) > 0 && pkScript[0] == OP_RETURN: + return true + case len(pkScript) > MaxScriptSize: return true } - return firstOpcode != nil && *firstOpcode == OP_RETURN + // The script is unspendable if it is guaranteed to fail at execution. + const scriptVersion = 0 + return checkScriptParses(scriptVersion, pkScript) != nil }