From 6a0a77fd81b8508bd9720698151ba3d030abdccf Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 13 Mar 2019 01:12:08 -0500 Subject: [PATCH] txscript: Optimize ExtractCoinbaseNullData. This converts the ExtractCoinbaseNullData function to make use of the new tokenizer instead of the far less efficient parseScript thereby significantly optimizing the function. The following is a before and after comparison of analyzing a typical coinbase script: benchmark old ns/op new ns/op delta ------------------------------------------------------------------- BenchmarkExactCoinbaseNullData 227 31.0 -86.34% benchmark old allocs new allocs delta ------------------------------------------------------------------- BenchmarkExactCoinbaseNullData 1 0 -100.00% benchmark old bytes new bytes delta ------------------------------------------------------------------- BenchmarkExactCoinbaseNullData 448 0 -100.00% --- txscript/consensus.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/txscript/consensus.go b/txscript/consensus.go index 8a5e55f3..cfeeb6f7 100644 --- a/txscript/consensus.go +++ b/txscript/consensus.go @@ -1,5 +1,5 @@ // Copyright (c) 2015-2016 The btcsuite developers -// Copyright (c) 2015-2017 The Decred developers +// Copyright (c) 2015-2019 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -25,12 +25,11 @@ const ( // ExtractCoinbaseNullData ensures the passed script is a nulldata script as // required by the consensus rules for the coinbase output that is used to // ensure the coinbase has a unique hash and returns the data it pushes. +// +// 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 ExtractCoinbaseNullData(pkScript []byte) ([]byte, error) { - pops, err := parseScript(pkScript) - if err != nil { - return nil, err - } - // The nulldata in the coinbase must be a single OP_RETURN followed by a // data push up to maxUniqueCoinbaseNullDataSize bytes. // @@ -44,14 +43,18 @@ func ExtractCoinbaseNullData(pkScript []byte) ([]byte, error) { // height to be encoded as a 4-byte little-endian uint32 pushed via a normal // data push, as opposed to using the normal number handling semantics of // scripts, so this is specialized to accommodate that. - if len(pops) == 1 && pops[0].opcode.value == OP_RETURN { + const scriptVersion = 0 + if len(pkScript) == 1 && pkScript[0] == OP_RETURN { return nil, nil } - if len(pops) == 2 && pops[0].opcode.value == OP_RETURN && - pops[1].opcode.value <= OP_PUSHDATA4 && len(pops[1].data) <= - maxUniqueCoinbaseNullDataSize { + if len(pkScript) > 1 && pkScript[0] == OP_RETURN { + tokenizer := MakeScriptTokenizer(scriptVersion, pkScript[1:]) + if tokenizer.Next() && tokenizer.Done() && + tokenizer.Opcode() <= OP_PUSHDATA4 && + len(tokenizer.Data()) <= maxUniqueCoinbaseNullDataSize { - return pops[1].data, nil + return tokenizer.Data(), nil + } } str := fmt.Sprintf("script %x is not well-formed coinbase nulldata",