Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

txscript: backport tokenizer from dcrd #1684

Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
116 commits
Select commit Hold shift + click to select a range
c5a193f
txscript: Add benchmark for CalcSignatureHash
davecgh Mar 13, 2019
d683492
txscript: Add benchmark for CalcWitnessSigHash
cfromknecht Feb 5, 2021
a93ee39
txscript: Add benchmark for script parsing.
davecgh Mar 13, 2019
fff82cf
txscript: Introduce zero-alloc script tokenizer.
davecgh Mar 13, 2019
cce880c
txscript: Add benchmark for DisasmString.
davecgh Mar 13, 2019
1762107
txscript: Optimize script disasm.
davecgh Mar 13, 2019
04ed404
txscript: Introduce raw script sighash calc func.
cfromknecht Apr 19, 2019
bcbf950
txscript: Optimize CalcSignatureHash.
davecgh Mar 13, 2019
54fda7d
txscript/reference_test: Convert sighash calc test
cfromknecht Apr 19, 2019
b4abc15
txscript: Make isSmallInt accept raw opcode.
davecgh Mar 13, 2019
cd6f1f9
txscript: Make asSmallInt accept raw opcode.
davecgh Mar 13, 2019
9846914
txscript: Add benchmark for IsPayToPubKey
davecgh Mar 13, 2019
8ceea24
txscript: Optimize IsPayToPubKey
cfromknecht Feb 4, 2021
dff834b
txscript: Add benchmark for IsPayToPubKeyHash
cfromknecht Feb 4, 2021
e51d50a
txscript: Optimize IsPayToPubKeyHash
cfromknecht Feb 4, 2021
f75b1e2
txscript: Add benchmark for IsPayToScriptHash.
davecgh Mar 13, 2019
ff0fc4c
txscript: Optimize IsPayToScriptHash.
davecgh Mar 13, 2019
086b4f1
txscript: Add benchmarks for IsMutlsigScript.
davecgh Mar 13, 2019
e0b8ade
txscript: Optimize IsMultisigScript.
davecgh Mar 13, 2019
d64dbbb
txscript: Add benchmarks for IsMutlsigSigScript.
davecgh Mar 13, 2019
3cfdeca
txscript: Optimize IsMultisigSigScript.
davecgh Mar 13, 2019
1f2afc1
txscript: Add benchmark for IsPushOnlyScript.
davecgh Mar 13, 2019
29dcb83
txscript: Optimize IsPushOnlyScript.
davecgh Mar 13, 2019
c1d1b0e
txscript: Add benchmark IsPayToWitnessPubkeyHash
cfromknecht Apr 19, 2019
344636f
txscript: Optimize IsPayToWitnessPubKeyHash
cfromknecht Feb 4, 2021
58a2fa9
txscript: Add benchmark for IsPayToWitnessScriptHash
cfromknecht Apr 19, 2019
5b69223
txscript: Optimize IsPayToWitnessScriptHash
cfromknecht Feb 4, 2021
0ccd702
txscript: Add benchmark for IsNullData
davecgh Mar 13, 2019
eda642a
txscript: Optimize IsNullData
cfromknecht Feb 4, 2021
75b614c
txscript: Add benchmark for IsUnspendable.
davecgh Mar 13, 2019
6635382
txscript: Optimize IsUnspendable.
davecgh Mar 13, 2019
e726274
txscript/engine: Optimize new engine push only script
cfromknecht Apr 19, 2019
2f4de13
txscript/engine: Use optimized IsPushOnlyScript
cfromknecht Apr 19, 2019
f676d32
txscript/engine: Use optimized isScriptHashScript
cfromknecht Apr 19, 2019
3d352cd
txscript/engine: Check ps2h push before parsing script
cfromknecht Apr 19, 2019
564681c
txscript: Add benchmark for GetSigOpCount.
davecgh Mar 13, 2019
43624d9
txscript: Optimize GetSigOpCount.
davecgh Mar 13, 2019
86f5907
txscript: Add benchmark for GetPreciseSigOpCount.
davecgh Mar 13, 2019
c747cc5
txscript: Optimize GetPreciseSigOpCount.
davecgh Mar 13, 2019
931aab2
txscript: add GetWitnessSigOpCountBenchmarks
cfromknecht Feb 4, 2021
f3185ee
txscript: Optimize GetWitnessSigOpCount
cfromknecht Feb 4, 2021
ca395fe
txscript: Add benchmark for GetScriptClass.
davecgh Mar 13, 2019
e9d7686
txscript: Make typeOfScript accept raw script.
davecgh Mar 13, 2019
5161238
txscript: Optimize typeOfScript pay-to-script-hash.
davecgh Mar 13, 2019
f4b4bfa
txscript: Remove unused isScriptHash function.
davecgh Mar 13, 2019
b1a191a
txscript: Optimize typeOfScript multisig.
davecgh Mar 13, 2019
b76572d
txscript: Remove unused isMultiSig function.
davecgh Mar 13, 2019
d8e3a4a
txscript: Optimze typeOfScript pay-to-pubkey
cfromknecht Feb 4, 2021
d165f48
txscript: Remove unused isPubkey function.
davecgh Mar 13, 2019
fe80899
txscript: Optimize typeOfScript pay-to-pubkey-hash.
davecgh Mar 13, 2019
bf287f5
txscript: Remove unused isPubkeyHash function.
davecgh Mar 13, 2019
612f05c
txscript: Optimize typeOfScript for null data scripts
cfromknecht Feb 4, 2021
ddffa50
txscript: Remove unused isNullData function.
davecgh Mar 13, 2019
7a471e0
txscript: Optimize typeOfScript witness-pubkey-hash
cfromknecht Apr 19, 2019
7f10c29
txscript: Optimize typeOfScript for witness-script-hash
cfromknecht Apr 19, 2019
7032bd5
txscript: Remove unused isWitnessScriptHash
cfromknecht Apr 19, 2019
5c97e22
txscript: Convert CalcScriptInfo.
davecgh Mar 13, 2019
b933306
txscript: Remove unused isPushOnly function.
davecgh Mar 13, 2019
fae615d
txscript: Remove unused getSigOpCount function.
davecgh Mar 13, 2019
1df85f4
txscript: Optimize CalcMultiSigStats.
davecgh Mar 13, 2019
5079460
txscript: Add benchmark for PushedData.
davecgh Mar 13, 2019
d88103e
txscript: Optimize PushedData.
davecgh Mar 13, 2019
be38621
txscript: Make canonicalPush accept raw opcode.
davecgh Mar 13, 2019
7e6b84c
txscript: Add ExtractAtomicSwapDataPushes benches.
davecgh Mar 13, 2019
ba0c48b
txscript/scriptnum: add maxscriptnum and maxcltvlength
cfromknecht Apr 19, 2019
bf39ad7
txscript: Optimize ExtractAtomicSwapDataPushes.
davecgh Mar 13, 2019
3b8712d
txscript: Add ExtractPkScriptAddrs benchmarks.
davecgh Mar 13, 2019
1adafa3
txscript: Optimize ExtractPkScriptAddrs scripthash.
davecgh Mar 13, 2019
7aaa28a
txscript: Optimize ExtractPkScriptAddrs pubkeyhash.
davecgh Mar 13, 2019
76a2d2b
txscript: Optimize ExtractPkScriptAddrs pubkey.
davecgh Mar 13, 2019
05388db
txscript: Optimize ExtractPkScriptAddrs multisig.
davecgh Mar 13, 2019
5f9581d
txscript: Optimize ExtractPkScriptAddrs nulldata.
davecgh Mar 13, 2019
57bef04
txscript: Optimize ExtractPkScriptAddrs witness pubkey hash
cfromknecht Apr 20, 2019
d17add5
txscript: Optimize ExtractPkScriptAddrs witness script hash
cfromknecht Apr 20, 2019
c1f8d30
txscript: Optimize ExtractPkScriptAddr assume non-standard if no success
cfromknecht Apr 20, 2019
cbc3a2e
txscript: Optimize IsWitnessProgram
cfromknecht Apr 20, 2019
6702a2b
txscript: Return witness version and program in one pass
cfromknecht Apr 20, 2019
f0b3095
txscript: Use internal analysis methods for GetWitnessSigOpCount
cfromknecht Feb 5, 2021
5c56138
txscript: Optimize ExtractWitnessProgramInfo
cfromknecht Apr 20, 2019
8170896
txscript: mergeMultiSig function def order cleanup.
davecgh Mar 13, 2019
b280be1
txscript: Introduce calcWitnessSignatureHashRaw
cfromknecht Apr 19, 2019
bfc2a3e
txscript: Remove unused isWitnessPubKeyHash
cfromknecht Apr 20, 2019
5aa00f7
txscript: Use optimized calcWitnessSignatureHashRaw w/o parsing
cfromknecht Apr 19, 2019
01f8aec
txscript: Use raw scripts in SignTxOutput.
davecgh Mar 13, 2019
1200b05
txscript: Implement efficient opcode data removal.
davecgh Mar 13, 2019
897c0ec
txscript: Optimize removeOpcodeRaw
cfromknecht Apr 19, 2019
3337741
txscript: Remove unused removeOpcode
cfromknecht Apr 20, 2019
9351643
txscript: Use removeOpcodeRaw for CODESEP in calcSigHash
cfromknecht Apr 19, 2019
a3d3df3
txscript: Make isDisabled accept raw opcode.
davecgh Mar 13, 2019
6bdd5c0
txscript: Make alwaysIllegal accept raw opcode.
davecgh Mar 13, 2019
84bf89a
txscript: Make isConditional accept raw opcode.
davecgh Mar 13, 2019
f2ab844
txscript: Make min push accept raw opcode and data.
davecgh Mar 13, 2019
2258e94
txscript: Convert to use non-parsed opcode disasm.
davecgh Mar 13, 2019
3f1788f
txscript: Refactor engine to use raw scripts.
davecgh Mar 13, 2019
215269d
txscript: Remove unused calcSignatureHash
cfromknecht Apr 20, 2019
4091cd0
txscript: Remove unused isWitnessProgram
cfromknecht Apr 20, 2019
79d106e
txscript: Remove unused removeOpcodeByData func.
davecgh Mar 13, 2019
afa1d5a
txscript: Rename removeOpcodeByDataRaw func.
davecgh Mar 13, 2019
97a1f61
txscript: Rename calcSignatureHashRaw
cfromknecht Apr 20, 2019
1442714
txscript/sign: Use calcWitnessSigHashRaw for witness sigs
cfromknecht Apr 20, 2019
83375f1
txscript/pkscript: Use finalOpcodeData to extract redeem script
cfromknecht Apr 20, 2019
1b571f6
txscript: Remove unused parseScript func.
davecgh Mar 13, 2019
11f1857
txscript: Remove unused calcWitnessSignatureHash
cfromknecht Apr 20, 2019
d16fd9f
txscript: Remove unused unparseScript func.
davecgh Mar 13, 2019
f535e04
txscript: Remove unused parsedOpcode.bytes func.
davecgh Mar 13, 2019
56c26d1
txscript: Remove unused parseScriptTemplate func.
davecgh Mar 13, 2019
1cdaaf3
txscript: Make executeOpcode take opcode and data.
davecgh Mar 13, 2019
398da74
txscript: Make op callbacks take opcode and data.
davecgh Mar 13, 2019
a7e0771
fixup! txscript: Optimize GetSigOpCount.
cfromknecht Feb 10, 2021
7b69471
fixup! txscript/reference_test: Convert sighash calc test
cfromknecht Feb 10, 2021
ef59254
fixup! txscript: Optimize IsPayToWitnessPubKeyHash
cfromknecht Feb 10, 2021
60ab922
fixup! txscript: Optimize IsPayToWitnessScriptHash
cfromknecht Feb 10, 2021
563521d
fixup! txscript: Optimize IsPayToPubKey
cfromknecht Feb 10, 2021
e095ea8
fixup! txscript: Optimize IsPayToWitnessPubKeyHash
cfromknecht Feb 10, 2021
81013ff
fixup! txscript: Optimize IsPayToWitnessScriptHash
cfromknecht Feb 10, 2021
a6ce226
fixup! txscript: Optimize IsPayToPubKey
cfromknecht Feb 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions txscript/README.md
Expand Up @@ -37,6 +37,10 @@ $ go get -u github.com/btcsuite/btcd/txscript
* [Manually Signing a Transaction Output](http://godoc.org/github.com/btcsuite/btcd/txscript#example-SignTxOutput)
Demonstrates manually creating and signing a redeem transaction.

* [Counting Opcodes in Scripts](http://godoc.org/github.com/decred/dcrd/txscript#example-ScriptTokenizer)
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
Demonstrates creating a script tokenizer instance and using it to count the
number of opcodes a script contains.

## GPG Verification Key

All official release tags are signed by Conformal so users can ensure the code
Expand Down
113 changes: 113 additions & 0 deletions txscript/bench_test.go
@@ -0,0 +1,113 @@
// Copyright (c) 2018-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
"bytes"
"fmt"
"io/ioutil"
"testing"

"github.com/btcsuite/btcd/wire"
)

var (
// manyInputsBenchTx is a transaction that contains a lot of inputs which is
// useful for benchmarking signature hash calculation.
manyInputsBenchTx wire.MsgTx

// A mock previous output script to use in the signing benchmark.
prevOutScript = hexToBytes("a914f5916158e3e2c4551c1796708db8367207ed13bb87")
)

func init() {
// tx 620f57c92cf05a7f7e7f7d28255d5f7089437bc48e34dcfebf7751d08b7fb8f5
txHex, err := ioutil.ReadFile("data/many_inputs_tx.hex")
if err != nil {
panic(fmt.Sprintf("unable to read benchmark tx file: %v", err))
}

txBytes := hexToBytes(string(txHex))
err = manyInputsBenchTx.Deserialize(bytes.NewReader(txBytes))
if err != nil {
panic(err)
}
}

// BenchmarkCalcSigHash benchmarks how long it takes to calculate the signature
// hashes for all inputs of a transaction with many inputs.
func BenchmarkCalcSigHash(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
_, err := CalcSignatureHash(prevOutScript, SigHashAll,
&manyInputsBenchTx, j)
if err != nil {
b.Fatalf("failed to calc signature hash: %v", err)
}
}
}
}

// BenchmarkCalcWitnessSigHash benchmarks how long it takes to calculate the
// witness signature hashes for all inputs of a transaction with many inputs.
func BenchmarkCalcWitnessSigHash(b *testing.B) {
sigHashes := NewTxSigHashes(&manyInputsBenchTx)

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
_, err := CalcWitnessSigHash(
prevOutScript, sigHashes, SigHashAll,
&manyInputsBenchTx, j, 5,
)
if err != nil {
b.Fatalf("failed to calc signature hash: %v", err)
}
}
}
}

// genComplexScript returns a script comprised of half as many opcodes as the
// maximum allowed followed by as many max size data pushes fit without
// exceeding the max allowed script size.
func genComplexScript() ([]byte, error) {
var scriptLen int
builder := NewScriptBuilder()
for i := 0; i < MaxOpsPerScript/2; i++ {
builder.AddOp(OP_TRUE)
scriptLen++
}
maxData := bytes.Repeat([]byte{0x02}, MaxScriptElementSize)
for i := 0; i < (MaxScriptSize-scriptLen)/(MaxScriptElementSize+3); i++ {
builder.AddData(maxData)
}
return builder.Script()
}

// BenchmarkScriptParsing benchmarks how long it takes to parse a very large
// script.
func BenchmarkScriptParsing(b *testing.B) {
script, err := genComplexScript()
if err != nil {
b.Fatalf("failed to create benchmark script: %v", err)
}

const scriptVersion = 0
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
tokenizer := MakeScriptTokenizer(scriptVersion, script)
for tokenizer.Next() {
_ = tokenizer.Opcode()
_ = tokenizer.Data()
_ = tokenizer.ByteIndex()
}
if err := tokenizer.Err(); err != nil {
b.Fatalf("failed to parse script: %v", err)
}
}
}
1 change: 1 addition & 0 deletions txscript/data/many_inputs_tx.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions txscript/error.go
@@ -1,4 +1,5 @@
// Copyright (c) 2013-2017 The btcsuite 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.

Expand Down Expand Up @@ -47,6 +48,10 @@ const (
// the provided data exceeds MaxDataCarrierSize.
ErrTooMuchNullData

// ErrUnsupportedScriptVersion is returned when an unsupported script
// version is passed to a function which deals with script analysis.
ErrUnsupportedScriptVersion

// ------------------------------------------
// Failures related to final execution state.
// ------------------------------------------
Expand Down Expand Up @@ -352,6 +357,7 @@ var errorCodeStrings = map[ErrorCode]string{
ErrNotMultisigScript: "ErrNotMultisigScript",
ErrTooManyRequiredSigs: "ErrTooManyRequiredSigs",
ErrTooMuchNullData: "ErrTooMuchNullData",
ErrUnsupportedScriptVersion: "ErrUnsupportedScriptVersion",
ErrEarlyReturn: "ErrEarlyReturn",
ErrEmptyStack: "ErrEmptyStack",
ErrEvalFalse: "ErrEvalFalse",
Expand Down
2 changes: 2 additions & 0 deletions txscript/error_test.go
@@ -1,4 +1,5 @@
// Copyright (c) 2017 The btcsuite 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.

Expand All @@ -22,6 +23,7 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrUnsupportedAddress, "ErrUnsupportedAddress"},
{ErrTooManyRequiredSigs, "ErrTooManyRequiredSigs"},
{ErrTooMuchNullData, "ErrTooMuchNullData"},
{ErrUnsupportedScriptVersion, "ErrUnsupportedScriptVersion"},
{ErrNotMultisigScript, "ErrNotMultisigScript"},
{ErrEarlyReturn, "ErrEarlyReturn"},
{ErrEmptyStack, "ErrEmptyStack"},
Expand Down
32 changes: 32 additions & 0 deletions txscript/example_test.go
@@ -1,4 +1,5 @@
// Copyright (c) 2014-2016 The btcsuite 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.

Expand Down Expand Up @@ -180,3 +181,34 @@ func ExampleSignTxOutput() {
// Output:
// Transaction successfully signed
}

// This example demonstrates creating a script tokenizer instance and using it
// to count the number of opcodes a script contains.
func ExampleScriptTokenizer() {
// Create a script to use in the example. Ordinarily this would come from
// some other source.
hash160 := btcutil.Hash160([]byte("example"))
script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_DUP).
AddOp(txscript.OP_HASH160).AddData(hash160).
AddOp(txscript.OP_EQUALVERIFY).AddOp(txscript.OP_CHECKSIG).Script()
if err != nil {
fmt.Printf("failed to build script: %v\n", err)
return
}

// Create a tokenizer to iterate the script and count the number of opcodes.
const scriptVersion = 0
var numOpcodes int
tokenizer := txscript.MakeScriptTokenizer(scriptVersion, script)
for tokenizer.Next() {
numOpcodes++
}
if tokenizer.Err() != nil {
fmt.Printf("script failed to parse: %v\n", err)
} else {
fmt.Printf("script contains %d opcode(s)\n", numOpcodes)
}

// Output:
// script contains 5 opcode(s)
}
183 changes: 183 additions & 0 deletions txscript/tokenizer.go
@@ -0,0 +1,183 @@
// Copyright (c) 2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
"encoding/binary"
"fmt"
)

// opcodeArrayRef is used to break initialization cycles.
var opcodeArrayRef *[256]opcode

func init() {
opcodeArrayRef = &opcodeArray
}

// ScriptTokenizer provides a facility for easily and efficiently tokenizing
// transaction scripts without creating allocations. Each successive opcode is
// parsed with the Next function, which returns false when iteration is
// complete, either due to successfully tokenizing the entire script or
// encountering a parse error. In the case of failure, the Err function may be
// used to obtain the specific parse error.
//
// Upon successfully parsing an opcode, the opcode and data associated with it
// may be obtained via the Opcode and Data functions, respectively.
//
// The ByteIndex function may be used to obtain the tokenizer's current offset
// into the raw script.
type ScriptTokenizer struct {
script []byte
version uint16
offset int32
op *opcode
data []byte
err error
}

// Done returns true when either all opcodes have been exhausted or a parse
// failure was encountered and therefore the state has an associated error.
func (t *ScriptTokenizer) Done() bool {
return t.err != nil || t.offset >= int32(len(t.script))
}

// Next attempts to parse the next opcode and returns whether or not it was
// successful. It will not be successful if invoked when already at the end of
// the script, a parse failure is encountered, or an associated error already
// exists due to a previous parse failure.
//
// In the case of a true return, the parsed opcode and data can be obtained with
// the associated functions and the offset into the script will either point to
// the next opcode or the end of the script if the final opcode was parsed.
//
// In the case of a false return, the parsed opcode and data will be the last
// successfully parsed values (if any) and the offset into the script will
// either point to the failing opcode or the end of the script if the function
// was invoked when already at the end of the script.
//
// Invoking this function when already at the end of the script is not
// considered an error and will simply return false.
func (t *ScriptTokenizer) Next() bool {
if t.Done() {
return false
}

op := &opcodeArrayRef[t.script[t.offset]]
switch {
// No additional data. Note that some of the opcodes, notably OP_1NEGATE,
// OP_0, and OP_[1-16] represent the data themselves.
case op.length == 1:
t.offset++
t.op = op
t.data = nil
return true

// Data pushes of specific lengths -- OP_DATA_[1-75].
case op.length > 1:
script := t.script[t.offset:]
if len(script) < op.length {
str := fmt.Sprintf("opcode %s requires %d bytes, but script only "+
"has %d remaining", op.name, op.length, len(script))
t.err = scriptError(ErrMalformedPush, str)
return false
}

// Move the offset forward and set the opcode and data accordingly.
t.offset += int32(op.length)
t.op = op
t.data = script[1:op.length]
return true

// Data pushes with parsed lengths -- OP_PUSHDATA{1,2,4}.
case op.length < 0:
script := t.script[t.offset+1:]
if len(script) < -op.length {
str := fmt.Sprintf("opcode %s requires %d bytes, but script only "+
"has %d remaining", op.name, -op.length, len(script))
t.err = scriptError(ErrMalformedPush, str)
return false
}

// Next -length bytes are little endian length of data.
var dataLen int32
switch op.length {
case -1:
dataLen = int32(script[0])
case -2:
dataLen = int32(binary.LittleEndian.Uint16(script[:2]))
case -4:
dataLen = int32(binary.LittleEndian.Uint32(script[:4]))
default:
str := fmt.Sprintf("invalid opcode length %d", op.length)
t.err = scriptError(ErrMalformedPush, str)
return false
}

// Move to the beginning of the data.
script = script[-op.length:]

// Disallow entries that do not fit script or were sign extended.
if dataLen > int32(len(script)) || dataLen < 0 {
str := fmt.Sprintf("opcode %s pushes %d bytes, but script only "+
"has %d remaining", op.name, dataLen, len(script))
t.err = scriptError(ErrMalformedPush, str)
return false
}

// Move the offset forward and set the opcode and data accordingly.
t.offset += 1 + int32(-op.length) + dataLen
t.op = op
t.data = script[:dataLen]
return true
}

// The only remaining case is an opcode with length zero which is
// impossible.
panic("unreachable")
}

// Script returns the full script associated with the tokenizer.
func (t *ScriptTokenizer) Script() []byte {
return t.script
}

// ByteIndex returns the current offset into the full script that will be parsed
// next and therefore also implies everything before it has already been parsed.
func (t *ScriptTokenizer) ByteIndex() int32 {
return t.offset
}

// Opcode returns the current opcode associated with the tokenizer.
func (t *ScriptTokenizer) Opcode() byte {
return t.op.value
}

// Data returns the data associated with the most recently successfully parsed
// opcode.
func (t *ScriptTokenizer) Data() []byte {
return t.data
}

// Err returns any errors currently associated with the tokenizer. This will
// only be non-nil in the case a parsing error was encountered.
func (t *ScriptTokenizer) Err() error {
return t.err
}

// MakeScriptTokenizer returns a new instance of a script tokenizer. Passing
// an unsupported script version will result in the returned tokenizer
// immediately having an err set accordingly.
//
// See the docs for ScriptTokenizer for more details.
func MakeScriptTokenizer(scriptVersion uint16, script []byte) ScriptTokenizer {
// Only version 0 scripts are currently supported.
var err error
if scriptVersion != 0 {
str := fmt.Sprintf("script version %d is not supported", scriptVersion)
err = scriptError(ErrUnsupportedScriptVersion, str)

}
return ScriptTokenizer{version: scriptVersion, script: script, err: err}
}