7 "github.com/davecgh/go-spew/spew"
9 "github.com/bytom/consensus"
10 "github.com/bytom/crypto/sha3pool"
11 "github.com/bytom/errors"
12 "github.com/bytom/protocol/bc"
13 "github.com/bytom/protocol/bc/types"
14 "github.com/bytom/protocol/vm"
15 "github.com/bytom/protocol/vm/vmutil"
16 "github.com/bytom/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)
89 f: func(input *GasState) error {
90 return input.updateUsage(-1)
105 f: func(input *GasState) error {
106 return input.updateUsage(9999)
123 f: func(input *GasState) error {
124 return input.setGasValid()
141 f: func(input *GasState) error {
142 return input.setGasValid()
144 err: ErrGasCalculate,
149 GasUsed: math.MaxInt64,
159 f: func(input *GasState) error {
160 return input.setGasValid()
162 err: ErrGasCalculate,
166 GasLeft: math.MinInt64,
177 f: func(input *GasState) error {
178 return input.setGasValid()
180 err: ErrGasCalculate,
184 for i, c := range cases {
187 if rootErr(err) != c.err {
188 t.Errorf("case %d: got error %s, want %s", i, err, c.err)
189 } else if *c.input != *c.output {
190 t.Errorf("case %d: gasStatus %v, want %v;", i, c.input, c.output)
195 func TestOverflow(t *testing.T) {
196 sourceID := &bc.Hash{V0: 9999}
197 ctrlProgram := []byte{byte(vm.OP_TRUE)}
198 newTx := func(inputs []uint64, outputs []uint64) *bc.Tx {
199 txInputs := make([]*types.TxInput, 0, len(inputs))
200 txOutputs := make([]*types.TxOutput, 0, len(outputs))
202 for _, amount := range inputs {
203 txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram)
204 txInputs = append(txInputs, txInput)
207 for _, amount := range outputs {
208 txOutput := types.NewTxOutput(*consensus.BTMAssetID, amount, ctrlProgram)
209 txOutputs = append(txOutputs, txOutput)
212 txData := &types.TxData{
219 return types.MapTx(txData)
228 inputs: []uint64{math.MaxUint64, 1},
229 outputs: []uint64{0},
233 inputs: []uint64{math.MaxUint64, math.MaxUint64},
234 outputs: []uint64{0},
238 inputs: []uint64{math.MaxUint64, math.MaxUint64 - 1},
239 outputs: []uint64{0},
243 inputs: []uint64{math.MaxInt64, 1},
244 outputs: []uint64{0},
248 inputs: []uint64{math.MaxInt64, math.MaxInt64},
249 outputs: []uint64{0},
253 inputs: []uint64{math.MaxInt64, math.MaxInt64 - 1},
254 outputs: []uint64{0},
259 outputs: []uint64{math.MaxUint64},
264 outputs: []uint64{math.MaxInt64},
265 err: ErrGasCalculate,
268 inputs: []uint64{math.MaxInt64 - 1},
269 outputs: []uint64{math.MaxInt64},
270 err: ErrGasCalculate,
274 for i, c := range cases {
275 tx := newTx(c.inputs, c.outputs)
276 if _, err := ValidateTx(tx, mockBlock()); rootErr(err) != c.err {
277 t.Fatalf("case %d test failed, want %s, have %s", i, c.err, rootErr(err))
282 func TestTxValidation(t *testing.T) {
288 // the mux from tx, pulled out for convenience
292 addCoinbase := func(assetID *bc.AssetID, amount uint64, arbitrary []byte) {
293 coinbase := bc.NewCoinbase(arbitrary)
294 txOutput := types.NewTxOutput(*assetID, amount, []byte{byte(vm.OP_TRUE)})
295 muxID := getMuxID(tx)
296 coinbase.SetDestination(muxID, &txOutput.AssetAmount, uint64(len(mux.Sources)))
297 coinbaseID := bc.EntryID(coinbase)
298 tx.Entries[coinbaseID] = coinbase
300 mux.Sources = append(mux.Sources, &bc.ValueSource{
302 Value: &txOutput.AssetAmount,
305 src := &bc.ValueSource{
307 Value: &txOutput.AssetAmount,
308 Position: uint64(len(tx.ResultIds)),
310 prog := &bc.Program{txOutput.VMVersion, txOutput.ControlProgram}
311 output := bc.NewOutput(src, prog, uint64(len(tx.ResultIds)))
312 outputID := bc.EntryID(output)
313 tx.Entries[outputID] = output
315 dest := &bc.ValueDestination{
320 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
321 tx.ResultIds = append(tx.ResultIds, &outputID)
322 vs.block.Transactions = append(vs.block.Transactions, vs.tx)
326 desc string // description of the test case
327 f func() // function to adjust tx, vs, and/or mux
328 err error // expected error
334 desc: "unbalanced mux amounts",
336 mux.Sources[0].Value.Amount++
337 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
338 iss.WitnessDestination.Value.Amount++
343 desc: "unbalanced mux amounts",
345 mux.WitnessDestinations[0].Value.Amount++
350 desc: "balanced mux amounts",
352 mux.Sources[1].Value.Amount++
353 mux.WitnessDestinations[0].Value.Amount++
358 desc: "overflowing mux source amounts",
360 mux.Sources[0].Value.Amount = math.MaxInt64
361 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
362 iss.WitnessDestination.Value.Amount = math.MaxInt64
367 desc: "underflowing mux destination amounts",
369 mux.WitnessDestinations[0].Value.Amount = math.MaxInt64
370 out := tx.Entries[*mux.WitnessDestinations[0].Ref].(*bc.Output)
371 out.Source.Value.Amount = math.MaxInt64
372 mux.WitnessDestinations[1].Value.Amount = math.MaxInt64
373 out = tx.Entries[*mux.WitnessDestinations[1].Ref].(*bc.Output)
374 out.Source.Value.Amount = math.MaxInt64
379 desc: "unbalanced mux assets",
381 mux.Sources[1].Value.AssetId = newAssetID(255)
382 sp := tx.Entries[*mux.Sources[1].Ref].(*bc.Spend)
383 sp.WitnessDestination.Value.AssetId = newAssetID(255)
388 desc: "mismatched output source / mux dest position",
390 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
392 err: ErrMismatchedPosition,
395 desc: "mismatched input dest / mux source position",
397 mux.Sources[0].Position = 1
399 err: ErrMismatchedPosition,
402 desc: "mismatched output source and mux dest",
404 // For this test, it's necessary to construct a mostly
405 // identical second transaction in order to get a similar but
406 // not equal output entry for the mux to falsely point
407 // to. That entry must be added to the first tx's Entries map.
408 fixture2 := sample(t, fixture)
409 tx2 := types.NewTx(*fixture2.tx).Tx
410 out2ID := tx2.ResultIds[0]
411 out2 := tx2.Entries[*out2ID].(*bc.Output)
412 tx.Entries[*out2ID] = out2
413 mux.WitnessDestinations[0].Ref = out2ID
415 err: ErrMismatchedReference,
418 desc: "mismatched input dest and mux source",
420 fixture2 := sample(t, fixture)
421 tx2 := types.NewTx(*fixture2.tx).Tx
422 input2ID := tx2.InputIDs[2]
423 input2 := tx2.Entries[input2ID].(*bc.Spend)
424 dest2Ref := input2.WitnessDestination.Ref
425 dest2 := tx2.Entries[*dest2Ref].(*bc.Mux)
426 tx.Entries[*dest2Ref] = dest2
427 tx.Entries[input2ID] = input2
428 mux.Sources[0].Ref = &input2ID
430 err: ErrMismatchedReference,
433 desc: "invalid mux destination position",
435 mux.WitnessDestinations[0].Position = 1
440 desc: "mismatched mux dest value / output source value",
442 outID := tx.ResultIds[0]
443 out := tx.Entries[*outID].(*bc.Output)
444 mux.WitnessDestinations[0].Value = &bc.AssetAmount{
445 AssetId: out.Source.Value.AssetId,
446 Amount: out.Source.Value.Amount + 1,
448 mux.Sources[0].Value.Amount++ // the mux must still balance
450 err: ErrMismatchedValue,
453 desc: "empty tx results",
457 err: ErrEmptyResults,
460 desc: "empty tx results, but that's OK",
467 desc: "issuance program failure",
469 iss := txIssuance(t, tx, 0)
470 iss.WitnessArguments[0] = []byte{}
472 err: vm.ErrFalseVMResult,
475 desc: "spend control program failure",
477 spend := txSpend(t, tx, 1)
478 spend.WitnessArguments[0] = []byte{}
480 err: vm.ErrFalseVMResult,
483 desc: "mismatched spent source/witness value",
485 spend := txSpend(t, tx, 1)
486 spentOutput := tx.Entries[*spend.SpentOutputId].(*bc.Output)
487 spentOutput.Source.Value = &bc.AssetAmount{
488 AssetId: spend.WitnessDestination.Value.AssetId,
489 Amount: spend.WitnessDestination.Value.Amount + 1,
492 err: ErrMismatchedValue,
495 desc: "gas out of limit",
497 vs.tx.SerializedSize = 10000000
499 err: ErrOverGasCredit,
502 desc: "can't find gas spend input in entries",
504 spendID := mux.Sources[len(mux.Sources)-1].Ref
505 delete(tx.Entries, *spendID)
506 mux.Sources = mux.Sources[:len(mux.Sources)-1]
508 err: bc.ErrMissingEntry,
511 desc: "no gas spend input",
513 spendID := mux.Sources[len(mux.Sources)-1].Ref
514 delete(tx.Entries, *spendID)
515 mux.Sources = mux.Sources[:len(mux.Sources)-1]
517 vs.gasStatus.GasLeft = 0
519 err: vm.ErrRunLimitExceeded,
522 desc: "no gas spend input, but set gas left, so it's ok",
524 spendID := mux.Sources[len(mux.Sources)-1].Ref
525 delete(tx.Entries, *spendID)
526 mux.Sources = mux.Sources[:len(mux.Sources)-1]
532 desc: "mismatched gas spend input destination amount/prevout source amount",
534 spendID := mux.Sources[len(mux.Sources)-1].Ref
535 spend := tx.Entries[*spendID].(*bc.Spend)
536 spend.WitnessDestination.Value = &bc.AssetAmount{
537 AssetId: spend.WitnessDestination.Value.AssetId,
538 Amount: spend.WitnessDestination.Value.Amount + 1,
541 err: ErrMismatchedValue,
544 desc: "mismatched witness asset destination",
546 issuanceID := mux.Sources[0].Ref
547 issuance := tx.Entries[*issuanceID].(*bc.Issuance)
548 issuance.WitnessAssetDefinition.Data = &bc.Hash{V0: 9999}
550 err: ErrMismatchedAssetID,
553 desc: "issuance witness position greater than length of mux sources",
555 issuanceID := mux.Sources[0].Ref
556 issuance := tx.Entries[*issuanceID].(*bc.Issuance)
557 issuance.WitnessDestination.Position = uint64(len(mux.Sources) + 1)
562 desc: "normal coinbase tx",
564 addCoinbase(consensus.BTMAssetID, 100000, nil)
569 desc: "invalid coinbase tx asset id",
571 addCoinbase(&bc.AssetID{V1: 100}, 100000, nil)
573 err: ErrWrongCoinbaseAsset,
576 desc: "coinbase tx is not first tx in block",
578 addCoinbase(consensus.BTMAssetID, 100000, nil)
579 vs.block.Transactions[0] = nil
581 err: ErrWrongCoinbaseTransaction,
584 desc: "coinbase arbitrary size out of limit",
586 arbitrary := make([]byte, consensus.CoinbaseArbitrarySizeLimit+1)
587 addCoinbase(consensus.BTMAssetID, 100000, arbitrary)
589 err: ErrCoinbaseArbitraryOversize,
592 desc: "normal retirement output",
594 outputID := tx.ResultIds[0]
595 output := tx.Entries[*outputID].(*bc.Output)
596 retirement := bc.NewRetirement(output.Source, output.Ordinal)
597 retirementID := bc.EntryID(retirement)
598 tx.Entries[retirementID] = retirement
599 delete(tx.Entries, *outputID)
600 tx.ResultIds[0] = &retirementID
601 mux.WitnessDestinations[0].Ref = &retirementID
606 desc: "ordinal doesn't matter for prevouts",
608 spend := txSpend(t, tx, 1)
609 prevout := tx.Entries[*spend.SpentOutputId].(*bc.Output)
610 newPrevout := bc.NewOutput(prevout.Source, prevout.ControlProgram, 10)
611 hash := bc.EntryID(newPrevout)
612 spend.SpentOutputId = &hash
617 desc: "mux witness destination have no source",
619 dest := &bc.ValueDestination{
620 Value: &bc.AssetAmount{
621 AssetId: &bc.AssetID{V2: 1000},
624 Ref: mux.WitnessDestinations[0].Ref,
627 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
633 for i, c := range cases {
634 t.Run(c.desc, func(t *testing.T) {
635 fixture = sample(t, nil)
636 tx = types.NewTx(*fixture.tx).Tx
637 vs = &validationState{
641 gasStatus: &GasState{
642 GasLeft: int64(80000),
645 cache: make(map[bc.Hash]error),
647 muxID := getMuxID(tx)
648 mux = tx.Entries[*muxID].(*bc.Mux)
653 err := checkValid(vs, tx.TxHeader)
655 if rootErr(err) != c.err {
656 t.Errorf("case #%d (%s) got error %s, want %s; validationState is:\n%s", i, c.desc, err, c.err, spew.Sdump(vs))
662 // TestCoinbase test the coinbase transaction is valid (txtest#1016)
663 func TestCoinbase(t *testing.T) {
664 cp, _ := vmutil.DefaultCoinbaseProgram()
665 retire, _ := vmutil.RetireProgram([]byte{})
666 CbTx := types.MapTx(&types.TxData{
668 Inputs: []*types.TxInput{
669 types.NewCoinbaseInput(nil),
671 Outputs: []*types.TxOutput{
672 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
684 BlockHeader: &bc.BlockHeader{Height: 666},
685 Transactions: []*bc.Tx{CbTx},
693 BlockHeader: &bc.BlockHeader{Height: 666},
694 Transactions: []*bc.Tx{
696 types.MapTx(&types.TxData{
698 Inputs: []*types.TxInput{
699 types.NewCoinbaseInput(nil),
701 Outputs: []*types.TxOutput{
702 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
709 err: ErrWrongCoinbaseTransaction,
713 BlockHeader: &bc.BlockHeader{Height: 666},
714 Transactions: []*bc.Tx{
716 types.MapTx(&types.TxData{
718 Inputs: []*types.TxInput{
719 types.NewCoinbaseInput(nil),
720 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
722 Outputs: []*types.TxOutput{
723 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
724 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
731 err: ErrWrongCoinbaseTransaction,
735 BlockHeader: &bc.BlockHeader{Height: 666},
736 Transactions: []*bc.Tx{
738 types.MapTx(&types.TxData{
740 Inputs: []*types.TxInput{
741 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
742 types.NewCoinbaseInput(nil),
744 Outputs: []*types.TxOutput{
745 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
746 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
753 err: ErrWrongCoinbaseTransaction,
757 BlockHeader: &bc.BlockHeader{Height: 666},
758 Transactions: []*bc.Tx{
759 types.MapTx(&types.TxData{
761 Inputs: []*types.TxInput{
762 types.NewCoinbaseInput(nil),
763 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
765 Outputs: []*types.TxOutput{
766 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
767 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
778 BlockHeader: &bc.BlockHeader{Height: 666},
779 Transactions: []*bc.Tx{
780 types.MapTx(&types.TxData{
782 Inputs: []*types.TxInput{
783 types.NewCoinbaseInput(nil),
784 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, retire),
786 Outputs: []*types.TxOutput{
787 types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
788 types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
799 for i, c := range cases {
800 gasStatus, err := ValidateTx(c.block.Transactions[c.txIndex], c.block)
802 if rootErr(err) != c.err {
803 t.Errorf("#%d got error %s, want %s", i, err, c.err)
805 if c.GasValid != gasStatus.GasValid {
806 t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid)
811 func TestRuleAA(t *testing.T) {
812 testData := "070100040161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f63024062c29b20941e7f762c3afae232f61d8dac1c544825931e391408c6715c408ef69f494a1b3b61ce380ddee0c8b18ecac2b46ef96a62eebb6ec40f9f545410870a200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f630240e443d66c75b4d5fa71676d60b0b067e6941f06349f31e5f73a7d51a73f5797632b2e01e8584cd1c8730dc16df075866b0c796bd7870182e2da4b37188208fe02200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce99402013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08ba3fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08fe0fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700"
814 if err := tx.UnmarshalText([]byte(testData)); err != nil {
815 t.Errorf("fail on unmarshal txData: %s", err)
825 BlockHeader: &bc.BlockHeader{
830 err: ErrMismatchedPosition,
834 BlockHeader: &bc.BlockHeader{
839 err: ErrEmptyInputIDs,
843 for i, c := range cases {
844 gasStatus, err := ValidateTx(tx.Tx, c.block)
845 if rootErr(err) != c.err {
846 t.Errorf("#%d got error %s, want %s", i, err, c.err)
848 if c.GasValid != gasStatus.GasValid {
849 t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid)
855 // TestTimeRange test the checkTimeRange function (txtest#1004)
856 func TestTimeRange(t *testing.T) {
874 timeRange: 1521625824,
880 BlockHeader: &bc.BlockHeader{
882 Timestamp: 1521625823,
886 tx := types.MapTx(&types.TxData{
889 Inputs: []*types.TxInput{
892 Outputs: []*types.TxOutput{
893 types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}),
897 for i, c := range cases {
898 tx.TimeRange = c.timeRange
899 if _, err := ValidateTx(tx, block); (err != nil) != c.err {
900 t.Errorf("#%d got error %t, want %t", i, !c.err, c.err)
905 func TestStandardTx(t *testing.T) {
906 fixture := sample(t, nil)
907 tx := types.NewTx(*fixture.tx).Tx
915 desc: "normal standard tx",
919 desc: "not standard tx in spend input",
921 inputID := tx.GasInputIDs[0]
922 spend := tx.Entries[inputID].(*bc.Spend)
923 spentOutput, err := tx.Output(*spend.SpentOutputId)
927 spentOutput.ControlProgram = &bc.Program{Code: []byte{0}}
929 err: ErrNotStandardTx,
932 desc: "not standard tx in output",
934 outputID := tx.ResultIds[0]
935 output := tx.Entries[*outputID].(*bc.Output)
936 output.ControlProgram = &bc.Program{Code: []byte{0}}
938 err: ErrNotStandardTx,
942 for i, c := range cases {
946 if err := checkStandardTx(tx, 0); err != c.err {
947 t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, err, c.err)
952 func TestValidateTxVersion(t *testing.T) {
959 desc: "tx version greater than 1 (txtest#1001)",
961 BlockHeader: &bc.BlockHeader{Version: 1},
962 Transactions: []*bc.Tx{
963 &bc.Tx{TxHeader: &bc.TxHeader{Version: 2}},
969 desc: "tx version equals 0 (txtest#1002)",
971 BlockHeader: &bc.BlockHeader{Version: 1},
972 Transactions: []*bc.Tx{
973 &bc.Tx{TxHeader: &bc.TxHeader{Version: 0}},
979 desc: "tx version equals max uint64 (txtest#1003)",
981 BlockHeader: &bc.BlockHeader{Version: 1},
982 Transactions: []*bc.Tx{
983 &bc.Tx{TxHeader: &bc.TxHeader{Version: math.MaxUint64}},
990 for i, c := range cases {
991 if _, err := ValidateTx(c.block.Transactions[0], c.block); rootErr(err) != c.err {
992 t.Errorf("case #%d (%s) got error %t, want %t", i, c.desc, err, c.err)
997 // A txFixture is returned by sample (below) to produce a sample
998 // transaction, which takes a separate, optional _input_ txFixture to
999 // affect the transaction that's built. The components of the
1000 // transaction are the fields of txFixture.
1001 type txFixture struct {
1002 initialBlockID bc.Hash
1003 issuanceProg bc.Program
1004 issuanceArgs [][]byte
1008 txInputs []*types.TxInput
1009 txOutputs []*types.TxOutput
1013 // Produces a sample transaction in a txFixture object (see above). A
1014 // separate input txFixture can be used to alter the transaction
1017 // The output of this function can be used as the input to a
1018 // subsequent call to make iterative refinements to a test object.
1020 // The default transaction produced is valid and has three inputs:
1021 // - an issuance of 10 units
1022 // - a spend of 20 units
1023 // - a spend of 40 units
1024 // and two outputs, one of 25 units and one of 45 units.
1025 // All amounts are denominated in the same asset.
1027 // The issuance program for the asset requires two numbers as
1028 // arguments that add up to 5. The prevout control programs require
1029 // two numbers each, adding to 9 and 13, respectively.
1031 // The min and max times for the transaction are now +/- one minute.
1032 func sample(tb testing.TB, in *txFixture) *txFixture {
1033 var result txFixture
1038 if result.initialBlockID.IsZero() {
1039 result.initialBlockID = *newHash(1)
1041 if testutil.DeepEqual(result.issuanceProg, bc.Program{}) {
1042 prog, err := vm.Assemble("ADD 5 NUMEQUAL")
1046 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog}
1048 if len(result.issuanceArgs) == 0 {
1049 result.issuanceArgs = [][]byte{[]byte{2}, []byte{3}}
1051 if len(result.assetDef) == 0 {
1052 result.assetDef = []byte{2}
1054 if result.assetID.IsZero() {
1055 refdatahash := hashData(result.assetDef)
1056 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, result.issuanceProg.VmVersion, &refdatahash)
1059 if result.txVersion == 0 {
1060 result.txVersion = 1
1062 if len(result.txInputs) == 0 {
1063 cp1, err := vm.Assemble("ADD 9 NUMEQUAL")
1067 args1 := [][]byte{[]byte{4}, []byte{5}}
1069 cp2, err := vm.Assemble("ADD 13 NUMEQUAL")
1073 args2 := [][]byte{[]byte{6}, []byte{7}}
1075 result.txInputs = []*types.TxInput{
1076 types.NewIssuanceInput([]byte{3}, 10, result.issuanceProg.Code, result.issuanceArgs, result.assetDef),
1077 types.NewSpendInput(args1, *newHash(5), result.assetID, 20, 0, cp1),
1078 types.NewSpendInput(args2, *newHash(8), result.assetID, 40, 0, cp2),
1082 result.txInputs = append(result.txInputs, mockGasTxInput())
1084 if len(result.txOutputs) == 0 {
1085 cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
1089 cp2, err := vm.Assemble("ADD 21 NUMEQUAL")
1094 result.txOutputs = []*types.TxOutput{
1095 types.NewTxOutput(result.assetID, 25, cp1),
1096 types.NewTxOutput(result.assetID, 45, cp2),
1100 result.tx = &types.TxData{
1101 Version: result.txVersion,
1102 Inputs: result.txInputs,
1103 Outputs: result.txOutputs,
1109 func mockBlock() *bc.Block {
1111 BlockHeader: &bc.BlockHeader{
1117 func mockGasTxInput() *types.TxInput {
1118 cp, _ := vmutil.DefaultCoinbaseProgram()
1119 return types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)
1122 // Like errors.Root, but also unwraps vm.Error objects.
1123 func rootErr(e error) error {
1124 return errors.Root(e)
1127 func hashData(data []byte) bc.Hash {
1129 sha3pool.Sum256(b32[:], data)
1130 return bc.NewHash(b32)
1133 func newHash(n byte) *bc.Hash {
1134 h := bc.NewHash([32]byte{n})
1138 func newAssetID(n byte) *bc.AssetID {
1139 a := bc.NewAssetID([32]byte{n})
1143 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance {
1144 id := tx.InputIDs[index]
1145 res, err := tx.Issuance(id)
1152 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend {
1153 id := tx.InputIDs[index]
1154 res, err := tx.Spend(id)
1161 func getMuxID(tx *bc.Tx) *bc.Hash {
1162 out := tx.Entries[*tx.ResultIds[0]]
1163 switch result := out.(type) {
1165 return result.Source.Ref
1166 case *bc.Retirement:
1167 return result.Source.Ref