OSDN Git Service

6749963e63c1bfaf5e5f837933e099e4f6cabdce
[bytom/vapor.git] / protocol / validation / block_test.go
1 package validation
2
3 import (
4         "math"
5         "testing"
6         "time"
7
8         "github.com/vapor/consensus"
9         "github.com/vapor/protocol/bc"
10         "github.com/vapor/protocol/bc/types"
11         "github.com/vapor/protocol/state"
12         "github.com/vapor/protocol/vm"
13         "github.com/vapor/protocol/vm/vmutil"
14         "github.com/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, 5000, []byte{0x51}),
96                                         },
97                                 }),
98                         },
99                         rewards: []state.CoinbaseReward{
100                                 state.CoinbaseReward{
101                                         Amount:         5000,
102                                         ControlProgram: []byte{0x51},
103                                 },
104                         },
105                         err: nil,
106                 },
107                 {
108                         desc:    "wrong coinbase transaction",
109                         txs:     []*types.Tx{},
110                         rewards: []state.CoinbaseReward{},
111                         err:     ErrWrongCoinbaseTransaction,
112                 },
113         }
114
115         block := new(types.Block)
116         for i, c := range cases {
117                 block.Transactions = c.txs
118                 if err := checkCoinbaseTx(types.MapBlock(block), c.rewards); rootErr(err) != c.err {
119                         t.Errorf("case %d got error %s, want %T", i, err, c.err)
120                 }
121         }
122 }
123
124 func TestValidateBlockHeader(t *testing.T) {
125         parent := &types.BlockHeader{
126                 Version:   1,
127                 Height:    0,
128                 Timestamp: 1523352600000,
129         }
130         parentHash := parent.Hash()
131
132         cases := []struct {
133                 desc   string
134                 block  *bc.Block
135                 parent *types.BlockHeader
136                 err    error
137         }{
138                 {
139                         desc: "dismatch version",
140                         block: &bc.Block{BlockHeader: &bc.BlockHeader{
141                                 Version: 2,
142                         }},
143                         parent: &types.BlockHeader{
144                                 Version: 1,
145                         },
146                         err: errVersionRegression,
147                 },
148                 {
149                         desc: "misordered block height",
150                         block: &bc.Block{BlockHeader: &bc.BlockHeader{
151                                 Version: 1,
152                                 Height:  20,
153                         }},
154                         parent: &types.BlockHeader{
155                                 Version: 1,
156                                 Height:  18,
157                         },
158                         err: errMisorderedBlockHeight,
159                 },
160                 {
161                         desc: "the prev block hash not equals to the hash of parent",
162                         block: &bc.Block{BlockHeader: &bc.BlockHeader{
163                                 Version:         1,
164                                 Height:          20,
165                                 PreviousBlockId: &bc.Hash{V0: 20},
166                         }},
167                         parent: &types.BlockHeader{
168                                 Version:           1,
169                                 Height:            19,
170                                 PreviousBlockHash: bc.Hash{V0: 19},
171                         },
172                         err: errMismatchedBlock,
173                 },
174                 {
175                         desc: "normal block",
176                         block: &bc.Block{
177                                 ID: bc.Hash{V0: 1},
178                                 BlockHeader: &bc.BlockHeader{
179                                         Version:         1,
180                                         Height:          1,
181                                         Timestamp:       1523352601000,
182                                         PreviousBlockId: &parentHash,
183                                 },
184                         },
185                         parent: parent,
186                         err:    nil,
187                 },
188                 {
189                         desc: "version greater than 1",
190                         block: &bc.Block{
191                                 ID: bc.Hash{V0: 1},
192                                 BlockHeader: &bc.BlockHeader{
193                                         Version: 2,
194                                 },
195                         },
196                         parent: &types.BlockHeader{
197                                 Version: 1,
198                         },
199                         err: errVersionRegression,
200                 },
201                 {
202                         desc: "version equals 0",
203                         block: &bc.Block{
204                                 ID: bc.Hash{V0: 1},
205                                 BlockHeader: &bc.BlockHeader{
206                                         Version: 0,
207                                 },
208                         },
209                         parent: &types.BlockHeader{
210                                 Version: 1,
211                         },
212                         err: errVersionRegression,
213                 },
214                 {
215                         desc: "version equals max uint64",
216                         block: &bc.Block{
217                                 ID: bc.Hash{V0: 1},
218                                 BlockHeader: &bc.BlockHeader{
219                                         Version: math.MaxUint64,
220                                 },
221                         },
222                         parent: &types.BlockHeader{
223                                 Version: 1,
224                         },
225                         err: errVersionRegression,
226                 },
227         }
228
229         for i, c := range cases {
230                 if err := ValidateBlockHeader(c.block, c.parent); rootErr(err) != c.err {
231                         t.Errorf("case %d (%s) got error %s, want %s", i, c.desc, err, c.err)
232                 }
233         }
234 }
235
236 func TestValidateBlock(t *testing.T) {
237         cp, _ := vmutil.DefaultCoinbaseProgram()
238         parent := &types.BlockHeader{
239                 Version:           1,
240                 Height:            0,
241                 Timestamp:         1523352600000,
242                 PreviousBlockHash: bc.Hash{V0: 0},
243         }
244         parentHash := parent.Hash()
245         txsRoot := testutil.MustDecodeHash("001e21b9618c503d909c1e0b32bab9ccf80c538b35d49ac7fffcef98eb373b23")
246         txStatusHash := testutil.MustDecodeHash("6978a65b4ee5b6f4914fe5c05000459a803ecf59132604e5d334d64249c5e50a")
247
248         cases := []struct {
249                 desc    string
250                 block   *bc.Block
251                 parent  *types.BlockHeader
252                 rewards []state.CoinbaseReward
253                 err     error
254         }{
255                 {
256                         desc: "The calculated transaction merkel root hash is not equals to the hash of the block header",
257                         block: &bc.Block{
258                                 ID: bc.Hash{V0: 1},
259                                 BlockHeader: &bc.BlockHeader{
260                                         Version:          1,
261                                         Height:           1,
262                                         Timestamp:        1523352601000,
263                                         PreviousBlockId:  &parentHash,
264                                         TransactionsRoot: &bc.Hash{V0: 1},
265                                 },
266                                 Transactions: []*bc.Tx{
267                                         types.MapTx(&types.TxData{
268                                                 Version:        1,
269                                                 SerializedSize: 1,
270                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
271                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
272                                         }),
273                                 },
274                         },
275                         parent: parent,
276                         err:    errMismatchedMerkleRoot,
277                 },
278                 {
279                         desc: "The calculated transaction status merkel root hash is not equals to the hash of the block header",
280                         block: &bc.Block{
281                                 ID: bc.Hash{V0: 1},
282                                 BlockHeader: &bc.BlockHeader{
283                                         Version:               1,
284                                         Height:                1,
285                                         Timestamp:             1523352601000,
286                                         PreviousBlockId:       &parentHash,
287                                         TransactionsRoot:      &bc.Hash{V0: 6294987741126419124, V1: 12520373106916389157, V2: 5040806596198303681, V3: 1151748423853876189},
288                                         TransactionStatusHash: &bc.Hash{V0: 1},
289                                 },
290                                 Transactions: []*bc.Tx{
291                                         types.MapTx(&types.TxData{
292                                                 Version:        1,
293                                                 SerializedSize: 1,
294                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
295                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
296                                         }),
297                                 },
298                         },
299                         parent: parent,
300                         err:    errMismatchedMerkleRoot,
301                 },
302                 {
303                         desc: "the coinbase amount is not equal to the real coinbase amount",
304                         block: &bc.Block{
305                                 ID: bc.Hash{V0: 1},
306                                 BlockHeader: &bc.BlockHeader{
307                                         Version:               1,
308                                         Height:                1,
309                                         Timestamp:             1523352601000,
310                                         PreviousBlockId:       &parentHash,
311                                         TransactionsRoot:      &txsRoot,
312                                         TransactionStatusHash: &txStatusHash,
313                                 },
314                                 Transactions: []*bc.Tx{
315                                         types.MapTx(&types.TxData{
316                                                 Version:        1,
317                                                 SerializedSize: 1,
318                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
319                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 41250000000, cp)},
320                                         }),
321                                         types.MapTx(&types.TxData{
322                                                 Version:        1,
323                                                 SerializedSize: 1,
324                                                 Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)},
325                                                 Outputs: []*types.TxOutput{
326                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp),
327                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 90000000, cp),
328                                                 },
329                                         }),
330                                 },
331                         },
332                         parent:  parent,
333                         rewards: []state.CoinbaseReward{},
334                         err:     ErrWrongCoinbaseTransaction,
335                 },
336                 {
337                         desc: "the coinbase amount is less than the real coinbase amount",
338                         block: &bc.Block{
339                                 ID: bc.Hash{V0: 1},
340                                 BlockHeader: &bc.BlockHeader{
341                                         Version:         1,
342                                         Height:          1,
343                                         Timestamp:       1523352601000,
344                                         PreviousBlockId: &parentHash,
345                                 },
346                                 Transactions: []*bc.Tx{
347                                         types.MapTx(&types.TxData{
348                                                 Version:        1,
349                                                 SerializedSize: 1,
350                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
351                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
352                                         }),
353                                         types.MapTx(&types.TxData{
354                                                 Version:        1,
355                                                 SerializedSize: 1,
356                                                 Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)},
357                                                 Outputs: []*types.TxOutput{
358                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp),
359                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 100000000, cp),
360                                                 },
361                                         }),
362                                 },
363                         },
364                         parent: parent,
365                         err:    vm.ErrRunLimitExceeded,
366                 },
367         }
368
369         for i, c := range cases {
370                 err := ValidateBlock(c.block, c.parent, c.rewards)
371                 if rootErr(err) != c.err {
372                         t.Errorf("case #%d (%s) got error %s, want %s", i, c.desc, err, c.err)
373                 }
374         }
375 }
376
377 func TestGasOverBlockLimit(t *testing.T) {
378         cp, _ := vmutil.DefaultCoinbaseProgram()
379         parent := &types.BlockHeader{
380                 Version:           1,
381                 Height:            0,
382                 Timestamp:         1523352600000,
383                 PreviousBlockHash: bc.Hash{V0: 0},
384         }
385         parentHash := parent.Hash()
386
387         block := &bc.Block{
388                 ID: bc.Hash{V0: 1},
389                 BlockHeader: &bc.BlockHeader{
390                         Version:         1,
391                         Height:          1,
392                         Timestamp:       1523352601000,
393                         PreviousBlockId: &parentHash,
394                 },
395                 Transactions: []*bc.Tx{
396                         types.MapTx(&types.TxData{
397                                 Version:        1,
398                                 SerializedSize: 1,
399                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
400                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
401                         }),
402                 },
403         }
404
405         for i := 0; i < 100; i++ {
406                 block.Transactions = append(block.Transactions, types.MapTx(&types.TxData{
407                         Version:        1,
408                         SerializedSize: 100000,
409                         Inputs: []*types.TxInput{
410                                 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 10000000000, 0, cp),
411                         },
412                         Outputs: []*types.TxOutput{
413                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 9000000000, cp),
414                         },
415                 }))
416         }
417
418         if err := ValidateBlock(block, parent, []state.CoinbaseReward{}); err != errOverBlockLimit {
419                 t.Errorf("got error %s, want %s", err, errOverBlockLimit)
420         }
421 }
422
423 // TestSetTransactionStatus verify the transaction status is set correctly (blocktest#1010)
424 func TestSetTransactionStatus(t *testing.T) {
425         cp, _ := vmutil.DefaultCoinbaseProgram()
426         parent := &types.BlockHeader{
427                 Version:           1,
428                 Height:            0,
429                 Timestamp:         1523352600000,
430                 PreviousBlockHash: bc.Hash{V0: 0},
431         }
432         parentHash := parent.Hash()
433
434         block := &bc.Block{
435                 ID: bc.Hash{V0: 1},
436                 BlockHeader: &bc.BlockHeader{
437                         Version:               1,
438                         Height:                1,
439                         Timestamp:             1523352601000,
440                         PreviousBlockId:       &parentHash,
441                         TransactionsRoot:      &bc.Hash{V0: 8176741810667217458, V1: 14830712230021600370, V2: 8921661778795432162, V3: 3391855546006364086},
442                         TransactionStatusHash: &bc.Hash{V0: 8682965660674182538, V1: 8424137560837623409, V2: 6979974817894224946, V3: 4673809519342015041},
443                 },
444                 Transactions: []*bc.Tx{
445                         types.MapTx(&types.TxData{
446                                 Version:        1,
447                                 SerializedSize: 1,
448                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
449                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
450                         }),
451                         types.MapTx(&types.TxData{
452                                 Version:        1,
453                                 SerializedSize: 1,
454                                 Inputs: []*types.TxInput{
455                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
456                                         types.NewSpendInput([][]byte{}, *newHash(8), bc.AssetID{V0: 1}, 1000, 0, []byte{byte(vm.OP_FALSE)}),
457                                 },
458                                 Outputs: []*types.TxOutput{
459                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),
460                                         types.NewIntraChainOutput(bc.AssetID{V0: 1}, 1000, cp),
461                                 },
462                         }),
463                         types.MapTx(&types.TxData{
464                                 Version:        1,
465                                 SerializedSize: 1,
466                                 Inputs: []*types.TxInput{
467                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
468                                 },
469                                 Outputs: []*types.TxOutput{
470                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),
471                                 },
472                         }),
473                 },
474         }
475
476         if err := ValidateBlock(block, parent, []state.CoinbaseReward{}); err != nil {
477                 t.Fatal(err)
478         }
479
480         expectTxStatuses := []bool{false, true, false}
481         txStatuses := block.GetTransactionStatus().VerifyStatus
482         if len(expectTxStatuses) != len(txStatuses) {
483                 t.Error("the size of expect tx status is not equals to size of got tx status")
484         }
485
486         for i, status := range txStatuses {
487                 if expectTxStatuses[i] != status.StatusFail {
488                         t.Errorf("got tx status: %v, expect tx status: %v\n", status.StatusFail, expectTxStatuses[i])
489                 }
490         }
491 }