github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/validation/tx_test.go (about) 1 package validation 2 3 import ( 4 "math" 5 "testing" 6 7 "github.com/davecgh/go-spew/spew" 8 9 "github.com/bytom/bytom/consensus" 10 "github.com/bytom/bytom/crypto/sha3pool" 11 "github.com/bytom/bytom/errors" 12 "github.com/bytom/bytom/protocol/bc" 13 "github.com/bytom/bytom/protocol/bc/types" 14 "github.com/bytom/bytom/protocol/vm" 15 "github.com/bytom/bytom/protocol/vm/vmutil" 16 "github.com/bytom/bytom/testutil" 17 ) 18 19 func init() { 20 spew.Config.DisableMethods = true 21 } 22 23 func TestGasStatus(t *testing.T) { 24 cases := []struct { 25 input *GasState 26 output *GasState 27 f func(*GasState) error 28 err error 29 }{ 30 { 31 input: &GasState{ 32 GasLeft: 10000, 33 GasUsed: 0, 34 BTMValue: 0, 35 }, 36 output: &GasState{ 37 GasLeft: 10000 / consensus.VMGasRate, 38 GasUsed: 0, 39 BTMValue: 10000, 40 }, 41 f: func(input *GasState) error { 42 return input.setGas(10000, 0) 43 }, 44 err: nil, 45 }, 46 { 47 input: &GasState{ 48 GasLeft: 10000, 49 GasUsed: 0, 50 BTMValue: 0, 51 }, 52 output: &GasState{ 53 GasLeft: 10000, 54 GasUsed: 0, 55 BTMValue: 0, 56 }, 57 f: func(input *GasState) error { 58 return input.setGas(-10000, 0) 59 }, 60 err: ErrGasCalculate, 61 }, 62 { 63 input: &GasState{ 64 GasLeft: consensus.DefaultGasCredit, 65 GasUsed: 0, 66 BTMValue: 0, 67 }, 68 output: &GasState{ 69 GasLeft: 200000, 70 GasUsed: 0, 71 BTMValue: 80000000000, 72 }, 73 f: func(input *GasState) error { 74 return input.setGas(80000000000, 0) 75 }, 76 err: nil, 77 }, 78 { 79 input: &GasState{ 80 GasLeft: consensus.DefaultGasCredit, 81 GasUsed: 0, 82 BTMValue: 0, 83 }, 84 output: &GasState{ 85 GasLeft: 200000, 86 GasUsed: 0, 87 BTMValue: math.MaxInt64, 88 }, 89 f: func(input *GasState) error { 90 return input.setGas(math.MaxInt64, 0) 91 }, 92 err: nil, 93 }, 94 { 95 input: &GasState{ 96 GasLeft: 10000, 97 GasUsed: 0, 98 BTMValue: 0, 99 }, 100 output: &GasState{ 101 GasLeft: 10000, 102 GasUsed: 0, 103 BTMValue: 0, 104 }, 105 f: func(input *GasState) error { 106 return input.updateUsage(-1) 107 }, 108 err: ErrGasCalculate, 109 }, 110 { 111 input: &GasState{ 112 GasLeft: 10000, 113 GasUsed: 0, 114 BTMValue: 0, 115 }, 116 output: &GasState{ 117 GasLeft: 9999, 118 GasUsed: 1, 119 BTMValue: 0, 120 }, 121 f: func(input *GasState) error { 122 return input.updateUsage(9999) 123 }, 124 err: nil, 125 }, 126 { 127 input: &GasState{ 128 GasLeft: -10000, 129 GasUsed: 0, 130 BTMValue: 0, 131 }, 132 output: &GasState{ 133 GasLeft: -10000, 134 GasUsed: 0, 135 BTMValue: 0, 136 }, 137 f: func(input *GasState) error { 138 return input.updateUsage(math.MaxInt64) 139 }, 140 err: ErrGasCalculate, 141 }, 142 { 143 input: &GasState{ 144 GasLeft: 1000, 145 GasUsed: 10, 146 StorageGas: 1000, 147 GasValid: false, 148 }, 149 output: &GasState{ 150 GasLeft: 0, 151 GasUsed: 1010, 152 StorageGas: 1000, 153 GasValid: true, 154 }, 155 f: func(input *GasState) error { 156 return input.setGasValid() 157 }, 158 err: nil, 159 }, 160 { 161 input: &GasState{ 162 GasLeft: 900, 163 GasUsed: 10, 164 StorageGas: 1000, 165 GasValid: false, 166 }, 167 output: &GasState{ 168 GasLeft: -100, 169 GasUsed: 10, 170 StorageGas: 1000, 171 GasValid: false, 172 }, 173 f: func(input *GasState) error { 174 return input.setGasValid() 175 }, 176 err: ErrGasCalculate, 177 }, 178 { 179 input: &GasState{ 180 GasLeft: 1000, 181 GasUsed: math.MaxInt64, 182 StorageGas: 1000, 183 GasValid: false, 184 }, 185 output: &GasState{ 186 GasLeft: 0, 187 GasUsed: 0, 188 StorageGas: 1000, 189 GasValid: false, 190 }, 191 f: func(input *GasState) error { 192 return input.setGasValid() 193 }, 194 err: ErrGasCalculate, 195 }, 196 { 197 input: &GasState{ 198 GasLeft: math.MinInt64, 199 GasUsed: 0, 200 StorageGas: 1000, 201 GasValid: false, 202 }, 203 output: &GasState{ 204 GasLeft: 0, 205 GasUsed: 0, 206 StorageGas: 1000, 207 GasValid: false, 208 }, 209 f: func(input *GasState) error { 210 return input.setGasValid() 211 }, 212 err: ErrGasCalculate, 213 }, 214 } 215 216 for i, c := range cases { 217 err := c.f(c.input) 218 219 if rootErr(err) != c.err { 220 t.Errorf("case %d: got error %s, want %s", i, err, c.err) 221 } else if *c.input != *c.output { 222 t.Errorf("case %d: gasStatus %v, want %v;", i, c.input, c.output) 223 } 224 } 225 } 226 227 func TestOverflow(t *testing.T) { 228 sourceID := &bc.Hash{V0: 9999} 229 ctrlProgram := []byte{byte(vm.OP_TRUE)} 230 newTx := func(inputs []uint64, outputs []uint64) *bc.Tx { 231 txInputs := make([]*types.TxInput, 0, len(inputs)) 232 txOutputs := make([]*types.TxOutput, 0, len(outputs)) 233 234 for _, amount := range inputs { 235 txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram) 236 txInputs = append(txInputs, txInput) 237 } 238 239 for _, amount := range outputs { 240 txOutput := types.NewTxOutput(*consensus.BTMAssetID, amount, ctrlProgram) 241 txOutputs = append(txOutputs, txOutput) 242 } 243 244 txData := &types.TxData{ 245 Version: 1, 246 SerializedSize: 100, 247 TimeRange: 0, 248 Inputs: txInputs, 249 Outputs: txOutputs, 250 } 251 return types.MapTx(txData) 252 } 253 254 cases := []struct { 255 inputs []uint64 256 outputs []uint64 257 err error 258 }{ 259 { 260 inputs: []uint64{math.MaxUint64, 1}, 261 outputs: []uint64{0}, 262 err: ErrOverflow, 263 }, 264 { 265 inputs: []uint64{math.MaxUint64, math.MaxUint64}, 266 outputs: []uint64{0}, 267 err: ErrOverflow, 268 }, 269 { 270 inputs: []uint64{math.MaxUint64, math.MaxUint64 - 1}, 271 outputs: []uint64{0}, 272 err: ErrOverflow, 273 }, 274 { 275 inputs: []uint64{math.MaxInt64, 1}, 276 outputs: []uint64{0}, 277 err: ErrOverflow, 278 }, 279 { 280 inputs: []uint64{math.MaxInt64, math.MaxInt64}, 281 outputs: []uint64{0}, 282 err: ErrOverflow, 283 }, 284 { 285 inputs: []uint64{math.MaxInt64, math.MaxInt64 - 1}, 286 outputs: []uint64{0}, 287 err: ErrOverflow, 288 }, 289 { 290 inputs: []uint64{0}, 291 outputs: []uint64{math.MaxUint64}, 292 err: ErrOverflow, 293 }, 294 { 295 inputs: []uint64{0}, 296 outputs: []uint64{math.MaxInt64}, 297 err: ErrGasCalculate, 298 }, 299 { 300 inputs: []uint64{math.MaxInt64 - 1}, 301 outputs: []uint64{math.MaxInt64}, 302 err: ErrGasCalculate, 303 }, 304 } 305 306 for i, c := range cases { 307 tx := newTx(c.inputs, c.outputs) 308 if _, err := ValidateTx(tx, mockBlock()); rootErr(err) != c.err { 309 t.Fatalf("case %d test failed, want %s, have %s", i, c.err, rootErr(err)) 310 } 311 } 312 } 313 314 func TestTxValidation(t *testing.T) { 315 var ( 316 tx *bc.Tx 317 vs *validationState 318 fixture *txFixture 319 320 // the mux from tx, pulled out for convenience 321 mux *bc.Mux 322 ) 323 324 addCoinbase := func(assetID *bc.AssetID, amount uint64, arbitrary []byte) { 325 coinbase := bc.NewCoinbase(arbitrary) 326 txOutput := types.NewTxOutput(*assetID, amount, []byte{byte(vm.OP_TRUE)}) 327 muxID := getMuxID(tx) 328 coinbase.SetDestination(muxID, &txOutput.AssetAmount, uint64(len(mux.Sources))) 329 coinbaseID := bc.EntryID(coinbase) 330 tx.Entries[coinbaseID] = coinbase 331 332 mux.Sources = append(mux.Sources, &bc.ValueSource{ 333 Ref: &coinbaseID, 334 Value: &txOutput.AssetAmount, 335 }) 336 337 src := &bc.ValueSource{ 338 Ref: muxID, 339 Value: &txOutput.AssetAmount, 340 Position: uint64(len(tx.ResultIds)), 341 } 342 prog := &bc.Program{txOutput.VMVersion, txOutput.ControlProgram} 343 output := bc.NewOutput(src, prog, uint64(len(tx.ResultIds))) 344 outputID := bc.EntryID(output) 345 tx.Entries[outputID] = output 346 347 dest := &bc.ValueDestination{ 348 Value: src.Value, 349 Ref: &outputID, 350 Position: 0, 351 } 352 mux.WitnessDestinations = append(mux.WitnessDestinations, dest) 353 tx.ResultIds = append(tx.ResultIds, &outputID) 354 vs.block.Transactions = append(vs.block.Transactions, vs.tx) 355 } 356 357 cases := []struct { 358 desc string // description of the test case 359 f func() // function to adjust tx, vs, and/or mux 360 err error // expected error 361 }{ 362 { 363 desc: "base case", 364 }, 365 { 366 desc: "unbalanced mux amounts", 367 f: func() { 368 mux.Sources[0].Value.Amount++ 369 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance) 370 iss.WitnessDestination.Value.Amount++ 371 }, 372 err: ErrUnbalanced, 373 }, 374 { 375 desc: "unbalanced mux amounts", 376 f: func() { 377 mux.WitnessDestinations[0].Value.Amount++ 378 }, 379 err: ErrUnbalanced, 380 }, 381 { 382 desc: "balanced mux amounts", 383 f: func() { 384 mux.Sources[1].Value.Amount++ 385 mux.WitnessDestinations[0].Value.Amount++ 386 }, 387 err: nil, 388 }, 389 { 390 desc: "overflowing mux source amounts", 391 f: func() { 392 mux.Sources[0].Value.Amount = math.MaxInt64 393 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance) 394 iss.WitnessDestination.Value.Amount = math.MaxInt64 395 }, 396 err: ErrOverflow, 397 }, 398 { 399 desc: "underflowing mux destination amounts", 400 f: func() { 401 mux.WitnessDestinations[0].Value.Amount = math.MaxInt64 402 out := tx.Entries[*mux.WitnessDestinations[0].Ref].(*bc.Output) 403 out.Source.Value.Amount = math.MaxInt64 404 mux.WitnessDestinations[1].Value.Amount = math.MaxInt64 405 out = tx.Entries[*mux.WitnessDestinations[1].Ref].(*bc.Output) 406 out.Source.Value.Amount = math.MaxInt64 407 }, 408 err: ErrOverflow, 409 }, 410 { 411 desc: "unbalanced mux assets", 412 f: func() { 413 mux.Sources[1].Value.AssetId = newAssetID(255) 414 sp := tx.Entries[*mux.Sources[1].Ref].(*bc.Spend) 415 sp.WitnessDestination.Value.AssetId = newAssetID(255) 416 }, 417 err: ErrUnbalanced, 418 }, 419 { 420 desc: "mismatched output source / mux dest position", 421 f: func() { 422 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1 423 }, 424 err: ErrMismatchedPosition, 425 }, 426 { 427 desc: "mismatched input dest / mux source position", 428 f: func() { 429 mux.Sources[0].Position = 1 430 }, 431 err: ErrMismatchedPosition, 432 }, 433 { 434 desc: "mismatched output source and mux dest", 435 f: func() { 436 // For this test, it's necessary to construct a mostly 437 // identical second transaction in order to get a similar but 438 // not equal output entry for the mux to falsely point 439 // to. That entry must be added to the first tx's Entries map. 440 fixture2 := sample(t, fixture) 441 tx2 := types.NewTx(*fixture2.tx).Tx 442 out2ID := tx2.ResultIds[0] 443 out2 := tx2.Entries[*out2ID].(*bc.Output) 444 tx.Entries[*out2ID] = out2 445 mux.WitnessDestinations[0].Ref = out2ID 446 }, 447 err: ErrMismatchedReference, 448 }, 449 { 450 desc: "mismatched input dest and mux source", 451 f: func() { 452 fixture2 := sample(t, fixture) 453 tx2 := types.NewTx(*fixture2.tx).Tx 454 input2ID := tx2.InputIDs[2] 455 input2 := tx2.Entries[input2ID].(*bc.Spend) 456 dest2Ref := input2.WitnessDestination.Ref 457 dest2 := tx2.Entries[*dest2Ref].(*bc.Mux) 458 tx.Entries[*dest2Ref] = dest2 459 tx.Entries[input2ID] = input2 460 mux.Sources[0].Ref = &input2ID 461 }, 462 err: ErrMismatchedReference, 463 }, 464 { 465 desc: "invalid mux destination position", 466 f: func() { 467 mux.WitnessDestinations[0].Position = 1 468 }, 469 err: ErrPosition, 470 }, 471 { 472 desc: "mismatched mux dest value / output source value", 473 f: func() { 474 outID := tx.ResultIds[0] 475 out := tx.Entries[*outID].(*bc.Output) 476 mux.WitnessDestinations[0].Value = &bc.AssetAmount{ 477 AssetId: out.Source.Value.AssetId, 478 Amount: out.Source.Value.Amount + 1, 479 } 480 mux.Sources[0].Value.Amount++ // the mux must still balance 481 }, 482 err: ErrMismatchedValue, 483 }, 484 { 485 desc: "empty tx results", 486 f: func() { 487 tx.ResultIds = nil 488 }, 489 err: ErrEmptyResults, 490 }, 491 { 492 desc: "empty tx results, but that's OK", 493 f: func() { 494 tx.Version = 2 495 tx.ResultIds = nil 496 }, 497 }, 498 { 499 desc: "issuance program failure", 500 f: func() { 501 iss := txIssuance(t, tx, 0) 502 iss.WitnessArguments[0] = []byte{} 503 }, 504 err: vm.ErrFalseVMResult, 505 }, 506 { 507 desc: "spend control program failure", 508 f: func() { 509 spend := txSpend(t, tx, 1) 510 spend.WitnessArguments[0] = []byte{} 511 }, 512 err: vm.ErrFalseVMResult, 513 }, 514 { 515 desc: "mismatched spent source/witness value", 516 f: func() { 517 spend := txSpend(t, tx, 1) 518 spentOutput := tx.Entries[*spend.SpentOutputId].(*bc.Output) 519 spentOutput.Source.Value = &bc.AssetAmount{ 520 AssetId: spend.WitnessDestination.Value.AssetId, 521 Amount: spend.WitnessDestination.Value.Amount + 1, 522 } 523 }, 524 err: ErrMismatchedValue, 525 }, 526 { 527 desc: "gas out of limit", 528 f: func() { 529 vs.tx.SerializedSize = 10000000 530 }, 531 err: ErrOverGasCredit, 532 }, 533 { 534 desc: "can't find gas spend input in entries", 535 f: func() { 536 spendID := mux.Sources[len(mux.Sources)-1].Ref 537 delete(tx.Entries, *spendID) 538 mux.Sources = mux.Sources[:len(mux.Sources)-1] 539 }, 540 err: bc.ErrMissingEntry, 541 }, 542 { 543 desc: "no gas spend input", 544 f: func() { 545 spendID := mux.Sources[len(mux.Sources)-1].Ref 546 delete(tx.Entries, *spendID) 547 mux.Sources = mux.Sources[:len(mux.Sources)-1] 548 tx.GasInputIDs = nil 549 vs.gasStatus.GasLeft = 0 550 }, 551 err: vm.ErrRunLimitExceeded, 552 }, 553 { 554 desc: "no gas spend input, but set gas left, so it's ok", 555 f: func() { 556 spendID := mux.Sources[len(mux.Sources)-1].Ref 557 delete(tx.Entries, *spendID) 558 mux.Sources = mux.Sources[:len(mux.Sources)-1] 559 tx.GasInputIDs = nil 560 }, 561 err: nil, 562 }, 563 { 564 desc: "mismatched gas spend input destination amount/prevout source amount", 565 f: func() { 566 spendID := mux.Sources[len(mux.Sources)-1].Ref 567 spend := tx.Entries[*spendID].(*bc.Spend) 568 spend.WitnessDestination.Value = &bc.AssetAmount{ 569 AssetId: spend.WitnessDestination.Value.AssetId, 570 Amount: spend.WitnessDestination.Value.Amount + 1, 571 } 572 }, 573 err: ErrMismatchedValue, 574 }, 575 { 576 desc: "mismatched witness asset destination", 577 f: func() { 578 issuanceID := mux.Sources[0].Ref 579 issuance := tx.Entries[*issuanceID].(*bc.Issuance) 580 issuance.WitnessAssetDefinition.Data = &bc.Hash{V0: 9999} 581 }, 582 err: ErrMismatchedAssetID, 583 }, 584 { 585 desc: "issuance witness position greater than length of mux sources", 586 f: func() { 587 issuanceID := mux.Sources[0].Ref 588 issuance := tx.Entries[*issuanceID].(*bc.Issuance) 589 issuance.WitnessDestination.Position = uint64(len(mux.Sources) + 1) 590 }, 591 err: ErrPosition, 592 }, 593 { 594 desc: "normal coinbase tx", 595 f: func() { 596 addCoinbase(consensus.BTMAssetID, 100000, nil) 597 }, 598 err: nil, 599 }, 600 { 601 desc: "invalid coinbase tx asset id", 602 f: func() { 603 addCoinbase(&bc.AssetID{V1: 100}, 100000, nil) 604 }, 605 err: ErrWrongCoinbaseAsset, 606 }, 607 { 608 desc: "coinbase tx is not first tx in block", 609 f: func() { 610 addCoinbase(consensus.BTMAssetID, 100000, nil) 611 vs.block.Transactions[0] = nil 612 }, 613 err: ErrWrongCoinbaseTransaction, 614 }, 615 { 616 desc: "coinbase arbitrary size out of limit", 617 f: func() { 618 arbitrary := make([]byte, consensus.CoinbaseArbitrarySizeLimit+1) 619 addCoinbase(consensus.BTMAssetID, 100000, arbitrary) 620 }, 621 err: ErrCoinbaseArbitraryOversize, 622 }, 623 { 624 desc: "normal retirement output", 625 f: func() { 626 outputID := tx.ResultIds[0] 627 output := tx.Entries[*outputID].(*bc.Output) 628 retirement := bc.NewRetirement(output.Source, output.Ordinal) 629 retirementID := bc.EntryID(retirement) 630 tx.Entries[retirementID] = retirement 631 delete(tx.Entries, *outputID) 632 tx.ResultIds[0] = &retirementID 633 mux.WitnessDestinations[0].Ref = &retirementID 634 }, 635 err: nil, 636 }, 637 { 638 desc: "ordinal doesn't matter for prevouts", 639 f: func() { 640 spend := txSpend(t, tx, 1) 641 prevout := tx.Entries[*spend.SpentOutputId].(*bc.Output) 642 newPrevout := bc.NewOutput(prevout.Source, prevout.ControlProgram, 10) 643 hash := bc.EntryID(newPrevout) 644 spend.SpentOutputId = &hash 645 }, 646 err: nil, 647 }, 648 { 649 desc: "mux witness destination have no source", 650 f: func() { 651 dest := &bc.ValueDestination{ 652 Value: &bc.AssetAmount{ 653 AssetId: &bc.AssetID{V2: 1000}, 654 Amount: 100, 655 }, 656 Ref: mux.WitnessDestinations[0].Ref, 657 Position: 0, 658 } 659 mux.WitnessDestinations = append(mux.WitnessDestinations, dest) 660 }, 661 err: ErrNoSource, 662 }, 663 } 664 665 for i, c := range cases { 666 t.Run(c.desc, func(t *testing.T) { 667 fixture = sample(t, nil) 668 tx = types.NewTx(*fixture.tx).Tx 669 vs = &validationState{ 670 block: mockBlock(), 671 tx: tx, 672 entryID: tx.ID, 673 gasStatus: &GasState{ 674 GasLeft: int64(80000), 675 GasUsed: 0, 676 }, 677 cache: make(map[bc.Hash]error), 678 } 679 muxID := getMuxID(tx) 680 mux = tx.Entries[*muxID].(*bc.Mux) 681 682 if c.f != nil { 683 c.f() 684 } 685 err := checkValid(vs, tx.TxHeader) 686 687 if rootErr(err) != c.err { 688 t.Errorf("case #%d (%s) got error %s, want %s; validationState is:\n%s", i, c.desc, err, c.err, spew.Sdump(vs)) 689 } 690 }) 691 } 692 } 693 694 // TestCoinbase test the coinbase transaction is valid (txtest#1016) 695 func TestCoinbase(t *testing.T) { 696 cp, _ := vmutil.DefaultCoinbaseProgram() 697 retire, _ := vmutil.RetireProgram([]byte{}) 698 CbTx := types.MapTx(&types.TxData{ 699 SerializedSize: 1, 700 Inputs: []*types.TxInput{ 701 types.NewCoinbaseInput(nil), 702 }, 703 Outputs: []*types.TxOutput{ 704 types.NewTxOutput(*consensus.BTMAssetID, 888, cp), 705 }, 706 }) 707 708 cases := []struct { 709 block *bc.Block 710 txIndex int 711 GasValid bool 712 err error 713 }{ 714 { 715 block: &bc.Block{ 716 BlockHeader: &bc.BlockHeader{Height: 666}, 717 Transactions: []*bc.Tx{CbTx}, 718 }, 719 txIndex: 0, 720 GasValid: true, 721 err: nil, 722 }, 723 { 724 block: &bc.Block{ 725 BlockHeader: &bc.BlockHeader{Height: 666}, 726 Transactions: []*bc.Tx{ 727 CbTx, 728 types.MapTx(&types.TxData{ 729 SerializedSize: 1, 730 Inputs: []*types.TxInput{ 731 types.NewCoinbaseInput(nil), 732 }, 733 Outputs: []*types.TxOutput{ 734 types.NewTxOutput(*consensus.BTMAssetID, 888, cp), 735 }, 736 }), 737 }, 738 }, 739 txIndex: 1, 740 GasValid: false, 741 err: ErrWrongCoinbaseTransaction, 742 }, 743 { 744 block: &bc.Block{ 745 BlockHeader: &bc.BlockHeader{Height: 666}, 746 Transactions: []*bc.Tx{ 747 CbTx, 748 types.MapTx(&types.TxData{ 749 SerializedSize: 1, 750 Inputs: []*types.TxInput{ 751 types.NewCoinbaseInput(nil), 752 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp), 753 }, 754 Outputs: []*types.TxOutput{ 755 types.NewTxOutput(*consensus.BTMAssetID, 888, cp), 756 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp), 757 }, 758 }), 759 }, 760 }, 761 txIndex: 1, 762 GasValid: false, 763 err: ErrWrongCoinbaseTransaction, 764 }, 765 { 766 block: &bc.Block{ 767 BlockHeader: &bc.BlockHeader{Height: 666}, 768 Transactions: []*bc.Tx{ 769 CbTx, 770 types.MapTx(&types.TxData{ 771 SerializedSize: 1, 772 Inputs: []*types.TxInput{ 773 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp), 774 types.NewCoinbaseInput(nil), 775 }, 776 Outputs: []*types.TxOutput{ 777 types.NewTxOutput(*consensus.BTMAssetID, 888, cp), 778 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp), 779 }, 780 }), 781 }, 782 }, 783 txIndex: 1, 784 GasValid: false, 785 err: ErrWrongCoinbaseTransaction, 786 }, 787 { 788 block: &bc.Block{ 789 BlockHeader: &bc.BlockHeader{Height: 666}, 790 Transactions: []*bc.Tx{ 791 types.MapTx(&types.TxData{ 792 SerializedSize: 1, 793 Inputs: []*types.TxInput{ 794 types.NewCoinbaseInput(nil), 795 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp), 796 }, 797 Outputs: []*types.TxOutput{ 798 types.NewTxOutput(*consensus.BTMAssetID, 888, cp), 799 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp), 800 }, 801 }), 802 }, 803 }, 804 txIndex: 0, 805 GasValid: true, 806 err: nil, 807 }, 808 { 809 block: &bc.Block{ 810 BlockHeader: &bc.BlockHeader{Height: 666}, 811 Transactions: []*bc.Tx{ 812 types.MapTx(&types.TxData{ 813 SerializedSize: 1, 814 Inputs: []*types.TxInput{ 815 types.NewCoinbaseInput(nil), 816 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, retire), 817 }, 818 Outputs: []*types.TxOutput{ 819 types.NewTxOutput(*consensus.BTMAssetID, 888, cp), 820 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp), 821 }, 822 }), 823 }, 824 }, 825 txIndex: 0, 826 GasValid: false, 827 err: vm.ErrReturn, 828 }, 829 } 830 831 for i, c := range cases { 832 gasStatus, err := ValidateTx(c.block.Transactions[c.txIndex], c.block) 833 834 if rootErr(err) != c.err { 835 t.Errorf("#%d got error %s, want %s", i, err, c.err) 836 } 837 if c.GasValid != gasStatus.GasValid { 838 t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid) 839 } 840 } 841 } 842 843 func TestRuleAA(t *testing.T) { 844 testData := "070100040161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f63024062c29b20941e7f762c3afae232f61d8dac1c544825931e391408c6715c408ef69f494a1b3b61ce380ddee0c8b18ecac2b46ef96a62eebb6ec40f9f545410870a200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f630240e443d66c75b4d5fa71676d60b0b067e6941f06349f31e5f73a7d51a73f5797632b2e01e8584cd1c8730dc16df075866b0c796bd7870182e2da4b37188208fe02200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce99402013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08ba3fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08fe0fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700" 845 tx := types.Tx{} 846 if err := tx.UnmarshalText([]byte(testData)); err != nil { 847 t.Errorf("fail on unmarshal txData: %s", err) 848 } 849 850 cases := []struct { 851 block *bc.Block 852 GasValid bool 853 err error 854 }{ 855 { 856 block: &bc.Block{ 857 BlockHeader: &bc.BlockHeader{ 858 Height: ruleAA - 1, 859 }, 860 }, 861 GasValid: true, 862 err: ErrMismatchedPosition, 863 }, 864 { 865 block: &bc.Block{ 866 BlockHeader: &bc.BlockHeader{ 867 Height: ruleAA, 868 }, 869 }, 870 GasValid: false, 871 err: ErrEmptyInputIDs, 872 }, 873 } 874 875 for i, c := range cases { 876 gasStatus, err := ValidateTx(tx.Tx, c.block) 877 if rootErr(err) != c.err { 878 t.Errorf("#%d got error %s, want %s", i, err, c.err) 879 } 880 if c.GasValid != gasStatus.GasValid { 881 t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid) 882 } 883 } 884 885 } 886 887 // TestTimeRange test the checkTimeRange function (txtest#1004) 888 func TestTimeRange(t *testing.T) { 889 cases := []struct { 890 timeRange uint64 891 err bool 892 }{ 893 { 894 timeRange: 0, 895 err: false, 896 }, 897 { 898 timeRange: 334, 899 err: false, 900 }, 901 { 902 timeRange: 332, 903 err: true, 904 }, 905 { 906 timeRange: 1521625824, 907 err: false, 908 }, 909 } 910 911 block := &bc.Block{ 912 BlockHeader: &bc.BlockHeader{ 913 Height: 333, 914 Timestamp: 1521625823, 915 }, 916 } 917 918 tx := types.MapTx(&types.TxData{ 919 SerializedSize: 1, 920 TimeRange: 0, 921 Inputs: []*types.TxInput{ 922 mockGasTxInput(), 923 }, 924 Outputs: []*types.TxOutput{ 925 types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}), 926 }, 927 }) 928 929 for i, c := range cases { 930 tx.TimeRange = c.timeRange 931 if _, err := ValidateTx(tx, block); (err != nil) != c.err { 932 t.Errorf("#%d got error %t, want %t", i, !c.err, c.err) 933 } 934 } 935 } 936 937 func TestStandardTx(t *testing.T) { 938 fixture := sample(t, nil) 939 tx := types.NewTx(*fixture.tx).Tx 940 941 cases := []struct { 942 desc string 943 f func() 944 err error 945 }{ 946 { 947 desc: "normal standard tx", 948 err: nil, 949 }, 950 { 951 desc: "not standard tx in spend input", 952 f: func() { 953 inputID := tx.GasInputIDs[0] 954 spend := tx.Entries[inputID].(*bc.Spend) 955 spentOutput, err := tx.Output(*spend.SpentOutputId) 956 if err != nil { 957 t.Fatal(err) 958 } 959 spentOutput.ControlProgram = &bc.Program{Code: []byte{0}} 960 }, 961 err: ErrNotStandardTx, 962 }, 963 { 964 desc: "not standard tx in output", 965 f: func() { 966 outputID := tx.ResultIds[0] 967 output := tx.Entries[*outputID].(*bc.Output) 968 output.ControlProgram = &bc.Program{Code: []byte{0}} 969 }, 970 err: ErrNotStandardTx, 971 }, 972 } 973 974 for i, c := range cases { 975 if c.f != nil { 976 c.f() 977 } 978 if err := checkStandardTx(tx, 0); err != c.err { 979 t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, err, c.err) 980 } 981 } 982 } 983 984 func TestValidateTxVersion(t *testing.T) { 985 cases := []struct { 986 desc string 987 block *bc.Block 988 err error 989 }{ 990 { 991 desc: "tx version greater than 1 (txtest#1001)", 992 block: &bc.Block{ 993 BlockHeader: &bc.BlockHeader{Version: 1}, 994 Transactions: []*bc.Tx{ 995 {TxHeader: &bc.TxHeader{Version: 2}}, 996 }, 997 }, 998 err: ErrTxVersion, 999 }, 1000 { 1001 desc: "tx version equals 0 (txtest#1002)", 1002 block: &bc.Block{ 1003 BlockHeader: &bc.BlockHeader{Version: 1}, 1004 Transactions: []*bc.Tx{ 1005 {TxHeader: &bc.TxHeader{Version: 0}}, 1006 }, 1007 }, 1008 err: ErrTxVersion, 1009 }, 1010 { 1011 desc: "tx version equals max uint64 (txtest#1003)", 1012 block: &bc.Block{ 1013 BlockHeader: &bc.BlockHeader{Version: 1}, 1014 Transactions: []*bc.Tx{ 1015 {TxHeader: &bc.TxHeader{Version: math.MaxUint64}}, 1016 }, 1017 }, 1018 err: ErrTxVersion, 1019 }, 1020 } 1021 1022 for i, c := range cases { 1023 if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err { 1024 t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, err, c.err) 1025 } 1026 } 1027 } 1028 1029 // A txFixture is returned by sample (below) to produce a sample 1030 // transaction, which takes a separate, optional _input_ txFixture to 1031 // affect the transaction that's built. The components of the 1032 // transaction are the fields of txFixture. 1033 type txFixture struct { 1034 initialBlockID bc.Hash 1035 issuanceProg bc.Program 1036 issuanceArgs [][]byte 1037 assetDef []byte 1038 assetID bc.AssetID 1039 txVersion uint64 1040 txInputs []*types.TxInput 1041 txOutputs []*types.TxOutput 1042 tx *types.TxData 1043 } 1044 1045 // Produces a sample transaction in a txFixture object (see above). A 1046 // separate input txFixture can be used to alter the transaction 1047 // that's created. 1048 // 1049 // The output of this function can be used as the input to a 1050 // subsequent call to make iterative refinements to a test object. 1051 // 1052 // The default transaction produced is valid and has three inputs: 1053 // - an issuance of 10 units 1054 // - a spend of 20 units 1055 // - a spend of 40 units 1056 // and two outputs, one of 25 units and one of 45 units. 1057 // All amounts are denominated in the same asset. 1058 // 1059 // The issuance program for the asset requires two numbers as 1060 // arguments that add up to 5. The prevout control programs require 1061 // two numbers each, adding to 9 and 13, respectively. 1062 // 1063 // The min and max times for the transaction are now +/- one minute. 1064 func sample(tb testing.TB, in *txFixture) *txFixture { 1065 var result txFixture 1066 if in != nil { 1067 result = *in 1068 } 1069 1070 if result.initialBlockID.IsZero() { 1071 result.initialBlockID = *newHash(1) 1072 } 1073 if testutil.DeepEqual(result.issuanceProg, bc.Program{}) { 1074 prog, err := vm.Assemble("ADD 5 NUMEQUAL") 1075 if err != nil { 1076 tb.Fatal(err) 1077 } 1078 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog} 1079 } 1080 if len(result.issuanceArgs) == 0 { 1081 result.issuanceArgs = [][]byte{{2}, {3}} 1082 } 1083 if len(result.assetDef) == 0 { 1084 result.assetDef = []byte{2} 1085 } 1086 if result.assetID.IsZero() { 1087 refdatahash := hashData(result.assetDef) 1088 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, result.issuanceProg.VmVersion, &refdatahash) 1089 } 1090 1091 if result.txVersion == 0 { 1092 result.txVersion = 1 1093 } 1094 if len(result.txInputs) == 0 { 1095 cp1, err := vm.Assemble("ADD 9 NUMEQUAL") 1096 if err != nil { 1097 tb.Fatal(err) 1098 } 1099 args1 := [][]byte{{4}, {5}} 1100 1101 cp2, err := vm.Assemble("ADD 13 NUMEQUAL") 1102 if err != nil { 1103 tb.Fatal(err) 1104 } 1105 args2 := [][]byte{{6}, {7}} 1106 1107 result.txInputs = []*types.TxInput{ 1108 types.NewIssuanceInput([]byte{3}, 10, result.issuanceProg.Code, result.issuanceArgs, result.assetDef), 1109 types.NewSpendInput(args1, *newHash(5), result.assetID, 20, 0, cp1), 1110 types.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2), 1111 } 1112 } 1113 1114 result.txInputs = append(result.txInputs, mockGasTxInput()) 1115 1116 if len(result.txOutputs) == 0 { 1117 cp1, err := vm.Assemble("ADD 17 NUMEQUAL") 1118 if err != nil { 1119 tb.Fatal(err) 1120 } 1121 cp2, err := vm.Assemble("ADD 21 NUMEQUAL") 1122 if err != nil { 1123 tb.Fatal(err) 1124 } 1125 1126 result.txOutputs = []*types.TxOutput{ 1127 types.NewTxOutput(result.assetID, 25, cp1), 1128 types.NewTxOutput(result.assetID, 45, cp2), 1129 } 1130 } 1131 1132 result.tx = &types.TxData{ 1133 Version: result.txVersion, 1134 Inputs: result.txInputs, 1135 Outputs: result.txOutputs, 1136 } 1137 1138 return &result 1139 } 1140 1141 func mockBlock() *bc.Block { 1142 return &bc.Block{ 1143 BlockHeader: &bc.BlockHeader{ 1144 Height: 666, 1145 }, 1146 } 1147 } 1148 1149 func mockGasTxInput() *types.TxInput { 1150 cp, _ := vmutil.DefaultCoinbaseProgram() 1151 return types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp) 1152 } 1153 1154 // Like errors.Root, but also unwraps vm.Error objects. 1155 func rootErr(e error) error { 1156 return errors.Root(e) 1157 } 1158 1159 func hashData(data []byte) bc.Hash { 1160 var b32 [32]byte 1161 sha3pool.Sum256(b32[:], data) 1162 return bc.NewHash(b32) 1163 } 1164 1165 func newHash(n byte) *bc.Hash { 1166 h := bc.NewHash([32]byte{n}) 1167 return &h 1168 } 1169 1170 func newAssetID(n byte) *bc.AssetID { 1171 a := bc.NewAssetID([32]byte{n}) 1172 return &a 1173 } 1174 1175 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance { 1176 id := tx.InputIDs[index] 1177 res, err := tx.Issuance(id) 1178 if err != nil { 1179 t.Fatal(err) 1180 } 1181 return res 1182 } 1183 1184 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend { 1185 id := tx.InputIDs[index] 1186 res, err := tx.Spend(id) 1187 if err != nil { 1188 t.Fatal(err) 1189 } 1190 return res 1191 } 1192 1193 func getMuxID(tx *bc.Tx) *bc.Hash { 1194 out := tx.Entries[*tx.ResultIds[0]] 1195 switch result := out.(type) { 1196 case *bc.Output: 1197 return result.Source.Ref 1198 case *bc.Retirement: 1199 return result.Source.Ref 1200 } 1201 return nil 1202 }