txscript: Optimize isAnyKindOfScriptHash.

This converts the isAnyKindOfScriptHash function to analyze the raw
script instead of requiring far less efficient parsed opcodes thereby
significantly optimizing the function.

Since the function relies on isStakeScriptHash to identify a stake
tagged pay-to-script-hash, and is the only consumer of it, this also
converts that function to analyze the raw script and renames it to
isStakeScriptHashScript for more consistent naming.

Finally, the tests are updated accordingly.

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

benchmark                        old ns/op    new ns/op    delta
-------------------------------------------------------------------
BenchmarkIsAnyKindOfScriptHash   101249       3.83         -100.00%

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

benchmark                        old bytes    new bytes    delta
-------------------------------------------------------------------
BenchmarkIsAnyKindOfScriptHash   466944       0            -100.00%
This commit is contained in:
Dave Collins 2019-03-13 01:11:24 -05:00
parent bc56df1046
commit a598838fb7
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
4 changed files with 17 additions and 16 deletions

View File

@ -234,7 +234,6 @@ func BenchmarkIsAnyKindOfScriptHash(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
pops, _ := parseScript(script)
_ = isAnyKindOfScriptHash(pops)
_ = isAnyKindOfScriptHash(script)
}
}

View File

@ -718,7 +718,9 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
vm.scriptIdx++
}
if isAnyKindOfScriptHash(vm.scripts[1]) {
// The signature script must only contain data pushes for P2SH which is
// determined based on the form of the public key script.
if isAnyKindOfScriptHash(scriptPubKey) {
// Only accept input scripts that push data for P2SH.
if !isPushOnly(vm.scripts[0]) {
return nil, scriptError(ErrNotPushOnly,

View File

@ -112,20 +112,21 @@ func isScriptHashScript(script []byte) bool {
return extractScriptHash(script) != nil
}
// isStakeScriptHash returns whether or not the passed script is a stake
// pay-to-script-hash script.
func isStakeScriptHash(pops []parsedOpcode) bool {
return len(pops) == 4 &&
isStakeOpcode(pops[0].opcode.value) &&
pops[1].opcode.value == OP_HASH160 &&
pops[2].opcode.value == OP_DATA_20 &&
pops[3].opcode.value == OP_EQUAL
// isStakeScriptHashScript returns whether or not the passed script is a
// stake-tagged pay-to-script-hash script.
func isStakeScriptHashScript(script []byte) bool {
return len(script) == 24 &&
isStakeOpcode(script[0]) &&
script[1] == OP_HASH160 &&
script[2] == OP_DATA_20 &&
script[23] == OP_EQUAL
}
// isAnyKindOfScriptHash returns whether or not the passed script is either a
// regular pay-to-script-hash script or a stake pay-to-script-hash script.
func isAnyKindOfScriptHash(pops []parsedOpcode) bool {
return isScriptHash(pops) || isStakeScriptHash(pops)
// regular pay-to-script-hash script or a stake-tagged pay-to-script-hash
// script.
func isAnyKindOfScriptHash(script []byte) bool {
return isScriptHashScript(script) || isStakeScriptHashScript(script)
}
// HasP2SHScriptSigStakeOpCodes returns an error is the p2sh script has either

View File

@ -4123,9 +4123,8 @@ func TestIsAnyKindOfScriptHash(t *testing.T) {
for _, test := range scriptClassTests {
script := mustParseShortForm(test.script)
pops, _ := parseScript(script)
want := (test.class == ScriptHashTy || test.subClass == ScriptHashTy)
p2sh := isAnyKindOfScriptHash(pops)
p2sh := isAnyKindOfScriptHash(script)
if p2sh != want {
t.Errorf("%s: epxected p2sh %v, got %v", test.name,
want, p2sh)