OSDN Git Service

versoin1.1.9 (#594)
[bytom/vapor.git] / protocol / validation / block_test.go
1 package validation
2
3 import (
4         "math"
5         "testing"
6         "time"
7
8         "github.com/bytom/vapor/consensus"
9         "github.com/bytom/vapor/protocol/bc"
10         "github.com/bytom/vapor/protocol/bc/types"
11         "github.com/bytom/vapor/protocol/state"
12         "github.com/bytom/vapor/protocol/vm"
13         "github.com/bytom/vapor/protocol/vm/vmutil"
14         "github.com/bytom/vapor/testutil"
15 )
16
17 func TestCheckBlockTime(t *testing.T) {
18         cases := []struct {
19                 desc       string
20                 blockTime  uint64
21                 parentTime []uint64
22                 err        error
23         }{
24                 {
25                         blockTime:  1520000500,
26                         parentTime: []uint64{1520000000},
27                         err:        nil,
28                 },
29                 {
30                         desc:       "timestamp less than past median time",
31                         blockTime:  1520005500,
32                         parentTime: []uint64{1520000000, 1520000500, 1520001000, 1520001500, 1520002000, 1520002500, 1520003000, 1520003500, 1520004000, 1520004500, 1520005000},
33                         err:        nil,
34                 },
35                 {
36                         desc:       "timestamp greater than max limit",
37                         blockTime:  99999999990000,
38                         parentTime: []uint64{15200000000000},
39                         err:        errBadTimestamp,
40                 },
41                 {
42                         desc:       "timestamp of the block and the parent block are both greater than max limit",
43                         blockTime:  uint64(time.Now().UnixNano()/int64(time.Millisecond)) + consensus.ActiveNetParams.MaxTimeOffsetMs + 2000,
44                         parentTime: []uint64{uint64(time.Now().UnixNano()/int64(time.Millisecond)) + consensus.ActiveNetParams.MaxTimeOffsetMs + 1000},
45                         err:        errBadTimestamp,
46                 },
47         }
48
49         parent := &types.BlockHeader{Version: 1}
50         block := &bc.Block{
51                 BlockHeader: &bc.BlockHeader{Version: 1},
52         }
53
54         for i, c := range cases {
55                 parent.Timestamp = c.parentTime[0]
56                 parentSuccessor := parent
57                 for i := 1; i < len(c.parentTime); i++ {
58                         Previous := &types.BlockHeader{Version: 1, Timestamp: c.parentTime[i]}
59                         parentSuccessor.PreviousBlockHash = Previous.Hash()
60                         parentSuccessor = Previous
61                 }
62
63                 block.Timestamp = c.blockTime
64                 if err := checkBlockTime(block, parent); rootErr(err) != c.err {
65                         t.Errorf("case %d got error %s, want %s", i, err, c.err)
66                 }
67         }
68 }
69
70 func TestCheckCoinbaseTx(t *testing.T) {
71         cases := []struct {
72                 desc    string
73                 txs     []*types.Tx
74                 rewards []state.CoinbaseReward
75                 err     error
76         }{
77                 {
78                         desc: "zero coinbase amount",
79                         txs: []*types.Tx{
80                                 types.NewTx(types.TxData{
81                                         Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
82                                         Outputs: []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, []byte{0x51})},
83                                 }),
84                         },
85                         rewards: []state.CoinbaseReward{},
86                         err:     nil,
87                 },
88                 {
89                         desc: "zero coinbase amount and aggregate rewards",
90                         txs: []*types.Tx{
91                                 types.NewTx(types.TxData{
92                                         Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
93                                         Outputs: []*types.TxOutput{
94                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 0, []byte{0x51}),
95                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 20000, []byte{0x51}),
96                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 10000, []byte{0x52}),
97                                         },
98                                 }),
99                         },
100                         rewards: []state.CoinbaseReward{
101                                 state.CoinbaseReward{
102                                         Amount:         20000,
103                                         ControlProgram: []byte{0x51},
104                                 },
105                                 state.CoinbaseReward{
106                                         Amount:         10000,
107                                         ControlProgram: []byte{0x52},
108                                 },
109                         },
110                         err: nil,
111                 },
112                 {
113                         desc:    "wrong coinbase transaction with block is empty",
114                         txs:     []*types.Tx{},
115                         rewards: []state.CoinbaseReward{},
116                         err:     ErrWrongCoinbaseTransaction,
117                 },
118                 {
119                         desc: "wrong coinbase transaction with dismatch number of outputs",
120                         txs: []*types.Tx{
121                                 types.NewTx(types.TxData{
122                                         Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
123                                         Outputs: []*types.TxOutput{
124                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 0, []byte{0x51}),
125                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 20000, []byte{0x51}),
126                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 10000, []byte{0x52}),
127                                         },
128                                 }),
129                         },
130                         rewards: []state.CoinbaseReward{
131                                 state.CoinbaseReward{
132                                         Amount:         20000,
133                                         ControlProgram: []byte{0x51},
134                                 },
135                         },
136                         err: ErrWrongCoinbaseTransaction,
137                 },
138                 {
139                         desc: "wrong coinbase transaction with dismatch output amount",
140                         txs: []*types.Tx{
141                                 types.NewTx(types.TxData{
142                                         Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
143                                         Outputs: []*types.TxOutput{
144                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 0, []byte{0x51}),
145                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 20000, []byte{0x51}),
146                                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 10000, []byte{0x52}),
147                                         },
148                                 }),
149                         },
150                         rewards: []state.CoinbaseReward{
151                                 state.CoinbaseReward{
152                                         Amount:         10000,
153                                         ControlProgram: []byte{0x51},
154                                 },
155                                 state.CoinbaseReward{
156                                         Amount:         10000,
157                                         ControlProgram: []byte{0x52},
158                                 },
159                         },
160                         err: ErrWrongCoinbaseTransaction,
161                 },
162         }
163
164         block := new(types.Block)
165         for i, c := range cases {
166                 block.Transactions = c.txs
167                 if err := checkCoinbaseTx(types.MapBlock(block), c.rewards); rootErr(err) != c.err {
168                         t.Errorf("case %d got error %s, want %T", i, err, c.err)
169                 }
170         }
171 }
172
173 func TestValidateBlockHeader(t *testing.T) {
174         parent := &types.BlockHeader{
175                 Version:   1,
176                 Height:    0,
177                 Timestamp: 1523352600000,
178         }
179         parentHash := parent.Hash()
180
181         cases := []struct {
182                 desc   string
183                 block  *bc.Block
184                 parent *types.BlockHeader
185                 err    error
186         }{
187                 {
188                         desc: "dismatch version",
189                         block: &bc.Block{BlockHeader: &bc.BlockHeader{
190                                 Version: 2,
191                         }},
192                         parent: &types.BlockHeader{
193                                 Version: 1,
194                         },
195                         err: errVersionRegression,
196                 },
197                 {
198                         desc: "misordered block height",
199                         block: &bc.Block{BlockHeader: &bc.BlockHeader{
200                                 Version: 1,
201                                 Height:  20,
202                         }},
203                         parent: &types.BlockHeader{
204                                 Version: 1,
205                                 Height:  18,
206                         },
207                         err: errMisorderedBlockHeight,
208                 },
209                 {
210                         desc: "the prev block hash not equals to the hash of parent",
211                         block: &bc.Block{BlockHeader: &bc.BlockHeader{
212                                 Version:         1,
213                                 Height:          20,
214                                 PreviousBlockId: &bc.Hash{V0: 20},
215                         }},
216                         parent: &types.BlockHeader{
217                                 Version:           1,
218                                 Height:            19,
219                                 PreviousBlockHash: bc.Hash{V0: 19},
220                         },
221                         err: errMismatchedBlock,
222                 },
223                 {
224                         desc: "normal block",
225                         block: &bc.Block{
226                                 ID: bc.Hash{V0: 1},
227                                 BlockHeader: &bc.BlockHeader{
228                                         Version:         1,
229                                         Height:          1,
230                                         Timestamp:       1523352601000,
231                                         PreviousBlockId: &parentHash,
232                                 },
233                         },
234                         parent: parent,
235                         err:    nil,
236                 },
237                 {
238                         desc: "version greater than 1",
239                         block: &bc.Block{
240                                 ID: bc.Hash{V0: 1},
241                                 BlockHeader: &bc.BlockHeader{
242                                         Version: 2,
243                                 },
244                         },
245                         parent: &types.BlockHeader{
246                                 Version: 1,
247                         },
248                         err: errVersionRegression,
249                 },
250                 {
251                         desc: "version equals 0",
252                         block: &bc.Block{
253                                 ID: bc.Hash{V0: 1},
254                                 BlockHeader: &bc.BlockHeader{
255                                         Version: 0,
256                                 },
257                         },
258                         parent: &types.BlockHeader{
259                                 Version: 1,
260                         },
261                         err: errVersionRegression,
262                 },
263                 {
264                         desc: "version equals max uint64",
265                         block: &bc.Block{
266                                 ID: bc.Hash{V0: 1},
267                                 BlockHeader: &bc.BlockHeader{
268                                         Version: math.MaxUint64,
269                                 },
270                         },
271                         parent: &types.BlockHeader{
272                                 Version: 1,
273                         },
274                         err: errVersionRegression,
275                 },
276         }
277
278         for i, c := range cases {
279                 if err := ValidateBlockHeader(c.block, c.parent); rootErr(err) != c.err {
280                         t.Errorf("case %d (%s) got error %s, want %s", i, c.desc, err, c.err)
281                 }
282         }
283 }
284
285 func TestValidateBlock(t *testing.T) {
286         cp, _ := vmutil.DefaultCoinbaseProgram()
287         parent := &types.BlockHeader{
288                 Version:           1,
289                 Height:            0,
290                 Timestamp:         1523352600000,
291                 PreviousBlockHash: bc.Hash{V0: 0},
292         }
293         parentHash := parent.Hash()
294         txsRoot := testutil.MustDecodeHash("001e21b9618c503d909c1e0b32bab9ccf80c538b35d49ac7fffcef98eb373b23")
295         txStatusHash := testutil.MustDecodeHash("6978a65b4ee5b6f4914fe5c05000459a803ecf59132604e5d334d64249c5e50a")
296
297         txs := []*bc.Tx{
298                 types.MapTx(&types.TxData{
299                         Version:        1,
300                         SerializedSize: 1,
301                         Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
302                         Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
303                 }),
304         }
305
306         for i := 1; i <= 100; i++ {
307                 txs = append(txs, types.MapTx(&types.TxData{
308                         Version:        1,
309                         SerializedSize: 100000,
310                         Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, bc.Hash{V0: uint64(i)}, *consensus.BTMAssetID, 10000000000, 0, cp)},
311                         Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 9000000000, cp)},
312                 }))
313         }
314
315         cases := []struct {
316                 desc    string
317                 block   *bc.Block
318                 parent  *types.BlockHeader
319                 rewards []state.CoinbaseReward
320                 err     error
321         }{
322                 {
323                         desc: "validate transactions with output amount great than input amount",
324                         block: &bc.Block{
325                                 ID: bc.Hash{V0: 1},
326                                 BlockHeader: &bc.BlockHeader{
327                                         Version:               1,
328                                         Height:                1,
329                                         Timestamp:             1523352601000,
330                                         PreviousBlockId:       &parentHash,
331                                         TransactionsRoot:      &bc.Hash{V0: 16229071813194843118, V1: 7413717724217377663, V2: 10255217553502780716, V3: 17975900656333257644},
332                                         TransactionStatusHash: &txStatusHash,
333                                 },
334                                 Transactions: []*bc.Tx{
335                                         types.MapTx(&types.TxData{
336                                                 Version:        1,
337                                                 SerializedSize: 1,
338                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
339                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
340                                         }),
341                                         types.MapTx(&types.TxData{
342                                                 Version:        1,
343                                                 SerializedSize: 528,
344                                                 Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)},
345                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 100000000, cp)},
346                                         }),
347                                         types.MapTx(&types.TxData{
348                                                 Version:        1,
349                                                 SerializedSize: 528,
350                                                 Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)},
351                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 200000000, cp)},
352                                         }),
353                                 },
354                         },
355                         parent: parent,
356                         err:    ErrGasCalculate,
357                 },
358                 {
359                         desc: "validate block with the total transations used gas is over than the limit",
360                         block: &bc.Block{
361                                 ID: bc.Hash{V0: 1},
362                                 BlockHeader: &bc.BlockHeader{
363                                         Version:               1,
364                                         Height:                1,
365                                         Timestamp:             1523352601000,
366                                         PreviousBlockId:       &parentHash,
367                                         TransactionsRoot:      &bc.Hash{V0: 11799591616144015196, V1: 10485585098288308103, V2: 9819002243760462505, V3: 10203115105872271656},
368                                         TransactionStatusHash: &txStatusHash,
369                                 },
370                                 Transactions: txs,
371                         },
372                         parent: parent,
373                         err:    errOverBlockLimit,
374                 },
375                 {
376                         desc: "The calculated transaction merkel root hash is not equals to the hash of the block header",
377                         block: &bc.Block{
378                                 ID: bc.Hash{V0: 1},
379                                 BlockHeader: &bc.BlockHeader{
380                                         Version:          1,
381                                         Height:           1,
382                                         Timestamp:        1523352601000,
383                                         PreviousBlockId:  &parentHash,
384                                         TransactionsRoot: &bc.Hash{V0: 1},
385                                 },
386                                 Transactions: []*bc.Tx{
387                                         types.MapTx(&types.TxData{
388                                                 Version:        1,
389                                                 SerializedSize: 1,
390                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
391                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
392                                         }),
393                                 },
394                         },
395                         parent: parent,
396                         err:    errMismatchedMerkleRoot,
397                 },
398                 {
399                         desc: "The calculated transaction status merkel root hash is not equals to the hash of the block header",
400                         block: &bc.Block{
401                                 ID: bc.Hash{V0: 1},
402                                 BlockHeader: &bc.BlockHeader{
403                                         Version:               1,
404                                         Height:                1,
405                                         Timestamp:             1523352601000,
406                                         PreviousBlockId:       &parentHash,
407                                         TransactionsRoot:      &bc.Hash{V0: 6294987741126419124, V1: 12520373106916389157, V2: 5040806596198303681, V3: 1151748423853876189},
408                                         TransactionStatusHash: &bc.Hash{V0: 1},
409                                 },
410                                 Transactions: []*bc.Tx{
411                                         types.MapTx(&types.TxData{
412                                                 Version:        1,
413                                                 SerializedSize: 1,
414                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
415                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
416                                         }),
417                                 },
418                         },
419                         parent: parent,
420                         err:    errMismatchedMerkleRoot,
421                 },
422                 {
423                         desc: "the coinbase amount is not equal to the real coinbase outputs",
424                         block: &bc.Block{
425                                 ID: bc.Hash{V0: 1},
426                                 BlockHeader: &bc.BlockHeader{
427                                         Version:               1,
428                                         Height:                1,
429                                         Timestamp:             1523352601000,
430                                         PreviousBlockId:       &parentHash,
431                                         TransactionsRoot:      &txsRoot,
432                                         TransactionStatusHash: &txStatusHash,
433                                 },
434                                 Transactions: []*bc.Tx{
435                                         types.MapTx(&types.TxData{
436                                                 Version:        1,
437                                                 SerializedSize: 1,
438                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
439                                                 Outputs: []*types.TxOutput{
440                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp),
441                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 20000, []byte{0x51}),
442                                                 },
443                                         }),
444                                 },
445                         },
446                         parent: parent,
447                         rewards: []state.CoinbaseReward{
448                                 state.CoinbaseReward{
449                                         Amount:         20000,
450                                         ControlProgram: []byte{0x51},
451                                 },
452                                 state.CoinbaseReward{
453                                         Amount:         10000,
454                                         ControlProgram: []byte{0x52},
455                                 },
456                         },
457                         err: ErrWrongCoinbaseTransaction,
458                 },
459                 {
460                         desc: "the coinbase amount is equal to the real coinbase amount",
461                         block: &bc.Block{
462                                 ID: bc.Hash{V0: 1},
463                                 BlockHeader: &bc.BlockHeader{
464                                         Version:               1,
465                                         Height:                1,
466                                         Timestamp:             1523352601000,
467                                         PreviousBlockId:       &parentHash,
468                                         TransactionsRoot:      &bc.Hash{V0: 16229071813194843118, V1: 7413717724217377663, V2: 10255217553502780716, V3: 17975900656333257644},
469                                         TransactionStatusHash: &txStatusHash,
470                                 },
471                                 Transactions: []*bc.Tx{
472                                         types.MapTx(&types.TxData{
473                                                 Version:        1,
474                                                 SerializedSize: 1,
475                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
476                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
477                                         }),
478                                         types.MapTx(&types.TxData{
479                                                 Version:        1,
480                                                 SerializedSize: 1,
481                                                 Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)},
482                                                 Outputs: []*types.TxOutput{
483                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp),
484                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 100000000, cp),
485                                                 },
486                                         }),
487                                 },
488                         },
489                         parent: parent,
490                         err:    nil,
491                 },
492         }
493
494         for i, c := range cases {
495                 err := ValidateBlock(c.block, c.parent, c.rewards)
496                 if rootErr(err) != c.err {
497                         t.Errorf("case #%d (%s) got error %s, want %s", i, c.desc, err, c.err)
498                 }
499         }
500 }
501
502 // TestSetTransactionStatus verify the transaction status is set correctly (blocktest#1010)
503 func TestSetTransactionStatus(t *testing.T) {
504         cp, _ := vmutil.DefaultCoinbaseProgram()
505         parent := &types.BlockHeader{
506                 Version:           1,
507                 Height:            0,
508                 Timestamp:         1523352600000,
509                 PreviousBlockHash: bc.Hash{V0: 0},
510         }
511         parentHash := parent.Hash()
512
513         block := &bc.Block{
514                 ID: bc.Hash{V0: 1},
515                 BlockHeader: &bc.BlockHeader{
516                         Version:               1,
517                         Height:                1,
518                         Timestamp:             1523352601000,
519                         PreviousBlockId:       &parentHash,
520                         TransactionsRoot:      &bc.Hash{V0: 8176741810667217458, V1: 14830712230021600370, V2: 8921661778795432162, V3: 3391855546006364086},
521                         TransactionStatusHash: &bc.Hash{V0: 8682965660674182538, V1: 8424137560837623409, V2: 6979974817894224946, V3: 4673809519342015041},
522                 },
523                 Transactions: []*bc.Tx{
524                         types.MapTx(&types.TxData{
525                                 Version:        1,
526                                 SerializedSize: 1,
527                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
528                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
529                         }),
530                         types.MapTx(&types.TxData{
531                                 Version:        1,
532                                 SerializedSize: 1,
533                                 Inputs: []*types.TxInput{
534                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
535                                         types.NewSpendInput([][]byte{}, *newHash(8), bc.AssetID{V0: 1}, 1000, 0, []byte{byte(vm.OP_FALSE)}),
536                                 },
537                                 Outputs: []*types.TxOutput{
538                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),
539                                         types.NewIntraChainOutput(bc.AssetID{V0: 1}, 1000, cp),
540                                 },
541                         }),
542                         types.MapTx(&types.TxData{
543                                 Version:        1,
544                                 SerializedSize: 1,
545                                 Inputs: []*types.TxInput{
546                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
547                                 },
548                                 Outputs: []*types.TxOutput{
549                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),
550                                 },
551                         }),
552                 },
553         }
554
555         if err := ValidateBlock(block, parent, []state.CoinbaseReward{}); err != nil {
556                 t.Fatal(err)
557         }
558
559         expectTxStatuses := []bool{false, true, false}
560         txStatuses := block.GetTransactionStatus().VerifyStatus
561         if len(expectTxStatuses) != len(txStatuses) {
562                 t.Error("the size of expect tx status is not equals to size of got tx status")
563         }
564
565         for i, status := range txStatuses {
566                 if expectTxStatuses[i] != status.StatusFail {
567                         t.Errorf("got tx status: %v, expect tx status: %v\n", status.StatusFail, expectTxStatuses[i])
568                 }
569         }
570 }