Skip to content

Commit

Permalink
txscript: Optimize IsPushOnlyScript.
Browse files Browse the repository at this point in the history
This converts the IsPushOnlyScript function to make use of the new
tokenizer instead of the far less efficient parseScript thereby
significantly optimizing the function.

It also deprecates the isPushOnly function that requires opcodes in
favor of the new function and modifies the comment on IsPushOnlyScript
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
---------------------------------------------------------------
BenchmarkIsPayToScriptHash   139961       0.66         -100.00%

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

benchmark                    old bytes    new bytes    delta
---------------------------------------------------------------
BenchmarkIsPayToScriptHash   466944       0            -100.00%
  • Loading branch information
davecgh committed Mar 26, 2019
1 parent 93b039d commit 9e8bfd2
Showing 1 changed file with 20 additions and 6 deletions.
26 changes: 20 additions & 6 deletions txscript/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func IsPayToScriptHash(script []byte) bool {
}

// isPushOnly returns true if the script only pushes data, false otherwise.
//
// DEPRECATED. Use IsPushOnlyScript instead.
func isPushOnly(pops []parsedOpcode) bool {
// NOTE: This function does NOT verify opcodes directly since it is
// internal and is only called with parsed opcodes for scripts that did
Expand All @@ -58,15 +60,27 @@ func isPushOnly(pops []parsedOpcode) bool {
return true
}

// IsPushOnlyScript returns whether or not the passed script only pushes data.
// IsPushOnlyScript returns whether or not the passed script only pushes data
// according to the consensus definition of pushing data.
//
// False will be returned when the script does not parse.
// WARNING: This function always treats the passed script as version 0. Great
// care must be taken if introducing a new script version because it is used in
// consensus which, unfortunately as of the time of this writing, does not check
// script versions before checking if it is a push only script which means nodes
// on existing rules will treat new version scripts as if they were version 0.
func IsPushOnlyScript(script []byte) bool {
pops, err := parseScript(script)
if err != nil {
return false
const scriptVersion = 0
tokenizer := MakeScriptTokenizer(scriptVersion, script)
for tokenizer.Next() {
// All opcodes up to OP_16 are data push instructions.
// NOTE: This does consider OP_RESERVED to be a data push instruction,
// but execution of OP_RESERVED will fail anyway and matches the
// behavior required by consensus.
if tokenizer.Opcode() > OP_16 {
return false
}
}
return isPushOnly(pops)
return tokenizer.Err() == nil
}

// isStakeOpcode returns whether or not the opcode is one of the stake tagging
Expand Down

0 comments on commit 9e8bfd2

Please sign in to comment.