github.com/lbryio/lbcd@v0.22.119/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/lbryio/lbcd/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 tests := []struct { 177 name string 178 179 sigScript []byte 180 pkScript []byte 181 witness wire.TxWitness 182 183 numSigOps int 184 }{ 185 // A regualr p2wkh witness program. The output being spent 186 // should only have a single sig-op counted. 187 { 188 name: "p2wkh", 189 pkScript: mustParseShortForm("OP_0 DATA_20 " + 190 "0x365ab47888e150ff46f8d51bce36dcd680f1283f"), 191 witness: wire.TxWitness{ 192 hexToBytes("3045022100ee9fe8f9487afa977" + 193 "6647ebcf0883ce0cd37454d7ce19889d34ba2c9" + 194 "9ce5a9f402200341cb469d0efd3955acb9e46" + 195 "f568d7e2cc10f9084aaff94ced6dc50a59134ad01"), 196 hexToBytes("03f0000d0639a22bfaf217e4c9428" + 197 "9c2b0cc7fa1036f7fd5d9f61a9d6ec153100e"), 198 }, 199 numSigOps: 1, 200 }, 201 // A p2wkh witness program nested within a p2sh output script. 202 // The pattern should be recognized properly and attribute only 203 // a single sig op. 204 { 205 name: "nested p2sh", 206 sigScript: hexToBytes("160014ad0ffa2e387f07" + 207 "e7ead14dc56d5a97dbd6ff5a23"), 208 pkScript: mustParseShortForm("HASH160 DATA_20 " + 209 "0xb3a84b564602a9d68b4c9f19c2ea61458ff7826c EQUAL"), 210 witness: wire.TxWitness{ 211 hexToBytes("3045022100cb1c2ac1ff1d57d" + 212 "db98f7bdead905f8bf5bcc8641b029ce8eef25" + 213 "c75a9e22a4702203be621b5c86b771288706be5" + 214 "a7eee1db4fceabf9afb7583c1cc6ee3f8297b21201"), 215 hexToBytes("03f0000d0639a22bfaf217e4c9" + 216 "4289c2b0cc7fa1036f7fd5d9f61a9d6ec153100e"), 217 }, 218 numSigOps: 1, 219 }, 220 // A p2sh script that spends a 2-of-2 multi-sig output. 221 { 222 name: "p2wsh multi-sig spend", 223 numSigOps: 2, 224 pkScript: hexToBytes("0020e112b88a0cd87ba387f" + 225 "449d443ee2596eb353beb1f0351ab2cba8909d875db23"), 226 witness: wire.TxWitness{ 227 hexToBytes("522103b05faca7ceda92b493" + 228 "3f7acdf874a93de0dc7edc461832031cd69cbb1d1e" + 229 "6fae2102e39092e031c1621c902e3704424e8d8" + 230 "3ca481d4d4eeae1b7970f51c78231207e52ae"), 231 }, 232 }, 233 // A p2wsh witness program. However, the witness script fails 234 // to parse after the valid portion of the script. As a result, 235 // the valid portion of the script should still be counted. 236 { 237 name: "witness script doesn't parse", 238 numSigOps: 1, 239 pkScript: hexToBytes("0020e112b88a0cd87ba387f44" + 240 "9d443ee2596eb353beb1f0351ab2cba8909d875db23"), 241 witness: wire.TxWitness{ 242 mustParseShortForm("DUP HASH160 " + 243 "'17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'" + 244 " EQUALVERIFY CHECKSIG DATA_20 0x91"), 245 }, 246 }, 247 } 248 249 for _, test := range tests { 250 count := GetWitnessSigOpCount(test.sigScript, test.pkScript, 251 test.witness) 252 if count != test.numSigOps { 253 t.Errorf("%s: expected count of %d, got %d", test.name, 254 test.numSigOps, count) 255 256 } 257 } 258 } 259 260 // TestRemoveOpcodes ensures that removing opcodes from scripts behaves as 261 // expected. 262 func TestRemoveOpcodes(t *testing.T) { 263 t.Parallel() 264 265 tests := []struct { 266 name string 267 before string 268 remove byte 269 err error 270 after string 271 }{ 272 { 273 // Nothing to remove. 274 name: "nothing to remove", 275 before: "NOP", 276 remove: OP_CODESEPARATOR, 277 after: "NOP", 278 }, 279 { 280 // Test basic opcode removal. 281 name: "codeseparator 1", 282 before: "NOP CODESEPARATOR TRUE", 283 remove: OP_CODESEPARATOR, 284 after: "NOP TRUE", 285 }, 286 { 287 // The opcode in question is actually part of the data 288 // in a previous opcode. 289 name: "codeseparator by coincidence", 290 before: "NOP DATA_1 CODESEPARATOR TRUE", 291 remove: OP_CODESEPARATOR, 292 after: "NOP DATA_1 CODESEPARATOR TRUE", 293 }, 294 { 295 name: "invalid opcode", 296 before: "CAT", 297 remove: OP_CODESEPARATOR, 298 after: "CAT", 299 }, 300 { 301 name: "invalid length (instruction)", 302 before: "PUSHDATA1", 303 remove: OP_CODESEPARATOR, 304 err: scriptError(ErrMalformedPush, ""), 305 }, 306 { 307 name: "invalid length (data)", 308 before: "PUSHDATA1 0xff 0xfe", 309 remove: OP_CODESEPARATOR, 310 err: scriptError(ErrMalformedPush, ""), 311 }, 312 } 313 314 // tstRemoveOpcode is a convenience function to parse the provided 315 // raw script, remove the passed opcode, then unparse the result back 316 // into a raw script. 317 const scriptVersion = 0 318 tstRemoveOpcode := func(script []byte, opcode byte) ([]byte, error) { 319 if err := checkScriptParses(scriptVersion, script); err != nil { 320 return nil, err 321 } 322 return removeOpcodeRaw(script, opcode), nil 323 } 324 325 for _, test := range tests { 326 before := mustParseShortForm(test.before) 327 after := mustParseShortForm(test.after) 328 result, err := tstRemoveOpcode(before, test.remove) 329 if e := tstCheckScriptError(err, test.err); e != nil { 330 t.Errorf("%s: %v", test.name, e) 331 continue 332 } 333 334 if !bytes.Equal(after, result) { 335 t.Errorf("%s: value does not equal expected: exp: %q"+ 336 " got: %q", test.name, after, result) 337 } 338 } 339 } 340 341 // TestRemoveOpcodeByData ensures that removing data carrying opcodes based on 342 // the data they contain works as expected. 343 func TestRemoveOpcodeByData(t *testing.T) { 344 t.Parallel() 345 346 tests := []struct { 347 name string 348 before []byte 349 remove []byte 350 err error 351 after []byte 352 }{ 353 { 354 name: "nothing to do", 355 before: []byte{OP_NOP}, 356 remove: []byte{1, 2, 3, 4}, 357 after: []byte{OP_NOP}, 358 }, 359 { 360 name: "simple case", 361 before: []byte{OP_DATA_4, 1, 2, 3, 4}, 362 remove: []byte{1, 2, 3, 4}, 363 after: nil, 364 }, 365 { 366 name: "simple case (miss)", 367 before: []byte{OP_DATA_4, 1, 2, 3, 4}, 368 remove: []byte{1, 2, 3, 5}, 369 after: []byte{OP_DATA_4, 1, 2, 3, 4}, 370 }, 371 { 372 // padded to keep it canonical. 373 name: "simple case (pushdata1)", 374 before: append(append([]byte{OP_PUSHDATA1, 76}, 375 bytes.Repeat([]byte{0}, 72)...), 376 []byte{1, 2, 3, 4}...), 377 remove: []byte{1, 2, 3, 4}, 378 after: nil, 379 }, 380 { 381 name: "simple case (pushdata1 miss)", 382 before: append(append([]byte{OP_PUSHDATA1, 76}, 383 bytes.Repeat([]byte{0}, 72)...), 384 []byte{1, 2, 3, 4}...), 385 remove: []byte{1, 2, 3, 5}, 386 after: append(append([]byte{OP_PUSHDATA1, 76}, 387 bytes.Repeat([]byte{0}, 72)...), 388 []byte{1, 2, 3, 4}...), 389 }, 390 { 391 name: "simple case (pushdata1 miss noncanonical)", 392 before: []byte{OP_PUSHDATA1, 4, 1, 2, 3, 4}, 393 remove: []byte{1, 2, 3, 4}, 394 after: []byte{OP_PUSHDATA1, 4, 1, 2, 3, 4}, 395 }, 396 { 397 name: "simple case (pushdata2)", 398 before: append(append([]byte{OP_PUSHDATA2, 0, 1}, 399 bytes.Repeat([]byte{0}, 252)...), 400 []byte{1, 2, 3, 4}...), 401 remove: []byte{1, 2, 3, 4}, 402 after: nil, 403 }, 404 { 405 name: "simple case (pushdata2 miss)", 406 before: append(append([]byte{OP_PUSHDATA2, 0, 1}, 407 bytes.Repeat([]byte{0}, 252)...), 408 []byte{1, 2, 3, 4}...), 409 remove: []byte{1, 2, 3, 4, 5}, 410 after: append(append([]byte{OP_PUSHDATA2, 0, 1}, 411 bytes.Repeat([]byte{0}, 252)...), 412 []byte{1, 2, 3, 4}...), 413 }, 414 { 415 name: "simple case (pushdata2 miss noncanonical)", 416 before: []byte{OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, 417 remove: []byte{1, 2, 3, 4}, 418 after: []byte{OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, 419 }, 420 { 421 // This is padded to make the push canonical. 422 name: "simple case (pushdata4)", 423 before: append(append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, 424 bytes.Repeat([]byte{0}, 65532)...), 425 []byte{1, 2, 3, 4}...), 426 remove: []byte{1, 2, 3, 4}, 427 after: nil, 428 }, 429 { 430 name: "simple case (pushdata4 miss noncanonical)", 431 before: []byte{OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, 432 remove: []byte{1, 2, 3, 4}, 433 after: []byte{OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, 434 }, 435 { 436 // This is padded to make the push canonical. 437 name: "simple case (pushdata4 miss)", 438 before: append(append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, 439 bytes.Repeat([]byte{0}, 65532)...), []byte{1, 2, 3, 4}...), 440 remove: []byte{1, 2, 3, 4, 5}, 441 after: append(append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, 442 bytes.Repeat([]byte{0}, 65532)...), []byte{1, 2, 3, 4}...), 443 }, 444 { 445 name: "invalid opcode ", 446 before: []byte{OP_UNKNOWN187}, 447 remove: []byte{1, 2, 3, 4}, 448 after: []byte{OP_UNKNOWN187}, 449 }, 450 { 451 name: "invalid length (instruction)", 452 before: []byte{OP_PUSHDATA1}, 453 remove: []byte{1, 2, 3, 4}, 454 err: scriptError(ErrMalformedPush, ""), 455 }, 456 { 457 name: "invalid length (data)", 458 before: []byte{OP_PUSHDATA1, 255, 254}, 459 remove: []byte{1, 2, 3, 4}, 460 err: scriptError(ErrMalformedPush, ""), 461 }, 462 } 463 464 // tstRemoveOpcodeByData is a convenience function to ensure the provided 465 // script parses before attempting to remove the passed data. 466 const scriptVersion = 0 467 tstRemoveOpcodeByData := func(script []byte, data []byte) ([]byte, error) { 468 if err := checkScriptParses(scriptVersion, script); err != nil { 469 return nil, err 470 } 471 472 return removeOpcodeByData(script, data), nil 473 } 474 475 for _, test := range tests { 476 result, err := tstRemoveOpcodeByData(test.before, test.remove) 477 if e := tstCheckScriptError(err, test.err); e != nil { 478 t.Errorf("%s: %v", test.name, e) 479 continue 480 } 481 482 if !bytes.Equal(test.after, result) { 483 t.Errorf("%s: value does not equal expected: exp: %q"+ 484 " got: %q", test.name, test.after, result) 485 } 486 } 487 } 488 489 // TestIsPayToScriptHash ensures the IsPayToScriptHash function returns the 490 // expected results for all the scripts in scriptClassTests. 491 func TestIsPayToScriptHash(t *testing.T) { 492 t.Parallel() 493 494 for _, test := range scriptClassTests { 495 script := mustParseShortForm(test.script) 496 shouldBe := (test.class == ScriptHashTy) 497 p2sh := IsPayToScriptHash(script) 498 if p2sh != shouldBe { 499 t.Errorf("%s: expected p2sh %v, got %v", test.name, 500 shouldBe, p2sh) 501 } 502 } 503 } 504 505 // TestIsPayToWitnessScriptHash ensures the IsPayToWitnessScriptHash function 506 // returns the expected results for all the scripts in scriptClassTests. 507 func TestIsPayToWitnessScriptHash(t *testing.T) { 508 t.Parallel() 509 510 for _, test := range scriptClassTests { 511 script := mustParseShortForm(test.script) 512 shouldBe := (test.class == WitnessV0ScriptHashTy) 513 p2wsh := IsPayToWitnessScriptHash(script) 514 if p2wsh != shouldBe { 515 t.Errorf("%s: expected p2wsh %v, got %v", test.name, 516 shouldBe, p2wsh) 517 } 518 } 519 } 520 521 // TestIsPayToWitnessPubKeyHash ensures the IsPayToWitnessPubKeyHash function 522 // returns the expected results for all the scripts in scriptClassTests. 523 func TestIsPayToWitnessPubKeyHash(t *testing.T) { 524 t.Parallel() 525 526 for _, test := range scriptClassTests { 527 script := mustParseShortForm(test.script) 528 shouldBe := (test.class == WitnessV0PubKeyHashTy) 529 p2wkh := IsPayToWitnessPubKeyHash(script) 530 if p2wkh != shouldBe { 531 t.Errorf("%s: expected p2wkh %v, got %v", test.name, 532 shouldBe, p2wkh) 533 } 534 } 535 } 536 537 // TestHasCanonicalPushes ensures the isCanonicalPush function properly 538 // determines what is considered a canonical push for the purposes of 539 // removeOpcodeByData. 540 func TestHasCanonicalPushes(t *testing.T) { 541 t.Parallel() 542 543 const scriptVersion = 0 544 tests := []struct { 545 name string 546 script string 547 expected bool 548 }{ 549 { 550 name: "does not parse", 551 script: "0x046708afdb0fe5548271967f1a67130b7105cd6a82" + 552 "8e03909a67962e0ea1f61d", 553 expected: false, 554 }, 555 { 556 name: "non-canonical push", 557 script: "PUSHDATA1 0x04 0x01020304", 558 expected: false, 559 }, 560 } 561 562 for _, test := range tests { 563 script := mustParseShortForm(test.script) 564 if err := checkScriptParses(scriptVersion, script); err != nil { 565 if test.expected { 566 t.Errorf("%q: script parse failed: %v", test.name, err) 567 } 568 continue 569 } 570 tokenizer := MakeScriptTokenizer(scriptVersion, script) 571 for tokenizer.Next() { 572 result := isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) 573 if result != test.expected { 574 t.Errorf("%q: isCanonicalPush wrong result\ngot: %v\nwant: %v", 575 test.name, result, test.expected) 576 break 577 } 578 } 579 } 580 } 581 582 // TestIsPushOnlyScript ensures the IsPushOnlyScript function returns the 583 // expected results. 584 func TestIsPushOnlyScript(t *testing.T) { 585 t.Parallel() 586 587 test := struct { 588 name string 589 script []byte 590 expected bool 591 }{ 592 name: "does not parse", 593 script: mustParseShortForm("0x046708afdb0fe5548271967f1a67130" + 594 "b7105cd6a828e03909a67962e0ea1f61d"), 595 expected: false, 596 } 597 598 if IsPushOnlyScript(test.script) != test.expected { 599 t.Errorf("IsPushOnlyScript (%s) wrong result\ngot: %v\nwant: "+ 600 "%v", test.name, true, test.expected) 601 } 602 } 603 604 // TestIsUnspendable ensures the IsUnspendable function returns the expected 605 // results. 606 func TestIsUnspendable(t *testing.T) { 607 t.Parallel() 608 609 tests := []struct { 610 name string 611 pkScript []byte 612 expected bool 613 }{ 614 { 615 // Unspendable 616 pkScript: []byte{0x6a, 0x04, 0x74, 0x65, 0x73, 0x74}, 617 expected: true, 618 }, 619 { 620 // Spendable 621 pkScript: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0, 622 0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45, 623 0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6, 624 0xfa, 0x0b, 0x5c, 0x88, 0xac}, 625 expected: false, 626 }, 627 { 628 // Spendable 629 pkScript: []byte{0xa9, 0x14, 0x82, 0x1d, 0xba, 0x94, 0xbc, 0xfb, 630 0xa2, 0x57, 0x36, 0xa3, 0x9e, 0x5d, 0x14, 0x5d, 0x69, 0x75, 631 0xba, 0x8c, 0x0b, 0x42, 0x87}, 632 expected: false, 633 }, 634 { 635 // Not Necessarily Unspendable 636 pkScript: []byte{}, 637 expected: false, 638 }, 639 { 640 // Spendable 641 pkScript: []byte{OP_TRUE}, 642 expected: false, 643 }, 644 { 645 // Unspendable 646 pkScript: []byte{OP_RETURN}, 647 expected: true, 648 }, 649 } 650 651 for i, test := range tests { 652 res := IsUnspendable(test.pkScript) 653 if res != test.expected { 654 t.Errorf("TestIsUnspendable #%d failed: got %v want %v", 655 i, res, test.expected) 656 continue 657 } 658 } 659 }