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