github.com/lbryio/lbcd@v0.22.119/txscript/bench_test.go (about)

     1  // Copyright (c) 2018-2019 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package txscript
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"testing"
    12  
    13  	"github.com/lbryio/lbcd/chaincfg"
    14  	"github.com/lbryio/lbcd/wire"
    15  )
    16  
    17  var (
    18  	// manyInputsBenchTx is a transaction that contains a lot of inputs which is
    19  	// useful for benchmarking signature hash calculation.
    20  	manyInputsBenchTx wire.MsgTx
    21  
    22  	// A mock previous output script to use in the signing benchmark.
    23  	prevOutScript = hexToBytes("a914f5916158e3e2c4551c1796708db8367207ed13bb87")
    24  )
    25  
    26  func init() {
    27  	// tx 620f57c92cf05a7f7e7f7d28255d5f7089437bc48e34dcfebf7751d08b7fb8f5
    28  	txHex, err := ioutil.ReadFile("data/many_inputs_tx.hex")
    29  	if err != nil {
    30  		panic(fmt.Sprintf("unable to read benchmark tx file: %v", err))
    31  	}
    32  
    33  	txBytes := hexToBytes(string(txHex))
    34  	err = manyInputsBenchTx.Deserialize(bytes.NewReader(txBytes))
    35  	if err != nil {
    36  		panic(err)
    37  	}
    38  }
    39  
    40  // BenchmarkCalcSigHash benchmarks how long it takes to calculate the signature
    41  // hashes for all inputs of a transaction with many inputs.
    42  func BenchmarkCalcSigHash(b *testing.B) {
    43  	b.ReportAllocs()
    44  	for i := 0; i < b.N; i++ {
    45  		for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
    46  			_, err := CalcSignatureHash(prevOutScript, SigHashAll,
    47  				&manyInputsBenchTx, j)
    48  			if err != nil {
    49  				b.Fatalf("failed to calc signature hash: %v", err)
    50  			}
    51  		}
    52  	}
    53  }
    54  
    55  // BenchmarkCalcWitnessSigHash benchmarks how long it takes to calculate the
    56  // witness signature hashes for all inputs of a transaction with many inputs.
    57  func BenchmarkCalcWitnessSigHash(b *testing.B) {
    58  	sigHashes := NewTxSigHashes(&manyInputsBenchTx)
    59  
    60  	b.ResetTimer()
    61  	b.ReportAllocs()
    62  	for i := 0; i < b.N; i++ {
    63  		for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
    64  			_, err := CalcWitnessSigHash(
    65  				prevOutScript, sigHashes, SigHashAll,
    66  				&manyInputsBenchTx, j, 5,
    67  			)
    68  			if err != nil {
    69  				b.Fatalf("failed to calc signature hash: %v", err)
    70  			}
    71  		}
    72  	}
    73  }
    74  
    75  // genComplexScript returns a script comprised of half as many opcodes as the
    76  // maximum allowed followed by as many max size data pushes fit without
    77  // exceeding the max allowed script size.
    78  func genComplexScript() ([]byte, error) {
    79  	var scriptLen int
    80  	builder := NewScriptBuilder()
    81  	for i := 0; i < MaxOpsPerScript/2; i++ {
    82  		builder.AddOp(OP_TRUE)
    83  		scriptLen++
    84  	}
    85  	maxData := bytes.Repeat([]byte{0x02}, MaxScriptElementSize)
    86  	for i := 0; i < (MaxScriptSize-scriptLen)/(MaxScriptElementSize+3); i++ {
    87  		builder.AddData(maxData)
    88  	}
    89  	return builder.Script()
    90  }
    91  
    92  // BenchmarkScriptParsing benchmarks how long it takes to parse a very large
    93  // script.
    94  func BenchmarkScriptParsing(b *testing.B) {
    95  	script, err := genComplexScript()
    96  	if err != nil {
    97  		b.Fatalf("failed to create benchmark script: %v", err)
    98  	}
    99  
   100  	const scriptVersion = 0
   101  	b.ResetTimer()
   102  	b.ReportAllocs()
   103  	for i := 0; i < b.N; i++ {
   104  		tokenizer := MakeScriptTokenizer(scriptVersion, script)
   105  		for tokenizer.Next() {
   106  			_ = tokenizer.Opcode()
   107  			_ = tokenizer.Data()
   108  			_ = tokenizer.ByteIndex()
   109  		}
   110  		if err := tokenizer.Err(); err != nil {
   111  			b.Fatalf("failed to parse script: %v", err)
   112  		}
   113  	}
   114  }
   115  
   116  // BenchmarkDisasmString benchmarks how long it takes to disassemble a very
   117  // large script.
   118  func BenchmarkDisasmString(b *testing.B) {
   119  	script, err := genComplexScript()
   120  	if err != nil {
   121  		b.Fatalf("failed to create benchmark script: %v", err)
   122  	}
   123  
   124  	b.ResetTimer()
   125  	b.ReportAllocs()
   126  	for i := 0; i < b.N; i++ {
   127  		_, err := DisasmString(script)
   128  		if err != nil {
   129  			b.Fatalf("failed to disasm script: %v", err)
   130  		}
   131  	}
   132  }
   133  
   134  // BenchmarkIsPubKeyScript benchmarks how long it takes to analyze a very large
   135  // script to determine if it is a standard pay-to-pubkey script.
   136  func BenchmarkIsPubKeyScript(b *testing.B) {
   137  	script, err := genComplexScript()
   138  	if err != nil {
   139  		b.Fatalf("failed to create benchmark script: %v", err)
   140  	}
   141  
   142  	b.ResetTimer()
   143  	b.ReportAllocs()
   144  	for i := 0; i < b.N; i++ {
   145  		_ = IsPayToPubKey(script)
   146  	}
   147  }
   148  
   149  // BenchmarkIsPubKeyHashScript benchmarks how long it takes to analyze a very
   150  // large script to determine if it is a standard pay-to-pubkey-hash script.
   151  func BenchmarkIsPubKeyHashScript(b *testing.B) {
   152  	script, err := genComplexScript()
   153  	if err != nil {
   154  		b.Fatalf("failed to create benchmark script: %v", err)
   155  	}
   156  
   157  	b.ResetTimer()
   158  	b.ReportAllocs()
   159  	for i := 0; i < b.N; i++ {
   160  		_ = IsPayToPubKeyHash(script)
   161  	}
   162  }
   163  
   164  // BenchmarkIsPayToScriptHash benchmarks how long it takes IsPayToScriptHash to
   165  // analyze a very large script.
   166  func BenchmarkIsPayToScriptHash(b *testing.B) {
   167  	script, err := genComplexScript()
   168  	if err != nil {
   169  		b.Fatalf("failed to create benchmark script: %v", err)
   170  	}
   171  
   172  	b.ResetTimer()
   173  	b.ReportAllocs()
   174  	for i := 0; i < b.N; i++ {
   175  		_ = IsPayToScriptHash(script)
   176  	}
   177  }
   178  
   179  // BenchmarkIsMultisigScriptLarge benchmarks how long it takes IsMultisigScript
   180  // to analyze a very large script.
   181  func BenchmarkIsMultisigScriptLarge(b *testing.B) {
   182  	script, err := genComplexScript()
   183  	if err != nil {
   184  		b.Fatalf("failed to create benchmark script: %v", err)
   185  	}
   186  
   187  	b.ResetTimer()
   188  	b.ReportAllocs()
   189  	for i := 0; i < b.N; i++ {
   190  		isMultisig, err := IsMultisigScript(script)
   191  		if err != nil {
   192  			b.Fatalf("unexpected err: %v", err)
   193  		}
   194  		if isMultisig {
   195  			b.Fatalf("script should NOT be reported as mutisig script")
   196  		}
   197  	}
   198  }
   199  
   200  // BenchmarkIsMultisigScript benchmarks how long it takes IsMultisigScript to
   201  // analyze a 1-of-2 multisig public key script.
   202  func BenchmarkIsMultisigScript(b *testing.B) {
   203  	multisigShortForm := "1 " +
   204  		"DATA_33 " +
   205  		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
   206  		"DATA_33 " +
   207  		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
   208  		"2 CHECKMULTISIG"
   209  	pkScript := mustParseShortForm(multisigShortForm)
   210  
   211  	b.ResetTimer()
   212  	b.ReportAllocs()
   213  	for i := 0; i < b.N; i++ {
   214  		isMultisig, err := IsMultisigScript(pkScript)
   215  		if err != nil {
   216  			b.Fatalf("unexpected err: %v", err)
   217  		}
   218  		if !isMultisig {
   219  			b.Fatalf("script should be reported as a mutisig script")
   220  		}
   221  	}
   222  }
   223  
   224  // BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
   225  // to analyze a very large script.
   226  func BenchmarkIsMultisigSigScriptLarge(b *testing.B) {
   227  	script, err := genComplexScript()
   228  	if err != nil {
   229  		b.Fatalf("failed to create benchmark script: %v", err)
   230  	}
   231  
   232  	b.ResetTimer()
   233  	b.ReportAllocs()
   234  	for i := 0; i < b.N; i++ {
   235  		if IsMultisigSigScript(script) {
   236  			b.Fatalf("script should NOT be reported as mutisig sig script")
   237  		}
   238  	}
   239  }
   240  
   241  // BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
   242  // to analyze both a 1-of-2 multisig public key script (which should be false)
   243  // and a signature script comprised of a pay-to-script-hash 1-of-2 multisig
   244  // redeem script (which should be true).
   245  func BenchmarkIsMultisigSigScript(b *testing.B) {
   246  	multisigShortForm := "1 " +
   247  		"DATA_33 " +
   248  		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
   249  		"DATA_33 " +
   250  		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
   251  		"2 CHECKMULTISIG"
   252  	pkScript := mustParseShortForm(multisigShortForm)
   253  
   254  	sigHex := "0x304402205795c3ab6ba11331eeac757bf1fc9c34bef0c7e1a9c8bd5eebb8" +
   255  		"82f3b79c5838022001e0ab7b4c7662e4522dc5fa479e4b4133fa88c6a53d895dc1d5" +
   256  		"2eddc7bbcf2801 "
   257  	sigScript := mustParseShortForm("DATA_71 " + sigHex + "DATA_71 " +
   258  		multisigShortForm)
   259  
   260  	b.ResetTimer()
   261  	b.ReportAllocs()
   262  	for i := 0; i < b.N; i++ {
   263  		if IsMultisigSigScript(pkScript) {
   264  			b.Fatalf("script should NOT be reported as mutisig sig script")
   265  		}
   266  		if !IsMultisigSigScript(sigScript) {
   267  			b.Fatalf("script should be reported as a mutisig sig script")
   268  		}
   269  	}
   270  }
   271  
   272  // BenchmarkIsPushOnlyScript benchmarks how long it takes IsPushOnlyScript to
   273  // analyze a very large script.
   274  func BenchmarkIsPushOnlyScript(b *testing.B) {
   275  	script, err := genComplexScript()
   276  	if err != nil {
   277  		b.Fatalf("failed to create benchmark script: %v", err)
   278  	}
   279  
   280  	b.ResetTimer()
   281  	b.ReportAllocs()
   282  	for i := 0; i < b.N; i++ {
   283  		_ = IsPushOnlyScript(script)
   284  	}
   285  }
   286  
   287  // BenchmarkIsWitnessPubKeyHash benchmarks how long it takes to analyze a very
   288  // large script to determine if it is a standard witness pubkey hash script.
   289  func BenchmarkIsWitnessPubKeyHash(b *testing.B) {
   290  	script, err := genComplexScript()
   291  	if err != nil {
   292  		b.Fatalf("failed to create benchmark script: %v", err)
   293  	}
   294  
   295  	b.ResetTimer()
   296  	b.ReportAllocs()
   297  	for i := 0; i < b.N; i++ {
   298  		_ = IsPayToWitnessPubKeyHash(script)
   299  	}
   300  }
   301  
   302  // BenchmarkIsWitnessScriptHash benchmarks how long it takes to analyze a very
   303  // large script to determine if it is a standard witness script hash script.
   304  func BenchmarkIsWitnessScriptHash(b *testing.B) {
   305  	script, err := genComplexScript()
   306  	if err != nil {
   307  		b.Fatalf("failed to create benchmark script: %v", err)
   308  	}
   309  
   310  	b.ResetTimer()
   311  	b.ReportAllocs()
   312  	for i := 0; i < b.N; i++ {
   313  		_ = IsPayToWitnessScriptHash(script)
   314  	}
   315  }
   316  
   317  // BenchmarkIsNullDataScript benchmarks how long it takes to analyze a very
   318  // large script to determine if it is a standard nulldata script.
   319  func BenchmarkIsNullDataScript(b *testing.B) {
   320  	script, err := genComplexScript()
   321  	if err != nil {
   322  		b.Fatalf("failed to create benchmark script: %v", err)
   323  	}
   324  
   325  	b.ResetTimer()
   326  	b.ReportAllocs()
   327  	for i := 0; i < b.N; i++ {
   328  		_ = IsNullData(script)
   329  	}
   330  }
   331  
   332  // BenchmarkIsUnspendable benchmarks how long it takes IsUnspendable to analyze
   333  // a very large script.
   334  func BenchmarkIsUnspendable(b *testing.B) {
   335  	script, err := genComplexScript()
   336  	if err != nil {
   337  		b.Fatalf("failed to create benchmark script: %v", err)
   338  	}
   339  	b.ResetTimer()
   340  	b.ReportAllocs()
   341  	for i := 0; i < b.N; i++ {
   342  		_ = IsUnspendable(script)
   343  	}
   344  }
   345  
   346  // BenchmarkGetSigOpCount benchmarks how long it takes to count the signature
   347  // operations of a very large script.
   348  func BenchmarkGetSigOpCount(b *testing.B) {
   349  	script, err := genComplexScript()
   350  	if err != nil {
   351  		b.Fatalf("failed to create benchmark script: %v", err)
   352  	}
   353  
   354  	b.ResetTimer()
   355  	b.ReportAllocs()
   356  	for i := 0; i < b.N; i++ {
   357  		_ = GetSigOpCount(script)
   358  	}
   359  }
   360  
   361  // BenchmarkGetPreciseSigOpCount benchmarks how long it takes to count the
   362  // signature operations of a very large script using the more precise counting
   363  // method.
   364  func BenchmarkGetPreciseSigOpCount(b *testing.B) {
   365  	redeemScript, err := genComplexScript()
   366  	if err != nil {
   367  		b.Fatalf("failed to create benchmark script: %v", err)
   368  	}
   369  
   370  	// Create a fake pay-to-script-hash to pass the necessary checks and create
   371  	// the signature script accordingly by pushing the generated "redeem" script
   372  	// as the final data push so the benchmark will cover the p2sh path.
   373  	scriptHash := "0x0000000000000000000000000000000000000001"
   374  	pkScript := mustParseShortForm("HASH160 DATA_20 " + scriptHash + " EQUAL")
   375  	sigScript, err := NewScriptBuilder().AddFullData(redeemScript).Script()
   376  	if err != nil {
   377  		b.Fatalf("failed to create signature script: %v", err)
   378  	}
   379  
   380  	b.ResetTimer()
   381  	b.ReportAllocs()
   382  	for i := 0; i < b.N; i++ {
   383  		_ = GetPreciseSigOpCount(sigScript, pkScript, true)
   384  	}
   385  }
   386  
   387  // BenchmarkGetWitnessSigOpCount benchmarks how long it takes to count the
   388  // witness signature operations of a very large script.
   389  func BenchmarkGetWitnessSigOpCountP2WKH(b *testing.B) {
   390  	pkScript := mustParseShortForm("OP_0 DATA_20 0x0000000000000000000000000000000000000000")
   391  	redeemScript, err := genComplexScript()
   392  	if err != nil {
   393  		b.Fatalf("failed to create benchmark script: %v", err)
   394  	}
   395  
   396  	witness := wire.TxWitness{
   397  		redeemScript,
   398  	}
   399  
   400  	b.ResetTimer()
   401  	b.ReportAllocs()
   402  	for i := 0; i < b.N; i++ {
   403  		_ = GetWitnessSigOpCount(nil, pkScript, witness)
   404  	}
   405  }
   406  
   407  // BenchmarkGetWitnessSigOpCount benchmarks how long it takes to count the
   408  // witness signature operations of a very large script.
   409  func BenchmarkGetWitnessSigOpCountNested(b *testing.B) {
   410  	pkScript := mustParseShortForm("HASH160 DATA_20 0x0000000000000000000000000000000000000000 OP_EQUAL")
   411  	sigScript := mustParseShortForm("DATA_22 0x001600000000000000000000000000000000000000000000")
   412  	redeemScript, err := genComplexScript()
   413  	if err != nil {
   414  		b.Fatalf("failed to create benchmark script: %v", err)
   415  	}
   416  
   417  	witness := wire.TxWitness{
   418  		redeemScript,
   419  	}
   420  
   421  	b.ResetTimer()
   422  	b.ReportAllocs()
   423  	for i := 0; i < b.N; i++ {
   424  		_ = GetWitnessSigOpCount(sigScript, pkScript, witness)
   425  	}
   426  }
   427  
   428  // BenchmarkGetScriptClass benchmarks how long it takes GetScriptClass to
   429  // analyze a very large script.
   430  func BenchmarkGetScriptClass(b *testing.B) {
   431  	script, err := genComplexScript()
   432  	if err != nil {
   433  		b.Fatalf("failed to create benchmark script: %v", err)
   434  	}
   435  
   436  	b.ResetTimer()
   437  	b.ReportAllocs()
   438  	for i := 0; i < b.N; i++ {
   439  		_ = GetScriptClass(script)
   440  	}
   441  }
   442  
   443  // BenchmarkPushedData benchmarks how long it takes to extract the pushed data
   444  // from a very large script.
   445  func BenchmarkPushedData(b *testing.B) {
   446  	script, err := genComplexScript()
   447  	if err != nil {
   448  		b.Fatalf("failed to create benchmark script: %v", err)
   449  	}
   450  
   451  	b.ResetTimer()
   452  	b.ReportAllocs()
   453  	for i := 0; i < b.N; i++ {
   454  		_, err := PushedData(script)
   455  		if err != nil {
   456  			b.Fatalf("unexpected err: %v", err)
   457  		}
   458  	}
   459  }
   460  
   461  // BenchmarkExtractAtomicSwapDataPushesLarge benchmarks how long it takes
   462  // ExtractAtomicSwapDataPushes to analyze a very large script.
   463  func BenchmarkExtractAtomicSwapDataPushesLarge(b *testing.B) {
   464  	script, err := genComplexScript()
   465  	if err != nil {
   466  		b.Fatalf("failed to create benchmark script: %v", err)
   467  	}
   468  
   469  	const scriptVersion = 0
   470  	b.ResetTimer()
   471  	b.ReportAllocs()
   472  	for i := 0; i < b.N; i++ {
   473  		_, err := ExtractAtomicSwapDataPushes(scriptVersion, script)
   474  		if err != nil {
   475  			b.Fatalf("unexpected err: %v", err)
   476  		}
   477  	}
   478  }
   479  
   480  // BenchmarkExtractAtomicSwapDataPushesLarge benchmarks how long it takes
   481  // ExtractAtomicSwapDataPushes to analyze a standard atomic swap script.
   482  func BenchmarkExtractAtomicSwapDataPushes(b *testing.B) {
   483  	secret := "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
   484  	recipient := "0000000000000000000000000000000000000001"
   485  	refund := "0000000000000000000000000000000000000002"
   486  	script := mustParseShortForm(fmt.Sprintf("IF SIZE 32 EQUALVERIFY SHA256 "+
   487  		"DATA_32 0x%s EQUALVERIFY DUP HASH160 DATA_20 0x%s ELSE 300000 "+
   488  		"CHECKLOCKTIMEVERIFY DROP DUP HASH160 DATA_20 0x%s ENDIF "+
   489  		"EQUALVERIFY CHECKSIG", secret, recipient, refund))
   490  
   491  	const scriptVersion = 0
   492  	b.ResetTimer()
   493  	b.ReportAllocs()
   494  	for i := 0; i < b.N; i++ {
   495  		_, err := ExtractAtomicSwapDataPushes(scriptVersion, script)
   496  		if err != nil {
   497  			b.Fatalf("unexpected err: %v", err)
   498  		}
   499  	}
   500  }
   501  
   502  // BenchmarkExtractPkScriptAddrsLarge benchmarks how long it takes to analyze
   503  // and potentially extract addresses from a very large non-standard script.
   504  func BenchmarkExtractPkScriptAddrsLarge(b *testing.B) {
   505  	script, err := genComplexScript()
   506  	if err != nil {
   507  		b.Fatalf("failed to create benchmark script: %v", err)
   508  	}
   509  
   510  	params := &chaincfg.MainNetParams
   511  	b.ResetTimer()
   512  	b.ReportAllocs()
   513  	for i := 0; i < b.N; i++ {
   514  		_, _, _, err := ExtractPkScriptAddrs(script, params)
   515  		if err != nil {
   516  			b.Fatalf("unexpected err: %v", err)
   517  		}
   518  	}
   519  }
   520  
   521  // BenchmarkExtractPkScriptAddrs benchmarks how long it takes to analyze and
   522  // potentially extract addresses from a typical script.
   523  func BenchmarkExtractPkScriptAddrs(b *testing.B) {
   524  	script := mustParseShortForm("OP_DUP HASH160 " +
   525  		"DATA_20 0x0102030405060708090a0b0c0d0e0f1011121314 " +
   526  		"EQUAL")
   527  
   528  	params := &chaincfg.MainNetParams
   529  	b.ResetTimer()
   530  	b.ReportAllocs()
   531  	for i := 0; i < b.N; i++ {
   532  		_, _, _, err := ExtractPkScriptAddrs(script, params)
   533  		if err != nil {
   534  			b.Fatalf("unexpected err: %v", err)
   535  		}
   536  	}
   537  }