github.com/btcsuite/btcd@v0.24.0/txscript/script_test.go (about) 1 // Copyright (c) 2013-2017 The btcsuite 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 "reflect" 10 "testing" 11 12 "github.com/btcsuite/btcd/wire" 13 ) 14 15 // TestPushedData ensured the PushedData function extracts the expected data out 16 // of various scripts. 17 func TestPushedData(t *testing.T) { 18 t.Parallel() 19 20 var tests = []struct { 21 script string 22 out [][]byte 23 valid bool 24 }{ 25 { 26 "0 IF 0 ELSE 2 ENDIF", 27 [][]byte{nil, nil}, 28 true, 29 }, 30 { 31 "16777216 10000000", 32 [][]byte{ 33 {0x00, 0x00, 0x00, 0x01}, // 16777216 34 {0x80, 0x96, 0x98, 0x00}, // 10000000 35 }, 36 true, 37 }, 38 { 39 "DUP HASH160 '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem' EQUALVERIFY CHECKSIG", 40 [][]byte{ 41 // 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem 42 { 43 0x31, 0x37, 0x56, 0x5a, 0x4e, 0x58, 0x31, 0x53, 0x4e, 0x35, 44 0x4e, 0x74, 0x4b, 0x61, 0x38, 0x55, 0x51, 0x46, 0x78, 0x77, 45 0x51, 0x62, 0x46, 0x65, 0x46, 0x63, 0x33, 0x69, 0x71, 0x52, 46 0x59, 0x68, 0x65, 0x6d, 47 }, 48 }, 49 true, 50 }, 51 { 52 "PUSHDATA4 1000 EQUAL", 53 nil, 54 false, 55 }, 56 } 57 58 for i, test := range tests { 59 script := mustParseShortForm(test.script) 60 data, err := PushedData(script) 61 if test.valid && err != nil { 62 t.Errorf("TestPushedData failed test #%d: %v\n", i, err) 63 continue 64 } else if !test.valid && err == nil { 65 t.Errorf("TestPushedData failed test #%d: test should "+ 66 "be invalid\n", i) 67 continue 68 } 69 if !reflect.DeepEqual(data, test.out) { 70 t.Errorf("TestPushedData failed test #%d: want: %x "+ 71 "got: %x\n", i, test.out, data) 72 } 73 } 74 } 75 76 // TestHasCanonicalPush ensures the isCanonicalPush function works as expected. 77 func TestHasCanonicalPush(t *testing.T) { 78 t.Parallel() 79 80 const scriptVersion = 0 81 for i := 0; i < 65535; i++ { 82 script, err := NewScriptBuilder().AddInt64(int64(i)).Script() 83 if err != nil { 84 t.Errorf("Script: test #%d unexpected error: %v\n", i, err) 85 continue 86 } 87 if !IsPushOnlyScript(script) { 88 t.Errorf("IsPushOnlyScript: test #%d failed: %x\n", i, script) 89 continue 90 } 91 tokenizer := MakeScriptTokenizer(scriptVersion, script) 92 for tokenizer.Next() { 93 if !isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) { 94 t.Errorf("isCanonicalPush: test #%d failed: %x\n", i, script) 95 break 96 } 97 } 98 } 99 for i := 0; i <= MaxScriptElementSize; i++ { 100 builder := NewScriptBuilder() 101 builder.AddData(bytes.Repeat([]byte{0x49}, i)) 102 script, err := builder.Script() 103 if err != nil { 104 t.Errorf("Script: test #%d unexpected error: %v\n", i, err) 105 continue 106 } 107 if !IsPushOnlyScript(script) { 108 t.Errorf("IsPushOnlyScript: test #%d failed: %x\n", i, script) 109 continue 110 } 111 tokenizer := MakeScriptTokenizer(scriptVersion, script) 112 for tokenizer.Next() { 113 if !isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) { 114 t.Errorf("isCanonicalPush: test #%d failed: %x\n", i, script) 115 break 116 } 117 } 118 } 119 } 120 121 // TestGetPreciseSigOps ensures the more precise signature operation counting 122 // mechanism which includes signatures in P2SH scripts works as expected. 123 func TestGetPreciseSigOps(t *testing.T) { 124 t.Parallel() 125 126 tests := []struct { 127 name string 128 scriptSig []byte 129 nSigOps int 130 }{ 131 { 132 name: "scriptSig doesn't parse", 133 scriptSig: mustParseShortForm("PUSHDATA1 0x02"), 134 }, 135 { 136 name: "scriptSig isn't push only", 137 scriptSig: mustParseShortForm("1 DUP"), 138 nSigOps: 0, 139 }, 140 { 141 name: "scriptSig length 0", 142 scriptSig: nil, 143 nSigOps: 0, 144 }, 145 { 146 name: "No script at the end", 147 // No script at end but still push only. 148 scriptSig: mustParseShortForm("1 1"), 149 nSigOps: 0, 150 }, 151 { 152 name: "pushed script doesn't parse", 153 scriptSig: mustParseShortForm("DATA_2 PUSHDATA1 0x02"), 154 }, 155 } 156 157 // The signature in the p2sh script is nonsensical for the tests since 158 // this script will never be executed. What matters is that it matches 159 // the right pattern. 160 pkScript := mustParseShortForm("HASH160 DATA_20 0x433ec2ac1ffa1b7b7d0" + 161 "27f564529c57197f9ae88 EQUAL") 162 for _, test := range tests { 163 count := GetPreciseSigOpCount(test.scriptSig, pkScript, true) 164 if count != test.nSigOps { 165 t.Errorf("%s: expected count of %d, got %d", test.name, 166 test.nSigOps, count) 167 168 } 169 } 170 } 171 172 // TestGetWitnessSigOpCount tests that the sig op counting for p2wkh, p2wsh, 173 // nested p2sh, and invalid variants are counted properly. 174 func TestGetWitnessSigOpCount(t *testing.T) { 175 t.Parallel() 176 177 tests := []struct { 178 name string 179 180 sigScript []byte 181 pkScript []byte 182 witness wire.TxWitness 183 184 numSigOps int 185 }{ 186 // A regular p2wkh witness program. The output being spent 187 // should only have a single sig-op counted. 188 { 189 name: "p2wkh", 190 pkScript: mustParseShortForm("OP_0 DATA_20 " + 191 "0x365ab47888e150ff46f8d51bce36dcd680f1283f"), 192 witness: wire.TxWitness{ 193 hexToBytes("3045022100ee9fe8f9487afa977" + 194 "6647ebcf0883ce0cd37454d7ce19889d34ba2c9" + 195 "9ce5a9f402200341cb469d0efd3955acb9e46" + 196 "f568d7e2cc10f9084aaff94ced6dc50a59134ad01"), 197 hexToBytes("03f0000d0639a22bfaf217e4c9428" + 198 "9c2b0cc7fa1036f7fd5d9f61a9d6ec153100e"), 199 }, 200 numSigOps: 1, 201 }, 202 // A p2wkh witness program nested within a p2sh output script. 203 // The pattern should be recognized properly and attribute only 204 // a single sig op. 205 { 206 name: "nested p2sh", 207 sigScript: hexToBytes("160014ad0ffa2e387f07" + 208 "e7ead14dc56d5a97dbd6ff5a23"), 209 pkScript: mustParseShortForm("HASH160 DATA_20 " + 210 "0xb3a84b564602a9d68b4c9f19c2ea61458ff7826c EQUAL"), 211 witness: wire.TxWitness{ 212 hexToBytes("3045022100cb1c2ac1ff1d57d" + 213 "db98f7bdead905f8bf5bcc8641b029ce8eef25" + 214 "c75a9e22a4702203be621b5c86b771288706be5" + 215 "a7eee1db4fceabf9afb7583c1cc6ee3f8297b21201"), 216 hexToBytes("03f0000d0639a22bfaf217e4c9" + 217 "4289c2b0cc7fa1036f7fd5d9f61a9d6ec153100e"), 218 }, 219 numSigOps: 1, 220 }, 221 // A p2sh script that spends a 2-of-2 multi-sig output. 222 { 223 name: "p2wsh multi-sig spend", 224 numSigOps: 2, 225 pkScript: hexToBytes("0020e112b88a0cd87ba387f" + 226 "449d443ee2596eb353beb1f0351ab2cba8909d875db23"), 227 witness: wire.TxWitness{ 228 hexToBytes("522103b05faca7ceda92b493" + 229 "3f7acdf874a93de0dc7edc461832031cd69cbb1d1e" + 230 "6fae2102e39092e031c1621c902e3704424e8d8" + 231 "3ca481d4d4eeae1b7970f51c78231207e52ae"), 232 }, 233 }, 234 // A p2wsh witness program. However, the witness script fails 235 // to parse after the valid portion of the script. As a result, 236 // the valid portion of the script should still be counted. 237 { 238 name: "witness script doesn't parse", 239 numSigOps: 1, 240 pkScript: hexToBytes("0020e112b88a0cd87ba387f44" + 241 "9d443ee2596eb353beb1f0351ab2cba8909d875db23"), 242 witness: wire.TxWitness{ 243 mustParseShortForm("DUP HASH160 " + 244 "'17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'" + 245 " EQUALVERIFY CHECKSIG DATA_20 0x91"), 246 }, 247 }, 248 } 249 250 for _, test := range tests { 251 count := GetWitnessSigOpCount(test.sigScript, test.pkScript, 252 test.witness) 253 if count != test.numSigOps { 254 t.Errorf("%s: expected count of %d, got %d", test.name, 255 test.numSigOps, count) 256 257 } 258 } 259 } 260 261 // TestRemoveOpcodes ensures that removing opcodes from scripts behaves as 262 // expected. 263 func TestRemoveOpcodes(t *testing.T) { 264 t.Parallel() 265 266 tests := []struct { 267 name string 268 before string 269 remove byte 270 err error 271 after string 272 }{ 273 { 274 // Nothing to remove. 275 name: "nothing to remove", 276 before: "NOP", 277 remove: OP_CODESEPARATOR, 278 after: "NOP", 279 }, 280 { 281 // Test basic opcode removal. 282 name: "codeseparator 1", 283 before: "NOP CODESEPARATOR TRUE", 284 remove: OP_CODESEPARATOR, 285 after: "NOP TRUE", 286 }, 287 { 288 // The opcode in question is actually part of the data 289 // in a previous opcode. 290 name: "codeseparator by coincidence", 291 before: "NOP DATA_1 CODESEPARATOR TRUE", 292 remove: OP_CODESEPARATOR, 293 after: "NOP DATA_1 CODESEPARATOR TRUE", 294 }, 295 { 296 name: "invalid opcode", 297 before: "CAT", 298 remove: OP_CODESEPARATOR, 299 after: "CAT", 300 }, 301 { 302 name: "invalid length (instruction)", 303 before: "PUSHDATA1", 304 remove: OP_CODESEPARATOR, 305 err: scriptError(ErrMalformedPush, ""), 306 }, 307 { 308 name: "invalid length (data)", 309 before: "PUSHDATA1 0xff 0xfe", 310 remove: OP_CODESEPARATOR, 311 err: scriptError(ErrMalformedPush, ""), 312 }, 313 } 314 315 // tstRemoveOpcode is a convenience function to parse the provided 316 // raw script, remove the passed opcode, then unparse the result back 317 // into a raw script. 318 const scriptVersion = 0 319 tstRemoveOpcode := func(script []byte, opcode byte) ([]byte, error) { 320 if err := checkScriptParses(scriptVersion, script); err != nil { 321 return nil, err 322 } 323 return removeOpcodeRaw(script, opcode), nil 324 } 325 326 for _, test := range tests { 327 before := mustParseShortForm(test.before) 328 after := mustParseShortForm(test.after) 329 result, err := tstRemoveOpcode(before, test.remove) 330 if e := tstCheckScriptError(err, test.err); e != nil { 331 t.Errorf("%s: %v", test.name, e) 332 continue 333 } 334 335 if !bytes.Equal(after, result) { 336 t.Errorf("%s: value does not equal expected: exp: %q"+ 337 " got: %q", test.name, after, result) 338 } 339 } 340 } 341 342 // TestRemoveOpcodeByData ensures that removing data carrying opcodes based on 343 // the data they contain works as expected. 344 func TestRemoveOpcodeByData(t *testing.T) { 345 t.Parallel() 346 347 tests := []struct { 348 name string 349 before []byte 350 remove []byte 351 err error 352 after []byte 353 }{ 354 { 355 name: "nothing to do", 356 before: []byte{OP_NOP}, 357 remove: []byte{1, 2, 3, 4}, 358 after: []byte{OP_NOP}, 359 }, 360 { 361 name: "simple case", 362 before: []byte{OP_DATA_4, 1, 2, 3, 4}, 363 remove: []byte{1, 2, 3, 4}, 364 after: nil, 365 }, 366 { 367 name: "simple case (miss)", 368 before: []byte{OP_DATA_4, 1, 2, 3, 4}, 369 remove: []byte{1, 2, 3, 5}, 370 after: []byte{OP_DATA_4, 1, 2, 3, 4}, 371 }, 372 { 373 // padded to keep it canonical. 374 name: "simple case (pushdata1)", 375 before: append(append([]byte{OP_PUSHDATA1, 76}, 376 bytes.Repeat([]byte{0}, 72)...), 377 []byte{1, 2, 3, 4}...), 378 remove: []byte{1, 2, 3, 4}, 379 after: nil, 380 }, 381 { 382 name: "simple case (pushdata1 miss)", 383 before: append(append([]byte{OP_PUSHDATA1, 76}, 384 bytes.Repeat([]byte{0}, 72)...), 385 []byte{1, 2, 3, 4}...), 386 remove: []byte{1, 2, 3, 5}, 387 after: append(append([]byte{OP_PUSHDATA1, 76}, 388 bytes.Repeat([]byte{0}, 72)...), 389 []byte{1, 2, 3, 4}...), 390 }, 391 { 392 name: "simple case (pushdata1 miss noncanonical)", 393 before: []byte{OP_PUSHDATA1, 4, 1, 2, 3, 4}, 394 remove: []byte{1, 2, 3, 4}, 395 after: []byte{OP_PUSHDATA1, 4, 1, 2, 3, 4}, 396 }, 397 { 398 name: "simple case (pushdata2)", 399 before: append(append([]byte{OP_PUSHDATA2, 0, 1}, 400 bytes.Repeat([]byte{0}, 252)...), 401 []byte{1, 2, 3, 4}...), 402 remove: []byte{1, 2, 3, 4}, 403 after: nil, 404 }, 405 { 406 name: "simple case (pushdata2 miss)", 407 before: append(append([]byte{OP_PUSHDATA2, 0, 1}, 408 bytes.Repeat([]byte{0}, 252)...), 409 []byte{1, 2, 3, 4}...), 410 remove: []byte{1, 2, 3, 4, 5}, 411 after: append(append([]byte{OP_PUSHDATA2, 0, 1}, 412 bytes.Repeat([]byte{0}, 252)...), 413 []byte{1, 2, 3, 4}...), 414 }, 415 { 416 name: "simple case (pushdata2 miss noncanonical)", 417 before: []byte{OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, 418 remove: []byte{1, 2, 3, 4}, 419 after: []byte{OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, 420 }, 421 { 422 // This is padded to make the push canonical. 423 name: "simple case (pushdata4)", 424 before: append(append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, 425 bytes.Repeat([]byte{0}, 65532)...), 426 []byte{1, 2, 3, 4}...), 427 remove: []byte{1, 2, 3, 4}, 428 after: nil, 429 }, 430 { 431 name: "simple case (pushdata4 miss noncanonical)", 432 before: []byte{OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, 433 remove: []byte{1, 2, 3, 4}, 434 after: []byte{OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, 435 }, 436 { 437 // This is padded to make the push canonical. 438 name: "simple case (pushdata4 miss)", 439 before: append(append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, 440 bytes.Repeat([]byte{0}, 65532)...), []byte{1, 2, 3, 4}...), 441 remove: []byte{1, 2, 3, 4, 5}, 442 after: append(append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, 443 bytes.Repeat([]byte{0}, 65532)...), []byte{1, 2, 3, 4}...), 444 }, 445 { 446 name: "invalid opcode ", 447 before: []byte{OP_UNKNOWN187}, 448 remove: []byte{1, 2, 3, 4}, 449 after: []byte{OP_UNKNOWN187}, 450 }, 451 { 452 name: "invalid length (instruction)", 453 before: []byte{OP_PUSHDATA1}, 454 remove: []byte{1, 2, 3, 4}, 455 err: scriptError(ErrMalformedPush, ""), 456 }, 457 { 458 name: "invalid length (data)", 459 before: []byte{OP_PUSHDATA1, 255, 254}, 460 remove: []byte{1, 2, 3, 4}, 461 err: scriptError(ErrMalformedPush, ""), 462 }, 463 } 464 465 // tstRemoveOpcodeByData is a convenience function to ensure the provided 466 // script parses before attempting to remove the passed data. 467 const scriptVersion = 0 468 tstRemoveOpcodeByData := func(script []byte, data []byte) ([]byte, error) { 469 if err := checkScriptParses(scriptVersion, script); err != nil { 470 return nil, err 471 } 472 473 return removeOpcodeByData(script, data), nil 474 } 475 476 for _, test := range tests { 477 result, err := tstRemoveOpcodeByData(test.before, test.remove) 478 if e := tstCheckScriptError(err, test.err); e != nil { 479 t.Errorf("%s: %v", test.name, e) 480 continue 481 } 482 483 if !bytes.Equal(test.after, result) { 484 t.Errorf("%s: value does not equal expected: exp: %q"+ 485 " got: %q", test.name, test.after, result) 486 } 487 } 488 } 489 490 // TestIsPayToScriptHash ensures the IsPayToScriptHash function returns the 491 // expected results for all the scripts in scriptClassTests. 492 func TestIsPayToScriptHash(t *testing.T) { 493 t.Parallel() 494 495 for _, test := range scriptClassTests { 496 script := mustParseShortForm(test.script) 497 shouldBe := (test.class == ScriptHashTy) 498 p2sh := IsPayToScriptHash(script) 499 if p2sh != shouldBe { 500 t.Errorf("%s: expected p2sh %v, got %v", test.name, 501 shouldBe, p2sh) 502 } 503 } 504 } 505 506 // TestIsPayToWitnessScriptHash ensures the IsPayToWitnessScriptHash function 507 // returns the expected results for all the scripts in scriptClassTests. 508 func TestIsPayToWitnessScriptHash(t *testing.T) { 509 t.Parallel() 510 511 for _, test := range scriptClassTests { 512 script := mustParseShortForm(test.script) 513 shouldBe := (test.class == WitnessV0ScriptHashTy) 514 p2wsh := IsPayToWitnessScriptHash(script) 515 if p2wsh != shouldBe { 516 t.Errorf("%s: expected p2wsh %v, got %v", test.name, 517 shouldBe, p2wsh) 518 } 519 } 520 } 521 522 // TestIsPayToWitnessPubKeyHash ensures the IsPayToWitnessPubKeyHash function 523 // returns the expected results for all the scripts in scriptClassTests. 524 func TestIsPayToWitnessPubKeyHash(t *testing.T) { 525 t.Parallel() 526 527 for _, test := range scriptClassTests { 528 script := mustParseShortForm(test.script) 529 shouldBe := (test.class == WitnessV0PubKeyHashTy) 530 p2wkh := IsPayToWitnessPubKeyHash(script) 531 if p2wkh != shouldBe { 532 t.Errorf("%s: expected p2wkh %v, got %v", test.name, 533 shouldBe, p2wkh) 534 } 535 } 536 } 537 538 // TestHasCanonicalPushes ensures the isCanonicalPush function properly 539 // determines what is considered a canonical push for the purposes of 540 // removeOpcodeByData. 541 func TestHasCanonicalPushes(t *testing.T) { 542 t.Parallel() 543 544 const scriptVersion = 0 545 tests := []struct { 546 name string 547 script string 548 expected bool 549 }{ 550 { 551 name: "does not parse", 552 script: "0x046708afdb0fe5548271967f1a67130b7105cd6a82" + 553 "8e03909a67962e0ea1f61d", 554 expected: false, 555 }, 556 { 557 name: "non-canonical push", 558 script: "PUSHDATA1 0x04 0x01020304", 559 expected: false, 560 }, 561 } 562 563 for _, test := range tests { 564 script := mustParseShortForm(test.script) 565 if err := checkScriptParses(scriptVersion, script); err != nil { 566 if test.expected { 567 t.Errorf("%q: script parse failed: %v", test.name, err) 568 } 569 continue 570 } 571 tokenizer := MakeScriptTokenizer(scriptVersion, script) 572 for tokenizer.Next() { 573 result := isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) 574 if result != test.expected { 575 t.Errorf("%q: isCanonicalPush wrong result\ngot: %v\nwant: %v", 576 test.name, result, test.expected) 577 break 578 } 579 } 580 } 581 } 582 583 // TestIsPushOnlyScript ensures the IsPushOnlyScript function returns the 584 // expected results. 585 func TestIsPushOnlyScript(t *testing.T) { 586 t.Parallel() 587 588 test := struct { 589 name string 590 script []byte 591 expected bool 592 }{ 593 name: "does not parse", 594 script: mustParseShortForm("0x046708afdb0fe5548271967f1a67130" + 595 "b7105cd6a828e03909a67962e0ea1f61d"), 596 expected: false, 597 } 598 599 if IsPushOnlyScript(test.script) != test.expected { 600 t.Errorf("IsPushOnlyScript (%s) wrong result\ngot: %v\nwant: "+ 601 "%v", test.name, true, test.expected) 602 } 603 } 604 605 // TestIsUnspendable ensures the IsUnspendable function returns the expected 606 // results. 607 func TestIsUnspendable(t *testing.T) { 608 t.Parallel() 609 610 tests := []struct { 611 name string 612 pkScript []byte 613 expected bool 614 }{ 615 { 616 // Unspendable 617 pkScript: []byte{0x6a, 0x04, 0x74, 0x65, 0x73, 0x74}, 618 expected: true, 619 }, 620 { 621 // Spendable 622 pkScript: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0, 623 0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45, 624 0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6, 625 0xfa, 0x0b, 0x5c, 0x88, 0xac}, 626 expected: false, 627 }, 628 { 629 // Spendable 630 pkScript: []byte{0xa9, 0x14, 0x82, 0x1d, 0xba, 0x94, 0xbc, 0xfb, 631 0xa2, 0x57, 0x36, 0xa3, 0x9e, 0x5d, 0x14, 0x5d, 0x69, 0x75, 632 0xba, 0x8c, 0x0b, 0x42, 0x87}, 633 expected: false, 634 }, 635 { 636 // Not Necessarily Unspendable 637 pkScript: []byte{}, 638 expected: false, 639 }, 640 { 641 // Spendable 642 pkScript: []byte{OP_TRUE}, 643 expected: false, 644 }, 645 { 646 // Unspendable 647 pkScript: []byte{OP_RETURN}, 648 expected: true, 649 }, 650 } 651 652 for i, test := range tests { 653 res := IsUnspendable(test.pkScript) 654 if res != test.expected { 655 t.Errorf("TestIsUnspendable #%d failed: got %v want %v", 656 i, res, test.expected) 657 continue 658 } 659 } 660 }