OSDN Git Service

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