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 }