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.

The following is a before and after comparison of analyzing a large
script:

benchmark                old ns/op    new ns/op    delta
-----------------------------------------------------------
BenchmarkIsUnspendable   149899       860          -99.43%

benchmark                old allocs   new allocs   delta
-----------------------------------------------------------
BenchmarkIsUnspendable   1            0            -100.00%

benchmark                old bytes    new bytes    delta
-----------------------------------------------------------
BenchmarkIsUnspendable   466945       0            -100.00%
This commit is contained in:
Dave Collins 2019-03-13 01:12:21 -05:00
parent a1da017271
commit bb365f221f
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2

View File

@ -481,15 +481,20 @@ 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. In Decred, all zero value outputs are unspendable.
//
// 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(amount int64, pkScript []byte) bool {
if amount == 0 {
// 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.
if amount == 0 || len(pkScript) > MaxScriptSize || len(pkScript) > 0 &&
pkScript[0] == OP_RETURN {
return true
}
pops, err := parseScript(pkScript)
if err != nil {
return true
}
return len(pops) > 0 && pops[0].opcode.value == OP_RETURN
// The script is unspendable if it is guaranteed to fail at execution.
const scriptVersion = 0
return checkScriptParses(scriptVersion, pkScript) != nil
}