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