From 4d8edfe6d9457ff8578f8ea2381fb47f049a32a1 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 4 Feb 2021 13:22:40 -0800 Subject: [PATCH] txscript: Optimize IsPayToWitnessPubKeyHash This converts the IsPayToWitnessPubKeyHash function to analyze the raw script instead of the far less efficient parseScript, thereby significantly optimizing the function. In order to accomplish this, it introduces two new functions. The first one is named extractWitnessPubKeyHash and works with the raw script bytes to simultaneously deteremine if the script is a p2wkh, and in case it is, extract and return the hash. The second new function is name isWitnessPubKeyHashScript which is defined in terms of the former. The extract function is approach was chosen because it is common for callers to want to only extract relevant details from the script if the script is of the specific type. Extracting those details requires the exact same checks to ensure the script is of the correct type, so it is more efficient to combine the two and define the type determination in terms of the result so long as the extraction does not require allocations. Finally, this deprecates the isWitnessPubKeyHash function that requires opcodes in favor of the new functions and modifies the comment on IsPayToWitnessPubKeyHash to explicitly call out the script version semantics. The following is a before and after comparison of executing IsPayToWitnessPubKeyHash on a large script: benchmark old ns/op new ns/op delta BenchmarkIsWitnessPubKeyHash-8 68927 0.53 -100.00% benchmark old allocs new allocs delta BenchmarkIsWitnessPubKeyHash-8 1 0 -100.00% benchmark old bytes new bytes delta BenchmarkIsWitnessPubKeyHash-8 311299 0 -100.00% --- txscript/script.go | 6 +----- txscript/standard.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/txscript/script.go b/txscript/script.go index 43048e95..59dc2e27 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -110,11 +110,7 @@ func IsPayToWitnessScriptHash(script []byte) bool { // IsPayToWitnessPubKeyHash returns true if the is in the standard // pay-to-witness-pubkey-hash (P2WKH) format, false otherwise. func IsPayToWitnessPubKeyHash(script []byte) bool { - pops, err := parseScript(script) - if err != nil { - return false - } - return isWitnessPubKeyHash(pops) + return isWitnessPubKeyHashScript(script) } // isWitnessPubKeyHash returns true if the passed script is a diff --git a/txscript/standard.go b/txscript/standard.go index 272b42b8..513c9ce1 100644 --- a/txscript/standard.go +++ b/txscript/standard.go @@ -400,6 +400,27 @@ func IsMultisigSigScript(script []byte) bool { return isMultisigScript(scriptVersion, possibleRedeemScript) } +// extractWitnessPubKeyHash extracts the witness public key hash from the passed +// script if it is a standard pay-to-witness-pubkey-hash script. It will return +// nil otherwise. +func extractWitnessPubKeyHash(script []byte) []byte { + // A pay-to-witness-pubkey-hash script is of the form: + // OP_0 OP_DATA_20 <20-byte-hash> + if len(script) == 22 && + script[0] == OP_0 && + script[1] == OP_DATA_20 { + + return script[2:22] + } + return nil +} + +// isWitnessPubKeyHashScript returns whether or not the passed script is a +// standard pay-to-witness-pubkey-hash script. +func isWitnessPubKeyHashScript(script []byte) bool { + return extractWitnessPubKeyHash(script) != nil +} + // isNullData returns true if the passed script is a null data transaction, // false otherwise. func isNullData(pops []parsedOpcode) bool {