dcrd/txscript/stdscript/script_bench_test.go
Dave Collins 7d97fd5ff8
stdscript: Move from internal/staging to txscript.
This moves the new stdscript package from the internal staging area to
the txscript module and updates the relevant paths and package README.md
accordingly.
2021-11-18 12:29:53 -06:00

396 lines
13 KiB
Go

// Copyright (c) 2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package stdscript
import (
"bytes"
"fmt"
"testing"
"github.com/decred/dcrd/txscript/v4"
)
// These variables are used to help ensure the benchmarks do not elide code.
var (
noElideSwapData *AtomicSwapDataPushesV0
)
// complexScriptV0 is a version 0 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.
var complexScriptV0 = func() []byte {
const (
maxScriptSize = txscript.MaxScriptSize
maxScriptElementSize = txscript.MaxScriptElementSize
)
var scriptLen int
builder := txscript.NewScriptBuilder()
for i := 0; i < txscript.MaxOpsPerScript/2; i++ {
builder.AddOp(txscript.OP_TRUE)
scriptLen++
}
maxData := bytes.Repeat([]byte{0x02}, maxScriptElementSize)
for i := 0; i < (maxScriptSize-scriptLen)/maxScriptElementSize; i++ {
builder.AddData(maxData)
}
script, err := builder.Script()
if err != nil {
panic(err)
}
return script
}()
// makeBenchmarks constructs a slice of tests to use in the benchmarks as
// follows:
// - Start with a version 0 complex non standard script
// - Add all tests for which the provided filter function returns true
func makeBenchmarks(filterFn func(test scriptTest) bool) []scriptTest {
benches := make([]scriptTest, 0, 5)
benches = append(benches, scriptTest{
name: "v0 complex non standard",
version: 0,
script: complexScriptV0,
})
for _, test := range scriptV0Tests {
if filterFn(test) {
benches = append(benches, test)
}
}
return benches
}
// benchIsX is a convenience function that runs benchmarks for the entries that
// match the provided filter function using the given script type determination
// function and ensures the result matches the expected one.
func benchIsX(b *testing.B, filterFn func(test scriptTest) bool, isXFn func(scriptVersion uint16, script []byte) bool) {
b.Helper()
benches := makeBenchmarks(filterFn)
for _, bench := range benches {
want := filterFn(bench)
b.Run(bench.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := isXFn(bench.version, bench.script)
if got != want {
b.Fatalf("%q: unexpected result -- got %v, want %v",
bench.name, got, want)
}
}
})
}
}
// BenchmarkIsPubKeyScript benchmarks the performance of analyzing various
// public key scripts to determine if they are p2pk-ecdsa-secp256k1 scripts.
func BenchmarkIsPubKeyScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STPubKeyEcdsaSecp256k1
}
benchIsX(b, filterFn, IsPubKeyScript)
}
// BenchmarkIsPubKeyEd25519Script benchmarks the performance of analyzing
// various public key scripts to determine if they are p2pkh-ed25519 scripts.
func BenchmarkIsPubKeyEd25519Script(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STPubKeyEd25519
}
benchIsX(b, filterFn, IsPubKeyEd25519Script)
}
// BenchmarkIsPubKeySchnorrSecp256k1Script benchmarks the performance of
// analyzing various public key scripts to determine if they are
// p2pkh-schnorr-secp256k1 scripts.
func BenchmarkIsPubKeySchnorrSecp256k1Script(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STPubKeySchnorrSecp256k1
}
benchIsX(b, filterFn, IsPubKeySchnorrSecp256k1Script)
}
// BenchmarkIsPubKeyHashScript benchmarks the performance of analyzing various
// public key scripts to determine if they are p2pkh-ecdsa-secp256k1 scripts.
func BenchmarkIsPubKeyHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STPubKeyHashEcdsaSecp256k1
}
benchIsX(b, filterFn, IsPubKeyHashScript)
}
// BenchmarkIsPubKeyHashEd25519Script benchmarks the performance of analyzing
// various public key scripts to determine if they are p2pkh-ed25519 scripts.
func BenchmarkIsPubKeyHashEd25519Script(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STPubKeyHashEd25519
}
benchIsX(b, filterFn, IsPubKeyHashEd25519Script)
}
// BenchmarkIsPubKeyHashSchnorrSecp256k1Script benchmarks the performance of
// analyzing various public key scripts to determine if they are
// p2pkh-schnorr-secp256k1 scripts.
func BenchmarkIsPubKeyHashSchnorrSecp256k1Script(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STPubKeyHashSchnorrSecp256k1
}
benchIsX(b, filterFn, IsPubKeyHashSchnorrSecp256k1Script)
}
// BenchmarkIsScriptHashScript benchmarks the performance of analyzing various
// public key scripts to determine if they are p2sh scripts.
func BenchmarkIsScriptHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STScriptHash
}
benchIsX(b, filterFn, IsScriptHashScript)
}
// BenchmarkIsMultiSigScript benchmarks the performance of analyzing various
// public key scripts to determine if they are multisignature scripts.
func BenchmarkIsMultiSigScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STMultiSig && !test.isSig
}
benchIsX(b, filterFn, IsMultiSigScript)
}
// BenchmarkIsMultiSigSigScript benchmarks the performance of analyzing various
// signature scripts to determine if they are likely to be multisignature redeem
// scripts.
func BenchmarkIsMultiSigSigScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STMultiSig && test.isSig
}
benchIsX(b, filterFn, IsMultiSigSigScript)
}
// BenchmarkMultiSigRedeemScriptFromScriptSigV0 benchmarks the performance of
// extracting the redeem script from various version 0 signature scripts.
func BenchmarkMultiSigRedeemScriptFromScriptSigV0(b *testing.B) {
for _, test := range scriptV0Tests {
if test.version != 0 || test.wantType != STMultiSig || !test.isSig {
continue
}
want := test.wantData.([]byte)
b.Run(test.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := MultiSigRedeemScriptFromScriptSigV0(test.script)
if !bytes.Equal(got, want) {
b.Fatalf("%q: unexpected result -- got %x, want %x",
test.name, got, want)
}
}
})
}
}
// BenchmarkIsNullDataScript benchmarks the performance of analyzing various
// public key scripts to determine if they are nulldata scripts.
func BenchmarkIsNullDataScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STNullData
}
benchIsX(b, filterFn, IsNullDataScript)
}
// BenchmarkIsStakeSubmissionPubKeyHashScript benchmarks the performance of
// analyzing various public key scripts to determine if they are stake
// submission p2pkh-ecdsa-secp256k1 scripts.
func BenchmarkIsStakeSubmissionPubKeyHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeSubmissionPubKeyHash
}
benchIsX(b, filterFn, IsStakeSubmissionPubKeyHashScript)
}
// BenchmarkIsStakeSubmissionScriptHashScript benchmarks the performance of
// analyzing various public key scripts to determine if they are stake
// submission p2sh scripts.
func BenchmarkIsStakeSubmissionScriptHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeSubmissionScriptHash
}
benchIsX(b, filterFn, IsStakeSubmissionScriptHashScript)
}
// BenchmarkIsStakeGenPubKeyHashScript benchmarks the performance of analyzing
// various public key scripts to determine if they are stake generation
// p2pkh-ecdsa-secp256k1 scripts.
func BenchmarkIsStakeGenPubKeyHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeGenPubKeyHash
}
benchIsX(b, filterFn, IsStakeGenPubKeyHashScript)
}
// BenchmarkIsStakeGenScriptHashScript benchmarks the performance of analyzing various
// public key scripts to determine if they are stake generation p2sh scripts.
func BenchmarkIsStakeGenScriptHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeGenScriptHash
}
benchIsX(b, filterFn, IsStakeGenScriptHashScript)
}
// BenchmarkIsStakeRevocationPubKeyHashScript benchmarks the performance of
// analyzing various public key scripts to determine if they are stake
// revocation p2pkh-ecdsa-secp256k1 scripts.
func BenchmarkIsStakeRevocationPubKeyHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeRevocationPubKeyHash
}
benchIsX(b, filterFn, IsStakeRevocationPubKeyHashScript)
}
// BenchmarkIsStakeRevocationScriptHashScript benchmarks the performance of
// various public key scripts to determine if they are stake revocation p2sh
// scripts.
func BenchmarkIsStakeRevocationScriptHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeRevocationScriptHash
}
benchIsX(b, filterFn, IsStakeRevocationScriptHashScript)
}
// BenchmarkIsStakeChangePubKeyHash benchmarks the performance of analyzing
// various public key scripts to determine if they are stake change
// p2pkh-ecdsa-secp256k1 scripts.
func BenchmarkIsStakeChangePubKeyHash(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeChangePubKeyHash
}
benchIsX(b, filterFn, IsStakeChangePubKeyHashScript)
}
// BenchmarkIsStakeChangeScriptHash benchmarks the performance of analyzing
// various public key scripts to determine if they are stake change p2sh
// scripts.
func BenchmarkIsStakeChangeScriptHash(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STStakeChangeScriptHash
}
benchIsX(b, filterFn, IsStakeChangeScriptHashScript)
}
// BenchmarkIsTreasuryAddScript benchmarks the performance of analyzing various
// public key scripts to determine if they are treasury add scripts.
func BenchmarkIsTreasuryAddScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STTreasuryAdd
}
benchIsX(b, filterFn, IsTreasuryAddScript)
}
// BenchmarkIsTreasuryGenPubKeyHashScript benchmarks the performance of
// analyzing various public key scripts to determine if they are treasury
// generation p2pkh-ecdsa-secp256k1 scripts.
func BenchmarkIsTreasuryGenPubKeyHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STTreasuryGenPubKeyHash
}
benchIsX(b, filterFn, IsTreasuryGenPubKeyHashScript)
}
// BenchmarkIsTreasuryGenScriptHashScript benchmarks the performance of
// analyzing various public key scripts to determine if they are treasury
// generation p2sh scripts.
func BenchmarkIsTreasuryGenScriptHashScript(b *testing.B) {
filterFn := func(test scriptTest) bool {
return test.wantType == STTreasuryGenScriptHash
}
benchIsX(b, filterFn, IsTreasuryGenScriptHashScript)
}
// BenchmarkDetermineScriptType benchmarks the performance of analyzing various
// public key scripts to determine what type of standard script they are.
func BenchmarkDetermineScriptType(b *testing.B) {
counts := make(map[ScriptType]int)
benches := makeBenchmarks(func(test scriptTest) bool {
// Limit to one of each script type.
counts[test.wantType]++
return test.wantType != STNonStandard && counts[test.wantType] == 1
})
for _, bench := range benches {
b.Run(bench.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := DetermineScriptType(bench.version, bench.script)
if got != bench.wantType {
b.Fatalf("%q: unexpected result -- got %v, want %v",
bench.name, got, bench.wantType)
}
}
})
}
}
// BenchmarkDetermineRequiredSigs benchmarks the performance of determining the
// required number of signatures for various public key scripts.
func BenchmarkDetermineRequiredSigs(b *testing.B) {
counts := make(map[ScriptType]int)
benches := makeBenchmarks(func(test scriptTest) bool {
// Limit to one of each script type.
counts[test.wantType]++
return counts[test.wantType] == 1
})
for _, bench := range benches {
b.Run(bench.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
got := DetermineRequiredSigs(bench.version, bench.script)
if got != bench.wantSigs {
b.Fatalf("%q: unexpected result -- got %v, want %v",
bench.name, got, bench.wantSigs)
}
}
})
}
}
// BenchmarkExtractAtomicSwapDataPushes benchmarks the performance of
// attempting to extract the atomic swap data pushes from various version 0
// public key scripts.
func BenchmarkExtractAtomicSwapDataPushes(b *testing.B) {
benches := []struct {
name string // benchmark name
script []byte // script to analyze
}{{
name: "v0 complex not atomic swap",
script: complexScriptV0,
}, {
name: "v0 normal valid atomic swap",
script: mustParseShortForm(0, fmt.Sprintf("IF "+
"SIZE 32 EQUALVERIFY "+
"SHA256 DATA_32 "+
"0x9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 "+
"EQUALVERIFY DUP HASH160 "+
"DATA_20 0x0000000000000000000000000000000000000001 "+
"ELSE "+
"300000 CHECKLOCKTIMEVERIFY DROP DUP HASH160 "+
"DATA_20 0x0000000000000000000000000000000000000002 "+
"ENDIF "+
"EQUALVERIFY CHECKSIG")),
}}
for _, bench := range benches {
b.Run(bench.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
noElideSwapData = ExtractAtomicSwapDataPushesV0(bench.script)
}
})
}
}