7 "github.com/davecgh/go-spew/spew"
9 "github.com/vapor/consensus"
10 "github.com/vapor/crypto/sha3pool"
11 "github.com/vapor/errors"
12 "github.com/vapor/protocol/bc"
13 "github.com/vapor/protocol/bc/types"
14 "github.com/vapor/protocol/vm"
15 "github.com/vapor/protocol/vm/vmutil"
16 "github.com/vapor/testutil"
20 spew.Config.DisableMethods = true
23 func TestGasStatus(t *testing.T) {
27 f func(*GasState) error
37 GasLeft: 10000 / consensus.VMGasRate,
41 f: func(input *GasState) error {
42 return input.setGas(10000, 0)
57 f: func(input *GasState) error {
58 return input.setGas(-10000, 0)
64 GasLeft: consensus.DefaultGasCredit,
71 BTMValue: 80000000000,
73 f: func(input *GasState) error {
74 return input.setGas(80000000000, 0)
80 GasLeft: consensus.DefaultGasCredit,
87 BTMValue: math.MaxInt64,
89 f: func(input *GasState) error {
90 return input.setGas(math.MaxInt64, 0)
105 f: func(input *GasState) error {
106 return input.updateUsage(-1)
108 err: ErrGasCalculate,
121 f: func(input *GasState) error {
122 return input.updateUsage(9999)
137 f: func(input *GasState) error {
138 return input.updateUsage(math.MaxInt64)
140 err: ErrGasCalculate,
155 f: func(input *GasState) error {
156 return input.setGasValid()
173 f: func(input *GasState) error {
174 return input.setGasValid()
176 err: ErrGasCalculate,
181 GasUsed: math.MaxInt64,
191 f: func(input *GasState) error {
192 return input.setGasValid()
194 err: ErrGasCalculate,
198 GasLeft: math.MinInt64,
209 f: func(input *GasState) error {
210 return input.setGasValid()
212 err: ErrGasCalculate,
216 for i, c := range cases {
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)
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))
234 for _, amount := range inputs {
235 txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram)
236 txInputs = append(txInputs, txInput)
239 for _, amount := range outputs {
240 txOutput := types.NewTxOutput(*consensus.BTMAssetID, amount, ctrlProgram)
241 txOutputs = append(txOutputs, txOutput)
244 txData := &types.TxData{
251 return types.MapTx(txData)
260 inputs: []uint64{math.MaxUint64, 1},
261 outputs: []uint64{0},
265 inputs: []uint64{math.MaxUint64, math.MaxUint64},
266 outputs: []uint64{0},
270 inputs: []uint64{math.MaxUint64, math.MaxUint64 - 1},
271 outputs: []uint64{0},
275 inputs: []uint64{math.MaxInt64, 1},
276 outputs: []uint64{0},
280 inputs: []uint64{math.MaxInt64, math.MaxInt64},
281 outputs: []uint64{0},
285 inputs: []uint64{math.MaxInt64, math.MaxInt64 - 1},
286 outputs: []uint64{0},
291 outputs: []uint64{math.MaxUint64},
296 outputs: []uint64{math.MaxInt64},
297 err: ErrGasCalculate,
300 inputs: []uint64{math.MaxInt64 - 1},
301 outputs: []uint64{math.MaxInt64},
302 err: ErrGasCalculate,
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))
314 func TestTxValidation(t *testing.T) {
320 // the mux from tx, pulled out for convenience
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
332 mux.Sources = append(mux.Sources, &bc.ValueSource{
334 Value: &txOutput.AssetAmount,
337 src := &bc.ValueSource{
339 Value: &txOutput.AssetAmount,
340 Position: uint64(len(tx.ResultIds)),
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
347 dest := &bc.ValueDestination{
352 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
353 tx.ResultIds = append(tx.ResultIds, &outputID)
354 vs.block.Transactions = append(vs.block.Transactions, vs.tx)
358 desc string // description of the test case
359 f func() // function to adjust tx, vs, and/or mux
360 err error // expected error
366 desc: "unbalanced mux amounts",
368 mux.Sources[0].Value.Amount++
369 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
370 iss.WitnessDestination.Value.Amount++
375 desc: "unbalanced mux amounts",
377 mux.WitnessDestinations[0].Value.Amount++
382 desc: "balanced mux amounts",
384 mux.Sources[1].Value.Amount++
385 mux.WitnessDestinations[0].Value.Amount++
390 desc: "overflowing mux source amounts",
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
399 desc: "underflowing mux destination amounts",
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
411 desc: "unbalanced mux assets",
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)
420 desc: "mismatched output source / mux dest position",
422 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
424 err: ErrMismatchedPosition,
427 desc: "mismatched input dest / mux source position",
429 mux.Sources[0].Position = 1
431 err: ErrMismatchedPosition,
434 desc: "mismatched output source and mux dest",
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
447 err: ErrMismatchedReference,
450 desc: "mismatched input dest and mux source",
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
462 err: ErrMismatchedReference,
465 desc: "invalid mux destination position",
467 mux.WitnessDestinations[0].Position = 1
472 desc: "mismatched mux dest value / output source value",
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,
480 mux.Sources[0].Value.Amount++ // the mux must still balance
482 err: ErrMismatchedValue,
485 desc: "empty tx results",
489 err: ErrEmptyResults,
492 desc: "empty tx results, but that's OK",
499 desc: "issuance program failure",
501 iss := txIssuance(t, tx, 0)
502 iss.WitnessArguments[0] = []byte{}
504 err: vm.ErrFalseVMResult,
507 desc: "spend control program failure",
509 spend := txSpend(t, tx, 1)
510 spend.WitnessArguments[0] = []byte{}
512 err: vm.ErrFalseVMResult,
515 desc: "mismatched spent source/witness value",
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,
524 err: ErrMismatchedValue,
527 desc: "gas out of limit",
529 vs.tx.SerializedSize = 10000000
531 err: ErrOverGasCredit,
534 desc: "can't find gas spend input in entries",
536 spendID := mux.Sources[len(mux.Sources)-1].Ref
537 delete(tx.Entries, *spendID)
538 mux.Sources = mux.Sources[:len(mux.Sources)-1]
540 err: bc.ErrMissingEntry,
543 desc: "no gas spend input",
545 spendID := mux.Sources[len(mux.Sources)-1].Ref
546 delete(tx.Entries, *spendID)
547 mux.Sources = mux.Sources[:len(mux.Sources)-1]
549 vs.gasStatus.GasLeft = 0
551 err: vm.ErrRunLimitExceeded,
554 desc: "no gas spend input, but set gas left, so it's ok",
556 spendID := mux.Sources[len(mux.Sources)-1].Ref
557 delete(tx.Entries, *spendID)
558 mux.Sources = mux.Sources[:len(mux.Sources)-1]
564 desc: "mismatched gas spend input destination amount/prevout source amount",
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,
573 err: ErrMismatchedValue,
576 desc: "mismatched witness asset destination",
578 issuanceID := mux.Sources[0].Ref
579 issuance := tx.Entries[*issuanceID].(*bc.Issuance)
580 issuance.WitnessAssetDefinition.Data = &bc.Hash{V0: 9999}
582 err: ErrMismatchedAssetID,
585 desc: "issuance witness position greater than length of mux sources",
587 issuanceID := mux.Sources[0].Ref
588 issuance := tx.Entries[*issuanceID].(*bc.Issuance)
589 issuance.WitnessDestination.Position = uint64(len(mux.Sources) + 1)
594 desc: "normal coinbase tx",
596 addCoinbase(consensus.BTMAssetID, 100000, nil)
601 desc: "invalid coinbase tx asset id",
603 addCoinbase(&bc.AssetID{V1: 100}, 100000, nil)
605 err: ErrWrongCoinbaseAsset,
608 desc: "coinbase tx is not first tx in block",
610 addCoinbase(consensus.BTMAssetID, 100000, nil)
611 vs.block.Transactions[0] = nil
613 err: ErrWrongCoinbaseTransaction,
616 desc: "coinbase arbitrary size out of limit",
618 arbitrary := make([]byte, consensus.CoinbaseArbitrarySizeLimit+1)
619 addCoinbase(consensus.BTMAssetID, 100000, arbitrary)
621 err: ErrCoinbaseArbitraryOversize,
624 desc: "normal retirement output",
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
638 desc: "ordinal doesn't matter for prevouts",
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
649 desc: "mux witness destination have no source",
651 dest := &bc.ValueDestination{
652 Value: &bc.AssetAmount{
653 AssetId: &bc.AssetID{V2: 1000},
656 Ref: mux.WitnessDestinations[0].Ref,
659 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
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{
673 gasStatus: &GasState{
674 GasLeft: int64(80000),
677 cache: make(map[bc.Hash]error),
679 muxID := getMuxID(tx)
680 mux = tx.Entries[*muxID].(*bc.Mux)
685 err := checkValid(vs, tx.TxHeader)
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))
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{
700 Inputs: []*types.TxInput{
701 types.NewCoinbaseInput(nil),
703 Outputs: []*types.TxOutput{
704 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
716 BlockHeader: &bc.BlockHeader{Height: 666},
717 Transactions: []*bc.Tx{CbTx},
725 BlockHeader: &bc.BlockHeader{Height: 666},
726 Transactions: []*bc.Tx{
728 types.MapTx(&types.TxData{
730 Inputs: []*types.TxInput{
731 types.NewCoinbaseInput(nil),
733 Outputs: []*types.TxOutput{
734 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
741 err: ErrWrongCoinbaseTransaction,
745 BlockHeader: &bc.BlockHeader{Height: 666},
746 Transactions: []*bc.Tx{
748 types.MapTx(&types.TxData{
750 Inputs: []*types.TxInput{
751 types.NewCoinbaseInput(nil),
752 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
754 Outputs: []*types.TxOutput{
755 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
756 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
763 err: ErrWrongCoinbaseTransaction,
767 BlockHeader: &bc.BlockHeader{Height: 666},
768 Transactions: []*bc.Tx{
770 types.MapTx(&types.TxData{
772 Inputs: []*types.TxInput{
773 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
774 types.NewCoinbaseInput(nil),
776 Outputs: []*types.TxOutput{
777 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
778 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
785 err: ErrWrongCoinbaseTransaction,
789 BlockHeader: &bc.BlockHeader{Height: 666},
790 Transactions: []*bc.Tx{
791 types.MapTx(&types.TxData{
793 Inputs: []*types.TxInput{
794 types.NewCoinbaseInput(nil),
795 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
797 Outputs: []*types.TxOutput{
798 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
799 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
810 BlockHeader: &bc.BlockHeader{Height: 666},
811 Transactions: []*bc.Tx{
812 types.MapTx(&types.TxData{
814 Inputs: []*types.TxInput{
815 types.NewCoinbaseInput(nil),
816 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, retire),
818 Outputs: []*types.TxOutput{
819 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
820 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
831 for i, c := range cases {
832 gasStatus, err := ValidateTx(c.block.Transactions[c.txIndex], c.block)
834 if rootErr(err) != c.err {
835 t.Errorf("#%d got error %s, want %s", i, err, c.err)
837 if c.GasValid != gasStatus.GasValid {
838 t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid)
843 func TestRuleAA(t *testing.T) {
844 testData := "070100040161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f63024062c29b20941e7f762c3afae232f61d8dac1c544825931e391408c6715c408ef69f494a1b3b61ce380ddee0c8b18ecac2b46ef96a62eebb6ec40f9f545410870a200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f630240e443d66c75b4d5fa71676d60b0b067e6941f06349f31e5f73a7d51a73f5797632b2e01e8584cd1c8730dc16df075866b0c796bd7870182e2da4b37188208fe02200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce99402013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08ba3fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08fe0fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700"
846 if err := tx.UnmarshalText([]byte(testData)); err != nil {
847 t.Errorf("fail on unmarshal txData: %s", err)
857 BlockHeader: &bc.BlockHeader{
862 err: ErrMismatchedPosition,
866 BlockHeader: &bc.BlockHeader{
871 err: ErrEmptyInputIDs,
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)
880 if c.GasValid != gasStatus.GasValid {
881 t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid)
887 // TestTimeRange test the checkTimeRange function (txtest#1004)
888 func TestTimeRange(t *testing.T) {
906 timeRange: 1521625824,
912 BlockHeader: &bc.BlockHeader{
914 Timestamp: 1521625823,
918 tx := types.MapTx(&types.TxData{
921 Inputs: []*types.TxInput{
924 Outputs: []*types.TxOutput{
925 types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}),
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)
937 func TestStandardTx(t *testing.T) {
938 fixture := sample(t, nil)
939 tx := types.NewTx(*fixture.tx).Tx
947 desc: "normal standard tx",
951 desc: "not standard tx in spend input",
953 inputID := tx.GasInputIDs[0]
954 spend := tx.Entries[inputID].(*bc.Spend)
955 spentOutput, err := tx.Output(*spend.SpentOutputId)
959 spentOutput.ControlProgram = &bc.Program{Code: []byte{0}}
961 err: ErrNotStandardTx,
964 desc: "not standard tx in output",
966 outputID := tx.ResultIds[0]
967 output := tx.Entries[*outputID].(*bc.Output)
968 output.ControlProgram = &bc.Program{Code: []byte{0}}
970 err: ErrNotStandardTx,
974 for i, c := range cases {
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)
984 func TestValidateTxVersion(t *testing.T) {
991 desc: "tx version greater than 1 (txtest#1001)",
993 BlockHeader: &bc.BlockHeader{Version: 1},
994 Transactions: []*bc.Tx{
995 {TxHeader: &bc.TxHeader{Version: 2}},
1001 desc: "tx version equals 0 (txtest#1002)",
1003 BlockHeader: &bc.BlockHeader{Version: 1},
1004 Transactions: []*bc.Tx{
1005 {TxHeader: &bc.TxHeader{Version: 0}},
1011 desc: "tx version equals max uint64 (txtest#1003)",
1013 BlockHeader: &bc.BlockHeader{Version: 1},
1014 Transactions: []*bc.Tx{
1015 {TxHeader: &bc.TxHeader{Version: math.MaxUint64}},
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)
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
1040 txInputs []*types.TxInput
1041 txOutputs []*types.TxOutput
1045 // Produces a sample transaction in a txFixture object (see above). A
1046 // separate input txFixture can be used to alter the transaction
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.
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.
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.
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
1070 if result.initialBlockID.IsZero() {
1071 result.initialBlockID = *newHash(1)
1073 if testutil.DeepEqual(result.issuanceProg, bc.Program{}) {
1074 prog, err := vm.Assemble("ADD 5 NUMEQUAL")
1078 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog}
1080 if len(result.issuanceArgs) == 0 {
1081 result.issuanceArgs = [][]byte{{2}, {3}}
1083 if len(result.assetDef) == 0 {
1084 result.assetDef = []byte{2}
1086 if result.assetID.IsZero() {
1087 refdatahash := hashData(result.assetDef)
1088 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, result.issuanceProg.VmVersion, &refdatahash)
1091 if result.txVersion == 0 {
1092 result.txVersion = 1
1094 if len(result.txInputs) == 0 {
1095 cp1, err := vm.Assemble("ADD 9 NUMEQUAL")
1099 args1 := [][]byte{{4}, {5}}
1101 cp2, err := vm.Assemble("ADD 13 NUMEQUAL")
1105 args2 := [][]byte{{6}, {7}}
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),
1114 result.txInputs = append(result.txInputs, mockGasTxInput())
1116 if len(result.txOutputs) == 0 {
1117 cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
1121 cp2, err := vm.Assemble("ADD 21 NUMEQUAL")
1126 result.txOutputs = []*types.TxOutput{
1127 types.NewTxOutput(result.assetID, 25, cp1),
1128 types.NewTxOutput(result.assetID, 45, cp2),
1132 result.tx = &types.TxData{
1133 Version: result.txVersion,
1134 Inputs: result.txInputs,
1135 Outputs: result.txOutputs,
1141 func mockBlock() *bc.Block {
1143 BlockHeader: &bc.BlockHeader{
1149 func mockGasTxInput() *types.TxInput {
1150 cp, _ := vmutil.DefaultCoinbaseProgram()
1151 return types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)
1154 // Like errors.Root, but also unwraps vm.Error objects.
1155 func rootErr(e error) error {
1156 return errors.Root(e)
1159 func hashData(data []byte) bc.Hash {
1161 sha3pool.Sum256(b32[:], data)
1162 return bc.NewHash(b32)
1165 func newHash(n byte) *bc.Hash {
1166 h := bc.NewHash([32]byte{n})
1170 func newAssetID(n byte) *bc.AssetID {
1171 a := bc.NewAssetID([32]byte{n})
1175 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance {
1176 id := tx.InputIDs[index]
1177 res, err := tx.Issuance(id)
1184 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend {
1185 id := tx.InputIDs[index]
1186 res, err := tx.Spend(id)
1193 func getMuxID(tx *bc.Tx) *bc.Hash {
1194 out := tx.Entries[*tx.ResultIds[0]]
1195 switch result := out.(type) {
1197 return result.Source.Ref
1198 case *bc.Retirement:
1199 return result.Source.Ref