OSDN Git Service

add free gas (#295)
[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                                         TransactionsRoot:      &bc.Hash{V0: 16229071813194843118, V1: 7413717724217377663, V2: 10255217553502780716, V3: 17975900656333257644},
346                                         TransactionStatusHash: &txStatusHash,
347                                 },
348                                 Transactions: []*bc.Tx{
349                                         types.MapTx(&types.TxData{
350                                                 Version:        1,
351                                                 SerializedSize: 1,
352                                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
353                                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
354                                         }),
355                                         types.MapTx(&types.TxData{
356                                                 Version:        1,
357                                                 SerializedSize: 1,
358                                                 Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp)},
359                                                 Outputs: []*types.TxOutput{
360                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp),
361                                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 100000000, cp),
362                                                 },
363                                         }),
364                                 },
365                         },
366                         parent: parent,
367                         err:    nil,
368                 },
369         }
370
371         for i, c := range cases {
372                 err := ValidateBlock(c.block, c.parent, c.rewards)
373                 if rootErr(err) != c.err {
374                         t.Errorf("case #%d (%s) got error %s, want %s", i, c.desc, err, c.err)
375                 }
376         }
377 }
378
379 func TestGasOverBlockLimit(t *testing.T) {
380         cp, _ := vmutil.DefaultCoinbaseProgram()
381         parent := &types.BlockHeader{
382                 Version:           1,
383                 Height:            0,
384                 Timestamp:         1523352600000,
385                 PreviousBlockHash: bc.Hash{V0: 0},
386         }
387         parentHash := parent.Hash()
388
389         block := &bc.Block{
390                 ID: bc.Hash{V0: 1},
391                 BlockHeader: &bc.BlockHeader{
392                         Version:         1,
393                         Height:          1,
394                         Timestamp:       1523352601000,
395                         PreviousBlockId: &parentHash,
396                 },
397                 Transactions: []*bc.Tx{
398                         types.MapTx(&types.TxData{
399                                 Version:        1,
400                                 SerializedSize: 1,
401                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
402                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
403                         }),
404                 },
405         }
406
407         for i := 0; i < 100; i++ {
408                 block.Transactions = append(block.Transactions, types.MapTx(&types.TxData{
409                         Version:        1,
410                         SerializedSize: 100000,
411                         Inputs: []*types.TxInput{
412                                 types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 10000000000, 0, cp),
413                         },
414                         Outputs: []*types.TxOutput{
415                                 types.NewIntraChainOutput(*consensus.BTMAssetID, 9000000000, cp),
416                         },
417                 }))
418         }
419
420         if err := ValidateBlock(block, parent, []state.CoinbaseReward{}); err != errOverBlockLimit {
421                 t.Errorf("got error %s, want %s", err, errOverBlockLimit)
422         }
423 }
424
425 // TestSetTransactionStatus verify the transaction status is set correctly (blocktest#1010)
426 func TestSetTransactionStatus(t *testing.T) {
427         cp, _ := vmutil.DefaultCoinbaseProgram()
428         parent := &types.BlockHeader{
429                 Version:           1,
430                 Height:            0,
431                 Timestamp:         1523352600000,
432                 PreviousBlockHash: bc.Hash{V0: 0},
433         }
434         parentHash := parent.Hash()
435
436         block := &bc.Block{
437                 ID: bc.Hash{V0: 1},
438                 BlockHeader: &bc.BlockHeader{
439                         Version:               1,
440                         Height:                1,
441                         Timestamp:             1523352601000,
442                         PreviousBlockId:       &parentHash,
443                         TransactionsRoot:      &bc.Hash{V0: 8176741810667217458, V1: 14830712230021600370, V2: 8921661778795432162, V3: 3391855546006364086},
444                         TransactionStatusHash: &bc.Hash{V0: 8682965660674182538, V1: 8424137560837623409, V2: 6979974817894224946, V3: 4673809519342015041},
445                 },
446                 Transactions: []*bc.Tx{
447                         types.MapTx(&types.TxData{
448                                 Version:        1,
449                                 SerializedSize: 1,
450                                 Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
451                                 Outputs:        []*types.TxOutput{types.NewIntraChainOutput(*consensus.BTMAssetID, 0, cp)},
452                         }),
453                         types.MapTx(&types.TxData{
454                                 Version:        1,
455                                 SerializedSize: 1,
456                                 Inputs: []*types.TxInput{
457                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
458                                         types.NewSpendInput([][]byte{}, *newHash(8), bc.AssetID{V0: 1}, 1000, 0, []byte{byte(vm.OP_FALSE)}),
459                                 },
460                                 Outputs: []*types.TxOutput{
461                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),
462                                         types.NewIntraChainOutput(bc.AssetID{V0: 1}, 1000, cp),
463                                 },
464                         }),
465                         types.MapTx(&types.TxData{
466                                 Version:        1,
467                                 SerializedSize: 1,
468                                 Inputs: []*types.TxInput{
469                                         types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp),
470                                 },
471                                 Outputs: []*types.TxOutput{
472                                         types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp),
473                                 },
474                         }),
475                 },
476         }
477
478         if err := ValidateBlock(block, parent, []state.CoinbaseReward{}); err != nil {
479                 t.Fatal(err)
480         }
481
482         expectTxStatuses := []bool{false, true, false}
483         txStatuses := block.GetTransactionStatus().VerifyStatus
484         if len(expectTxStatuses) != len(txStatuses) {
485                 t.Error("the size of expect tx status is not equals to size of got tx status")
486         }
487
488         for i, status := range txStatuses {
489                 if expectTxStatuses[i] != status.StatusFail {
490                         t.Errorf("got tx status: %v, expect tx status: %v\n", status.StatusFail, expectTxStatuses[i])
491                 }
492         }
493 }