OSDN Git Service

add tx unit test
[bytom/bytom.git] / protocol / validation / tx_test.go
1 package validation
2
3 import (
4         "math"
5         "testing"
6
7         "github.com/davecgh/go-spew/spew"
8
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"
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:  10000,
81                                 GasUsed:  0,
82                                 BTMValue: 0,
83                         },
84                         output: &GasState{
85                                 GasLeft:  10000,
86                                 GasUsed:  0,
87                                 BTMValue: 0,
88                         },
89                         f: func(input *GasState) error {
90                                 return input.updateUsage(-1)
91                         },
92                         err: ErrGasCalculate,
93                 },
94                 {
95                         input: &GasState{
96                                 GasLeft:  10000,
97                                 GasUsed:  0,
98                                 BTMValue: 0,
99                         },
100                         output: &GasState{
101                                 GasLeft:  9999,
102                                 GasUsed:  1,
103                                 BTMValue: 0,
104                         },
105                         f: func(input *GasState) error {
106                                 return input.updateUsage(9999)
107                         },
108                         err: nil,
109                 },
110                 {
111                         input: &GasState{
112                                 GasLeft:    1000,
113                                 GasUsed:    10,
114                                 StorageGas: 1000,
115                                 GasValid:   false,
116                         },
117                         output: &GasState{
118                                 GasLeft:    0,
119                                 GasUsed:    1010,
120                                 StorageGas: 1000,
121                                 GasValid:   true,
122                         },
123                         f: func(input *GasState) error {
124                                 return input.setGasValid()
125                         },
126                         err: nil,
127                 },
128                 {
129                         input: &GasState{
130                                 GasLeft:    900,
131                                 GasUsed:    10,
132                                 StorageGas: 1000,
133                                 GasValid:   false,
134                         },
135                         output: &GasState{
136                                 GasLeft:    -100,
137                                 GasUsed:    10,
138                                 StorageGas: 1000,
139                                 GasValid:   false,
140                         },
141                         f: func(input *GasState) error {
142                                 return input.setGasValid()
143                         },
144                         err: ErrGasCalculate,
145                 },
146                 {
147                         input: &GasState{
148                                 GasLeft:    1000,
149                                 GasUsed:    math.MaxInt64,
150                                 StorageGas: 1000,
151                                 GasValid:   false,
152                         },
153                         output: &GasState{
154                                 GasLeft:    0,
155                                 GasUsed:    0,
156                                 StorageGas: 1000,
157                                 GasValid:   false,
158                         },
159                         f: func(input *GasState) error {
160                                 return input.setGasValid()
161                         },
162                         err: ErrGasCalculate,
163                 },
164                 {
165                         input: &GasState{
166                                 GasLeft:    math.MinInt64,
167                                 GasUsed:    0,
168                                 StorageGas: 1000,
169                                 GasValid:   false,
170                         },
171                         output: &GasState{
172                                 GasLeft:    0,
173                                 GasUsed:    0,
174                                 StorageGas: 1000,
175                                 GasValid:   false,
176                         },
177                         f: func(input *GasState) error {
178                                 return input.setGasValid()
179                         },
180                         err: ErrGasCalculate,
181                 },
182         }
183
184         for i, c := range cases {
185                 err := c.f(c.input)
186
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)
191                 }
192         }
193 }
194
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))
201
202                 for _, amount := range inputs {
203                         txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram)
204                         txInputs = append(txInputs, txInput)
205                 }
206
207                 for _, amount := range outputs {
208                         txOutput := types.NewTxOutput(*consensus.BTMAssetID, amount, ctrlProgram)
209                         txOutputs = append(txOutputs, txOutput)
210                 }
211
212                 txData := &types.TxData{
213                         Version:        1,
214                         SerializedSize: 100,
215                         TimeRange:      0,
216                         Inputs:         txInputs,
217                         Outputs:        txOutputs,
218                 }
219                 return types.MapTx(txData)
220         }
221
222         cases := []struct {
223                 inputs  []uint64
224                 outputs []uint64
225                 err     error
226         }{
227                 {
228                         inputs:  []uint64{math.MaxUint64, 1},
229                         outputs: []uint64{0},
230                         err:     ErrOverflow,
231                 },
232                 {
233                         inputs:  []uint64{math.MaxUint64, math.MaxUint64},
234                         outputs: []uint64{0},
235                         err:     ErrOverflow,
236                 },
237                 {
238                         inputs:  []uint64{math.MaxUint64, math.MaxUint64 - 1},
239                         outputs: []uint64{0},
240                         err:     ErrOverflow,
241                 },
242                 {
243                         inputs:  []uint64{math.MaxInt64, 1},
244                         outputs: []uint64{0},
245                         err:     ErrOverflow,
246                 },
247                 {
248                         inputs:  []uint64{math.MaxInt64, math.MaxInt64},
249                         outputs: []uint64{0},
250                         err:     ErrOverflow,
251                 },
252                 {
253                         inputs:  []uint64{math.MaxInt64, math.MaxInt64 - 1},
254                         outputs: []uint64{0},
255                         err:     ErrOverflow,
256                 },
257                 {
258                         inputs:  []uint64{0},
259                         outputs: []uint64{math.MaxUint64},
260                         err:     ErrOverflow,
261                 },
262                 {
263                         inputs:  []uint64{0},
264                         outputs: []uint64{math.MaxInt64},
265                         err:     ErrGasCalculate,
266                 },
267                 {
268                         inputs:  []uint64{math.MaxInt64 - 1},
269                         outputs: []uint64{math.MaxInt64},
270                         err:     ErrGasCalculate,
271                 },
272         }
273
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))
278                 }
279         }
280 }
281
282 func TestTxValidation(t *testing.T) {
283         var (
284                 tx      *bc.Tx
285                 vs      *validationState
286                 fixture *txFixture
287
288                 // the mux from tx, pulled out for convenience
289                 mux *bc.Mux
290         )
291
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
299
300                 mux.Sources = append(mux.Sources, &bc.ValueSource{
301                         Ref:   &coinbaseID,
302                         Value: &txOutput.AssetAmount,
303                 })
304
305                 src := &bc.ValueSource{
306                         Ref:      muxID,
307                         Value:    &txOutput.AssetAmount,
308                         Position: uint64(len(tx.ResultIds)),
309                 }
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
314
315                 dest := &bc.ValueDestination{
316                         Value:    src.Value,
317                         Ref:      &outputID,
318                         Position: 0,
319                 }
320                 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
321                 tx.ResultIds = append(tx.ResultIds, &outputID)
322                 vs.block.Transactions = append(vs.block.Transactions, vs.tx)
323         }
324
325         cases := []struct {
326                 desc string // description of the test case
327                 f    func() // function to adjust tx, vs, and/or mux
328                 err  error  // expected error
329         }{
330                 {
331                         desc: "base case",
332                 },
333                 {
334                         desc: "unbalanced mux amounts",
335                         f: func() {
336                                 mux.Sources[0].Value.Amount++
337                                 iss := tx.Entries[*mux.Sources[0].Ref].(*bc.Issuance)
338                                 iss.WitnessDestination.Value.Amount++
339                         },
340                         err: ErrUnbalanced,
341                 },
342                 {
343                         desc: "unbalanced mux amounts",
344                         f: func() {
345                                 mux.WitnessDestinations[0].Value.Amount++
346                         },
347                         err: ErrUnbalanced,
348                 },
349                 {
350                         desc: "balanced mux amounts",
351                         f: func() {
352                                 mux.Sources[1].Value.Amount++
353                                 mux.WitnessDestinations[0].Value.Amount++
354                         },
355                         err: nil,
356                 },
357                 {
358                         desc: "overflowing mux source amounts",
359                         f: func() {
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
363                         },
364                         err: ErrOverflow,
365                 },
366                 {
367                         desc: "underflowing mux destination amounts",
368                         f: func() {
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
375                         },
376                         err: ErrOverflow,
377                 },
378                 {
379                         desc: "unbalanced mux assets",
380                         f: func() {
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)
384                         },
385                         err: ErrUnbalanced,
386                 },
387                 {
388                         desc: "mismatched output source / mux dest position",
389                         f: func() {
390                                 tx.Entries[*tx.ResultIds[0]].(*bc.Output).Source.Position = 1
391                         },
392                         err: ErrMismatchedPosition,
393                 },
394                 {
395                         desc: "mismatched input dest / mux source position",
396                         f: func() {
397                                 mux.Sources[0].Position = 1
398                         },
399                         err: ErrMismatchedPosition,
400                 },
401                 {
402                         desc: "mismatched output source and mux dest",
403                         f: func() {
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
414                         },
415                         err: ErrMismatchedReference,
416                 },
417                 {
418                         desc: "mismatched input dest and mux source",
419                         f: func() {
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
429                         },
430                         err: ErrMismatchedReference,
431                 },
432                 {
433                         desc: "invalid mux destination position",
434                         f: func() {
435                                 mux.WitnessDestinations[0].Position = 1
436                         },
437                         err: ErrPosition,
438                 },
439                 {
440                         desc: "mismatched mux dest value / output source value",
441                         f: func() {
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,
447                                 }
448                                 mux.Sources[0].Value.Amount++ // the mux must still balance
449                         },
450                         err: ErrMismatchedValue,
451                 },
452                 {
453                         desc: "empty tx results",
454                         f: func() {
455                                 tx.ResultIds = nil
456                         },
457                         err: ErrEmptyResults,
458                 },
459                 {
460                         desc: "empty tx results, but that's OK",
461                         f: func() {
462                                 tx.Version = 2
463                                 tx.ResultIds = nil
464                         },
465                 },
466                 {
467                         desc: "issuance program failure",
468                         f: func() {
469                                 iss := txIssuance(t, tx, 0)
470                                 iss.WitnessArguments[0] = []byte{}
471                         },
472                         err: vm.ErrFalseVMResult,
473                 },
474                 {
475                         desc: "spend control program failure",
476                         f: func() {
477                                 spend := txSpend(t, tx, 1)
478                                 spend.WitnessArguments[0] = []byte{}
479                         },
480                         err: vm.ErrFalseVMResult,
481                 },
482                 {
483                         desc: "mismatched spent source/witness value",
484                         f: func() {
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,
490                                 }
491                         },
492                         err: ErrMismatchedValue,
493                 },
494                 {
495                         desc: "gas out of limit",
496                         f: func() {
497                                 vs.tx.SerializedSize = 10000000
498                         },
499                         err: ErrOverGasCredit,
500                 },
501                 {
502                         desc: "can't find gas spend input in entries",
503                         f: func() {
504                                 spendID := mux.Sources[len(mux.Sources)-1].Ref
505                                 delete(tx.Entries, *spendID)
506                                 mux.Sources = mux.Sources[:len(mux.Sources)-1]
507                         },
508                         err: bc.ErrMissingEntry,
509                 },
510                 {
511                         desc: "no gas spend input",
512                         f: func() {
513                                 spendID := mux.Sources[len(mux.Sources)-1].Ref
514                                 delete(tx.Entries, *spendID)
515                                 mux.Sources = mux.Sources[:len(mux.Sources)-1]
516                                 tx.GasInputIDs = nil
517                                 vs.gasStatus.GasLeft = 0
518                         },
519                         err: vm.ErrRunLimitExceeded,
520                 },
521                 {
522                         desc: "no gas spend input, but set gas left, so it's ok",
523                         f: func() {
524                                 spendID := mux.Sources[len(mux.Sources)-1].Ref
525                                 delete(tx.Entries, *spendID)
526                                 mux.Sources = mux.Sources[:len(mux.Sources)-1]
527                                 tx.GasInputIDs = nil
528                         },
529                         err: nil,
530                 },
531                 {
532                         desc: "mismatched gas spend input destination amount/prevout source amount",
533                         f: func() {
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,
539                                 }
540                         },
541                         err: ErrMismatchedValue,
542                 },
543                 {
544                         desc: "mismatched witness asset destination",
545                         f: func() {
546                                 issuanceID := mux.Sources[0].Ref
547                                 issuance := tx.Entries[*issuanceID].(*bc.Issuance)
548                                 issuance.WitnessAssetDefinition.Data = &bc.Hash{V0: 9999}
549                         },
550                         err: ErrMismatchedAssetID,
551                 },
552                 {
553                         desc: "issuance witness position greater than length of mux sources",
554                         f: func() {
555                                 issuanceID := mux.Sources[0].Ref
556                                 issuance := tx.Entries[*issuanceID].(*bc.Issuance)
557                                 issuance.WitnessDestination.Position = uint64(len(mux.Sources) + 1)
558                         },
559                         err: ErrPosition,
560                 },
561                 {
562                         desc: "normal coinbase tx",
563                         f: func() {
564                                 addCoinbase(consensus.BTMAssetID, 100000, nil)
565                         },
566                         err: nil,
567                 },
568                 {
569                         desc: "invalid coinbase tx asset id",
570                         f: func() {
571                                 addCoinbase(&bc.AssetID{V1: 100}, 100000, nil)
572                         },
573                         err: ErrWrongCoinbaseAsset,
574                 },
575                 {
576                         desc: "coinbase tx is not first tx in block",
577                         f: func() {
578                                 addCoinbase(consensus.BTMAssetID, 100000, nil)
579                                 vs.block.Transactions[0] = nil
580                         },
581                         err: ErrWrongCoinbaseTransaction,
582                 },
583                 {
584                         desc: "coinbase arbitrary size out of limit",
585                         f: func() {
586                                 arbitrary := make([]byte, consensus.CoinbaseArbitrarySizeLimit+1)
587                                 addCoinbase(consensus.BTMAssetID, 100000, arbitrary)
588                         },
589                         err: ErrCoinbaseArbitraryOversize,
590                 },
591                 {
592                         desc: "normal retirement output",
593                         f: func() {
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
602                         },
603                         err: nil,
604                 },
605                 {
606                         desc: "ordinal doesn't matter for prevouts",
607                         f: func() {
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
613                         },
614                         err: nil,
615                 },
616                 {
617                         desc: "mux witness destination have no source",
618                         f: func() {
619                                 dest := &bc.ValueDestination{
620                                         Value: &bc.AssetAmount{
621                                                 AssetId: &bc.AssetID{V2: 1000},
622                                                 Amount:  100,
623                                         },
624                                         Ref:      mux.WitnessDestinations[0].Ref,
625                                         Position: 0,
626                                 }
627                                 mux.WitnessDestinations = append(mux.WitnessDestinations, dest)
628                         },
629                         err: ErrNoSource,
630                 },
631         }
632
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{
638                                 block:   mockBlock(),
639                                 tx:      tx,
640                                 entryID: tx.ID,
641                                 gasStatus: &GasState{
642                                         GasLeft: int64(80000),
643                                         GasUsed: 0,
644                                 },
645                                 cache: make(map[bc.Hash]error),
646                         }
647                         muxID := getMuxID(tx)
648                         mux = tx.Entries[*muxID].(*bc.Mux)
649
650                         if c.f != nil {
651                                 c.f()
652                         }
653                         err := checkValid(vs, tx.TxHeader)
654
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))
657                         }
658                 })
659         }
660 }
661
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{
667                 SerializedSize: 1,
668                 Inputs: []*types.TxInput{
669                         types.NewCoinbaseInput(nil),
670                 },
671                 Outputs: []*types.TxOutput{
672                         types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
673                 },
674         })
675
676         cases := []struct {
677                 block    *bc.Block
678                 txIndex  int
679                 GasValid bool
680                 err      error
681         }{
682                 {
683                         block: &bc.Block{
684                                 BlockHeader:  &bc.BlockHeader{Height: 666},
685                                 Transactions: []*bc.Tx{CbTx},
686                         },
687                         txIndex:  0,
688                         GasValid: true,
689                         err:      nil,
690                 },
691                 {
692                         block: &bc.Block{
693                                 BlockHeader: &bc.BlockHeader{Height: 666},
694                                 Transactions: []*bc.Tx{
695                                         CbTx,
696                                         types.MapTx(&types.TxData{
697                                                 SerializedSize: 1,
698                                                 Inputs: []*types.TxInput{
699                                                         types.NewCoinbaseInput(nil),
700                                                 },
701                                                 Outputs: []*types.TxOutput{
702                                                         types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
703                                                 },
704                                         }),
705                                 },
706                         },
707                         txIndex:  1,
708                         GasValid: false,
709                         err:      ErrWrongCoinbaseTransaction,
710                 },
711                 {
712                         block: &bc.Block{
713                                 BlockHeader: &bc.BlockHeader{Height: 666},
714                                 Transactions: []*bc.Tx{
715                                         CbTx,
716                                         types.MapTx(&types.TxData{
717                                                 SerializedSize: 1,
718                                                 Inputs: []*types.TxInput{
719                                                         types.NewCoinbaseInput(nil),
720                                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
721                                                 },
722                                                 Outputs: []*types.TxOutput{
723                                                         types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
724                                                         types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
725                                                 },
726                                         }),
727                                 },
728                         },
729                         txIndex:  1,
730                         GasValid: false,
731                         err:      ErrWrongCoinbaseTransaction,
732                 },
733                 {
734                         block: &bc.Block{
735                                 BlockHeader: &bc.BlockHeader{Height: 666},
736                                 Transactions: []*bc.Tx{
737                                         CbTx,
738                                         types.MapTx(&types.TxData{
739                                                 SerializedSize: 1,
740                                                 Inputs: []*types.TxInput{
741                                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
742                                                         types.NewCoinbaseInput(nil),
743                                                 },
744                                                 Outputs: []*types.TxOutput{
745                                                         types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
746                                                         types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
747                                                 },
748                                         }),
749                                 },
750                         },
751                         txIndex:  1,
752                         GasValid: false,
753                         err:      ErrWrongCoinbaseTransaction,
754                 },
755                 {
756                         block: &bc.Block{
757                                 BlockHeader: &bc.BlockHeader{Height: 666},
758                                 Transactions: []*bc.Tx{
759                                         types.MapTx(&types.TxData{
760                                                 SerializedSize: 1,
761                                                 Inputs: []*types.TxInput{
762                                                         types.NewCoinbaseInput(nil),
763                                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
764                                                 },
765                                                 Outputs: []*types.TxOutput{
766                                                         types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
767                                                         types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
768                                                 },
769                                         }),
770                                 },
771                         },
772                         txIndex:  0,
773                         GasValid: true,
774                         err:      nil,
775                 },
776                 {
777                         block: &bc.Block{
778                                 BlockHeader: &bc.BlockHeader{Height: 666},
779                                 Transactions: []*bc.Tx{
780                                         types.MapTx(&types.TxData{
781                                                 SerializedSize: 1,
782                                                 Inputs: []*types.TxInput{
783                                                         types.NewCoinbaseInput(nil),
784                                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, retire),
785                                                 },
786                                                 Outputs: []*types.TxOutput{
787                                                         types.NewTxOutput(*consensus.BTMAssetID, 888, cp),
788                                                         types.NewTxOutput(*consensus.BTMAssetID, 90000000, cp),
789                                                 },
790                                         }),
791                                 },
792                         },
793                         txIndex:  0,
794                         GasValid: false,
795                         err:      vm.ErrReturn,
796                 },
797         }
798
799         for i, c := range cases {
800                 gasStatus, err := ValidateTx(c.block.Transactions[c.txIndex], c.block)
801
802                 if rootErr(err) != c.err {
803                         t.Errorf("#%d got error %s, want %s", i, err, c.err)
804                 }
805                 if c.GasValid != gasStatus.GasValid {
806                         t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid)
807                 }
808         }
809 }
810
811 func TestRuleAA(t *testing.T) {
812         testData := "070100040161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f6302404a17a5995b8163ee448719b462a5694b22a35522dd9883333fd462cc3d0aabf049445c5cbb911a40e1906a5bea99b23b1a79e215eeb1a818d8b1dd27e06f3004200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940161015f9bc47dda88eee18c7433340c16e054cabee4318a8d638e873be19e979df81dc7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0e3f9f5c80e01011600147c7662d92bd5e77454736f94731c60a6e9cbc69f63024062c29b20941e7f762c3afae232f61d8dac1c544825931e391408c6715c408ef69f494a1b3b61ce380ddee0c8b18ecac2b46ef96a62eebb6ec40f9f545410870a200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce9940160015ee334d4fe18398f0232d2aca7050388ce4ee5ae82c8148d7f0cea748438b65135ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80ace6842001011600147c7662d92bd5e77454736f94731c60a6e9cbc69f630240e443d66c75b4d5fa71676d60b0b067e6941f06349f31e5f73a7d51a73f5797632b2e01e8584cd1c8730dc16df075866b0c796bd7870182e2da4b37188208fe02200530c4bc9dd3cbf679fec6d824ce5c37b0c8dab88b67bcae3b000924b7dce99402013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08ba3fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08fe0fae80e01160014aac0345165045e612b3d7363f39a372bead80ce700"
813         tx := types.Tx{}
814         if err := tx.UnmarshalText([]byte(testData)); err != nil {
815                 t.Errorf("fail on unmarshal txData: %s", err)
816         }
817
818         cases := []struct {
819                 block    *bc.Block
820                 GasValid bool
821                 err      error
822         }{
823                 {
824                         block: &bc.Block{
825                                 BlockHeader: &bc.BlockHeader{
826                                         Height: ruleAA - 1,
827                                 },
828                         },
829                         GasValid: true,
830                         err:      ErrMismatchedPosition,
831                 },
832                 {
833                         block: &bc.Block{
834                                 BlockHeader: &bc.BlockHeader{
835                                         Height: ruleAA,
836                                 },
837                         },
838                         GasValid: false,
839                         err:      ErrEmptyInputIDs,
840                 },
841         }
842
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)
847                 }
848                 if c.GasValid != gasStatus.GasValid {
849                         t.Errorf("#%d got GasValid %t, want %t", i, gasStatus.GasValid, c.GasValid)
850                 }
851         }
852
853 }
854
855 // TestTimeRange test the checkTimeRange function (txtest#1004)
856 func TestTimeRange(t *testing.T) {
857         cases := []struct {
858                 timeRange uint64
859                 err       bool
860         }{
861                 {
862                         timeRange: 0,
863                         err:       false,
864                 },
865                 {
866                         timeRange: 334,
867                         err:       false,
868                 },
869                 {
870                         timeRange: 332,
871                         err:       true,
872                 },
873                 {
874                         timeRange: 1521625824,
875                         err:       false,
876                 },
877         }
878
879         block := &bc.Block{
880                 BlockHeader: &bc.BlockHeader{
881                         Height:    333,
882                         Timestamp: 1521625823,
883                 },
884         }
885
886         tx := types.MapTx(&types.TxData{
887                 SerializedSize: 1,
888                 TimeRange:      0,
889                 Inputs: []*types.TxInput{
890                         mockGasTxInput(),
891                 },
892                 Outputs: []*types.TxOutput{
893                         types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}),
894                 },
895         })
896
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)
901                 }
902         }
903 }
904
905 func TestStandardTx(t *testing.T) {
906         fixture := sample(t, nil)
907         tx := types.NewTx(*fixture.tx).Tx
908
909         cases := []struct {
910                 desc string
911                 f    func()
912                 err  error
913         }{
914                 {
915                         desc: "normal standard tx",
916                         err:  nil,
917                 },
918                 {
919                         desc: "not standard tx in spend input",
920                         f: func() {
921                                 inputID := tx.GasInputIDs[0]
922                                 spend := tx.Entries[inputID].(*bc.Spend)
923                                 spentOutput, err := tx.Output(*spend.SpentOutputId)
924                                 if err != nil {
925                                         t.Fatal(err)
926                                 }
927                                 spentOutput.ControlProgram = &bc.Program{Code: []byte{0}}
928                         },
929                         err: ErrNotStandardTx,
930                 },
931                 {
932                         desc: "not standard tx in output",
933                         f: func() {
934                                 outputID := tx.ResultIds[0]
935                                 output := tx.Entries[*outputID].(*bc.Output)
936                                 output.ControlProgram = &bc.Program{Code: []byte{0}}
937                         },
938                         err: ErrNotStandardTx,
939                 },
940         }
941
942         for i, c := range cases {
943                 if c.f != nil {
944                         c.f()
945                 }
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)
948                 }
949         }
950 }
951
952 func TestValidateTxVersion(t *testing.T) {
953         cases := []struct {
954                 desc  string
955                 block *bc.Block
956                 err   error
957         }{
958                 {
959                         desc: "tx version greater than 1 (txtest#1001)",
960                         block: &bc.Block{
961                                 BlockHeader: &bc.BlockHeader{Version: 1},
962                                 Transactions: []*bc.Tx{
963                                         &bc.Tx{TxHeader: &bc.TxHeader{Version: 2}},
964                                 },
965                         },
966                         err: ErrTxVersion,
967                 },
968                 {
969                         desc: "tx version equals 0 (txtest#1002)",
970                         block: &bc.Block{
971                                 BlockHeader: &bc.BlockHeader{Version: 1},
972                                 Transactions: []*bc.Tx{
973                                         &bc.Tx{TxHeader: &bc.TxHeader{Version: 0}},
974                                 },
975                         },
976                         err: ErrTxVersion,
977                 },
978                 {
979                         desc: "tx version equals max uint64 (txtest#1003)",
980                         block: &bc.Block{
981                                 BlockHeader: &bc.BlockHeader{Version: 1},
982                                 Transactions: []*bc.Tx{
983                                         &bc.Tx{TxHeader: &bc.TxHeader{Version: math.MaxUint64}},
984                                 },
985                         },
986                         err: ErrTxVersion,
987                 },
988         }
989
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)
993                 }
994         }
995 }
996
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
1005         assetDef       []byte
1006         assetID        bc.AssetID
1007         txVersion      uint64
1008         txInputs       []*types.TxInput
1009         txOutputs      []*types.TxOutput
1010         tx             *types.TxData
1011 }
1012
1013 // Produces a sample transaction in a txFixture object (see above). A
1014 // separate input txFixture can be used to alter the transaction
1015 // that's created.
1016 //
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.
1019 //
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.
1026 //
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.
1030 //
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
1034         if in != nil {
1035                 result = *in
1036         }
1037
1038         if result.initialBlockID.IsZero() {
1039                 result.initialBlockID = *newHash(1)
1040         }
1041         if testutil.DeepEqual(result.issuanceProg, bc.Program{}) {
1042                 prog, err := vm.Assemble("ADD 5 NUMEQUAL")
1043                 if err != nil {
1044                         tb.Fatal(err)
1045                 }
1046                 result.issuanceProg = bc.Program{VmVersion: 1, Code: prog}
1047         }
1048         if len(result.issuanceArgs) == 0 {
1049                 result.issuanceArgs = [][]byte{[]byte{2}, []byte{3}}
1050         }
1051         if len(result.assetDef) == 0 {
1052                 result.assetDef = []byte{2}
1053         }
1054         if result.assetID.IsZero() {
1055                 refdatahash := hashData(result.assetDef)
1056                 result.assetID = bc.ComputeAssetID(result.issuanceProg.Code, result.issuanceProg.VmVersion, &refdatahash)
1057         }
1058
1059         if result.txVersion == 0 {
1060                 result.txVersion = 1
1061         }
1062         if len(result.txInputs) == 0 {
1063                 cp1, err := vm.Assemble("ADD 9 NUMEQUAL")
1064                 if err != nil {
1065                         tb.Fatal(err)
1066                 }
1067                 args1 := [][]byte{[]byte{4}, []byte{5}}
1068
1069                 cp2, err := vm.Assemble("ADD 13 NUMEQUAL")
1070                 if err != nil {
1071                         tb.Fatal(err)
1072                 }
1073                 args2 := [][]byte{[]byte{6}, []byte{7}}
1074
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),
1079                 }
1080         }
1081
1082         result.txInputs = append(result.txInputs, mockGasTxInput())
1083
1084         if len(result.txOutputs) == 0 {
1085                 cp1, err := vm.Assemble("ADD 17 NUMEQUAL")
1086                 if err != nil {
1087                         tb.Fatal(err)
1088                 }
1089                 cp2, err := vm.Assemble("ADD 21 NUMEQUAL")
1090                 if err != nil {
1091                         tb.Fatal(err)
1092                 }
1093
1094                 result.txOutputs = []*types.TxOutput{
1095                         types.NewTxOutput(result.assetID, 25, cp1),
1096                         types.NewTxOutput(result.assetID, 45, cp2),
1097                 }
1098         }
1099
1100         result.tx = &types.TxData{
1101                 Version: result.txVersion,
1102                 Inputs:  result.txInputs,
1103                 Outputs: result.txOutputs,
1104         }
1105
1106         return &result
1107 }
1108
1109 func mockBlock() *bc.Block {
1110         return &bc.Block{
1111                 BlockHeader: &bc.BlockHeader{
1112                         Height: 666,
1113                 },
1114         }
1115 }
1116
1117 func mockGasTxInput() *types.TxInput {
1118         cp, _ := vmutil.DefaultCoinbaseProgram()
1119         return types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)
1120 }
1121
1122 // Like errors.Root, but also unwraps vm.Error objects.
1123 func rootErr(e error) error {
1124         return errors.Root(e)
1125 }
1126
1127 func hashData(data []byte) bc.Hash {
1128         var b32 [32]byte
1129         sha3pool.Sum256(b32[:], data)
1130         return bc.NewHash(b32)
1131 }
1132
1133 func newHash(n byte) *bc.Hash {
1134         h := bc.NewHash([32]byte{n})
1135         return &h
1136 }
1137
1138 func newAssetID(n byte) *bc.AssetID {
1139         a := bc.NewAssetID([32]byte{n})
1140         return &a
1141 }
1142
1143 func txIssuance(t *testing.T, tx *bc.Tx, index int) *bc.Issuance {
1144         id := tx.InputIDs[index]
1145         res, err := tx.Issuance(id)
1146         if err != nil {
1147                 t.Fatal(err)
1148         }
1149         return res
1150 }
1151
1152 func txSpend(t *testing.T, tx *bc.Tx, index int) *bc.Spend {
1153         id := tx.InputIDs[index]
1154         res, err := tx.Spend(id)
1155         if err != nil {
1156                 t.Fatal(err)
1157         }
1158         return res
1159 }
1160
1161 func getMuxID(tx *bc.Tx) *bc.Hash {
1162         out := tx.Entries[*tx.ResultIds[0]]
1163         switch result := out.(type) {
1164         case *bc.Output:
1165                 return result.Source.Ref
1166         case *bc.Retirement:
1167                 return result.Source.Ref
1168         }
1169         return nil
1170 }