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