OSDN Git Service

Add protocol state module test case (#380)
[bytom/vapor.git] / protocol / state / utxo_view_test.go
1 package state
2
3 import (
4         "testing"
5
6         "github.com/davecgh/go-spew/spew"
7
8         "github.com/vapor/consensus"
9         "github.com/vapor/database/storage"
10         "github.com/vapor/errors"
11         "github.com/vapor/protocol/bc"
12         "github.com/vapor/testutil"
13 )
14
15 var defaultEntry = map[bc.Hash]bc.Entry{
16         bc.Hash{V0: 0}: &bc.IntraChainOutput{
17                 Source: &bc.ValueSource{
18                         Value: &bc.AssetAmount{
19                                 AssetId: &bc.AssetID{V0: 0},
20                         },
21                 },
22         },
23 }
24
25 var coinbaseEntry = map[bc.Hash]bc.Entry{
26         bc.Hash{V0: 0}: &bc.IntraChainOutput{
27                 Source: &bc.ValueSource{
28                         Value: &bc.AssetAmount{
29                                 AssetId: consensus.BTMAssetID,
30                         },
31                 },
32         },
33         bc.Hash{V0: 1}: &bc.IntraChainOutput{
34                 Source: &bc.ValueSource{
35                         Value: &bc.AssetAmount{
36                                 AssetId: consensus.BTMAssetID,
37                                 Amount:  uint64(100),
38                         },
39                 },
40         },
41 }
42
43 var voteEntry = map[bc.Hash]bc.Entry{
44         bc.Hash{V0: 0}: &bc.VoteOutput{
45                 Source: &bc.ValueSource{
46                         Value: &bc.AssetAmount{
47                                 AssetId: &bc.AssetID{V0: 0},
48                         },
49                 },
50         },
51 }
52
53 var gasOnlyTxEntry = map[bc.Hash]bc.Entry{
54         bc.Hash{V1: 0}: &bc.IntraChainOutput{
55                 Source: &bc.ValueSource{
56                         Value: &bc.AssetAmount{
57                                 AssetId: consensus.BTMAssetID,
58                         },
59                 },
60         },
61         bc.Hash{V1: 1}: &bc.IntraChainOutput{
62                 Source: &bc.ValueSource{
63                         Value: &bc.AssetAmount{
64                                 AssetId: &bc.AssetID{V0: 999},
65                         },
66                 },
67         },
68 }
69
70 func TestApplyBlock(t *testing.T) {
71         cases := []struct {
72                 block     *bc.Block
73                 inputView *UtxoViewpoint
74                 fetchView *UtxoViewpoint
75                 gasOnlyTx bool
76                 err       bool
77         }{
78                 {
79                         // can't find prevout in tx entries
80                         block: &bc.Block{
81                                 BlockHeader: &bc.BlockHeader{
82                                         TransactionStatus: bc.NewTransactionStatus(),
83                                 },
84                                 Transactions: []*bc.Tx{
85                                         &bc.Tx{
86                                                 SpentOutputIDs: []bc.Hash{
87                                                         bc.Hash{V0: 1},
88                                                 },
89                                                 Entries: defaultEntry,
90                                         },
91                                 },
92                         },
93                         inputView: &UtxoViewpoint{
94                                 Entries: map[bc.Hash]*storage.UtxoEntry{
95                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
96                                 },
97                         },
98                         fetchView: NewUtxoViewpoint(),
99                         err:       true,
100                 },
101                 {
102                         block: &bc.Block{
103                                 BlockHeader: &bc.BlockHeader{
104                                         TransactionStatus: bc.NewTransactionStatus(),
105                                 },
106                                 Transactions: []*bc.Tx{
107                                         &bc.Tx{
108                                                 SpentOutputIDs: []bc.Hash{
109                                                         bc.Hash{V0: 0},
110                                                 },
111                                                 Entries: defaultEntry,
112                                         },
113                                 },
114                         },
115                         inputView: NewUtxoViewpoint(),
116                         fetchView: NewUtxoViewpoint(),
117                         err:       true,
118                 },
119                 {
120                         block: &bc.Block{
121                                 BlockHeader: &bc.BlockHeader{
122                                         TransactionStatus: bc.NewTransactionStatus(),
123                                 },
124                                 Transactions: []*bc.Tx{
125                                         &bc.Tx{
126                                                 SpentOutputIDs: []bc.Hash{
127                                                         bc.Hash{V0: 0},
128                                                 },
129                                                 Entries: defaultEntry,
130                                         },
131                                 },
132                         },
133                         inputView: &UtxoViewpoint{
134                                 Entries: map[bc.Hash]*storage.UtxoEntry{
135                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
136                                 },
137                         },
138                         err: true,
139                 },
140                 {
141                         block: &bc.Block{
142                                 BlockHeader: &bc.BlockHeader{
143                                         TransactionStatus: bc.NewTransactionStatus(),
144                                 },
145                                 Transactions: []*bc.Tx{
146                                         &bc.Tx{
147                                                 TxHeader: &bc.TxHeader{
148                                                         ResultIds: []*bc.Hash{},
149                                                 },
150                                                 SpentOutputIDs: []bc.Hash{
151                                                         bc.Hash{V0: 0},
152                                                 },
153                                                 Entries: defaultEntry,
154                                         },
155                                 },
156                         },
157                         inputView: &UtxoViewpoint{
158                                 Entries: map[bc.Hash]*storage.UtxoEntry{
159                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
160                                 },
161                         },
162                         fetchView: &UtxoViewpoint{
163                                 Entries: map[bc.Hash]*storage.UtxoEntry{
164                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
165                                 },
166                         },
167                         err: false,
168                 },
169                 {
170                         block: &bc.Block{
171                                 BlockHeader: &bc.BlockHeader{
172                                         Height:            consensus.MainNetParams.CoinbasePendingBlockNumber + 1,
173                                         TransactionStatus: bc.NewTransactionStatus(),
174                                 },
175                                 Transactions: []*bc.Tx{
176                                         &bc.Tx{
177                                                 TxHeader: &bc.TxHeader{
178                                                         ResultIds: []*bc.Hash{},
179                                                 },
180                                                 SpentOutputIDs: []bc.Hash{
181                                                         bc.Hash{V0: 0},
182                                                 },
183                                                 Entries: defaultEntry,
184                                         },
185                                 },
186                         },
187                         inputView: &UtxoViewpoint{
188                                 Entries: map[bc.Hash]*storage.UtxoEntry{
189                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
190                                 },
191                         },
192                         fetchView: &UtxoViewpoint{
193                                 Entries: map[bc.Hash]*storage.UtxoEntry{
194                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, true),
195                                 },
196                         },
197                         err: false,
198                 },
199                 {
200                         block: &bc.Block{
201                                 BlockHeader: &bc.BlockHeader{
202                                         Height:            0,
203                                         TransactionStatus: bc.NewTransactionStatus(),
204                                 },
205                                 Transactions: []*bc.Tx{
206                                         &bc.Tx{
207                                                 TxHeader: &bc.TxHeader{
208                                                         ResultIds: []*bc.Hash{},
209                                                 },
210                                                 SpentOutputIDs: []bc.Hash{
211                                                         bc.Hash{V0: 0},
212                                                 },
213                                                 Entries: defaultEntry,
214                                         },
215                                 },
216                         },
217                         inputView: &UtxoViewpoint{
218                                 Entries: map[bc.Hash]*storage.UtxoEntry{
219                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
220                                 },
221                         },
222                         fetchView: &UtxoViewpoint{
223                                 Entries: map[bc.Hash]*storage.UtxoEntry{
224                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, true),
225                                 },
226                         },
227                         err: true,
228                 },
229                 {
230                         // output will be store
231                         block: &bc.Block{
232                                 BlockHeader: &bc.BlockHeader{
233                                         TransactionStatus: bc.NewTransactionStatus(),
234                                 },
235                                 Transactions: []*bc.Tx{
236                                         &bc.Tx{
237                                                 TxHeader: &bc.TxHeader{
238                                                         ResultIds: []*bc.Hash{
239                                                                 &bc.Hash{V0: 0},
240                                                                 &bc.Hash{V0: 1},
241                                                         },
242                                                 },
243                                                 SpentOutputIDs: []bc.Hash{},
244                                                 Entries:        coinbaseEntry,
245                                         },
246                                 },
247                         },
248                         inputView: NewUtxoViewpoint(),
249                         fetchView: &UtxoViewpoint{
250                                 Entries: map[bc.Hash]*storage.UtxoEntry{
251                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
252                                 },
253                         },
254                         err: false,
255                 },
256                 {
257                         // apply gas only tx, non-btm asset spent input will not be spent
258                         block: &bc.Block{
259                                 BlockHeader: &bc.BlockHeader{
260                                         TransactionStatus: bc.NewTransactionStatus(),
261                                 },
262                                 Transactions: []*bc.Tx{
263                                         &bc.Tx{
264                                                 TxHeader: &bc.TxHeader{
265                                                         ResultIds: []*bc.Hash{},
266                                                 },
267                                                 SpentOutputIDs: []bc.Hash{
268                                                         bc.Hash{V1: 0},
269                                                         bc.Hash{V1: 1},
270                                                 },
271                                                 Entries: gasOnlyTxEntry,
272                                         },
273                                 },
274                         },
275                         inputView: &UtxoViewpoint{
276                                 Entries: map[bc.Hash]*storage.UtxoEntry{
277                                         bc.Hash{V1: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
278                                         bc.Hash{V1: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
279                                 },
280                         },
281                         fetchView: &UtxoViewpoint{
282                                 Entries: map[bc.Hash]*storage.UtxoEntry{
283                                         bc.Hash{V1: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
284                                         bc.Hash{V1: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
285                                 },
286                         },
287                         gasOnlyTx: true,
288                         err:       false,
289                 },
290                 {
291                         // apply gas only tx, non-btm asset spent output will not be store
292                         block: &bc.Block{
293                                 BlockHeader: &bc.BlockHeader{
294                                         TransactionStatus: bc.NewTransactionStatus(),
295                                 },
296                                 Transactions: []*bc.Tx{
297                                         &bc.Tx{
298                                                 TxHeader: &bc.TxHeader{
299                                                         ResultIds: []*bc.Hash{
300                                                                 &bc.Hash{V1: 0},
301                                                                 &bc.Hash{V1: 1},
302                                                         },
303                                                 },
304                                                 SpentOutputIDs: []bc.Hash{},
305                                                 Entries:        gasOnlyTxEntry,
306                                         },
307                                 },
308                         },
309                         inputView: NewUtxoViewpoint(),
310                         fetchView: NewUtxoViewpoint(),
311                         gasOnlyTx: true,
312                         err:       false,
313                 },
314                 {
315                         block: &bc.Block{
316                                 BlockHeader: &bc.BlockHeader{
317                                         TransactionStatus: bc.NewTransactionStatus(),
318                                 },
319                                 Transactions: []*bc.Tx{
320                                         &bc.Tx{
321                                                 TxHeader: &bc.TxHeader{
322                                                         ResultIds: []*bc.Hash{},
323                                                 },
324                                                 SpentOutputIDs: []bc.Hash{
325                                                         bc.Hash{V0: 0},
326                                                 },
327                                                 Entries: voteEntry,
328                                         },
329                                 },
330                         },
331                         inputView: &UtxoViewpoint{
332                                 Entries: map[bc.Hash]*storage.UtxoEntry{
333                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
334                                 },
335                         },
336                         fetchView: &UtxoViewpoint{
337                                 Entries: map[bc.Hash]*storage.UtxoEntry{
338                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
339                                 },
340                         },
341                         err: true,
342                 },
343                 {
344                         block: &bc.Block{
345                                 BlockHeader: &bc.BlockHeader{
346                                         Height:            consensus.MainNetParams.VotePendingBlockNumber + 1,
347                                         TransactionStatus: bc.NewTransactionStatus(),
348                                 },
349                                 Transactions: []*bc.Tx{
350                                         &bc.Tx{
351                                                 TxHeader: &bc.TxHeader{
352                                                         ResultIds: []*bc.Hash{},
353                                                 },
354                                                 SpentOutputIDs: []bc.Hash{
355                                                         bc.Hash{V0: 0},
356                                                 },
357                                                 Entries: voteEntry,
358                                         },
359                                 },
360                         },
361                         inputView: &UtxoViewpoint{
362                                 Entries: map[bc.Hash]*storage.UtxoEntry{
363                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.VoteUTXOType, 1, false),
364                                 },
365                         },
366                         fetchView: &UtxoViewpoint{
367                                 Entries: map[bc.Hash]*storage.UtxoEntry{
368                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.VoteUTXOType, 1, true),
369                                 },
370                         },
371                         err: false,
372                 },
373         }
374
375         for i, c := range cases {
376                 c.block.TransactionStatus.SetStatus(0, c.gasOnlyTx)
377                 if err := c.inputView.ApplyBlock(c.block, c.block.TransactionStatus); c.err != (err != nil) {
378                         t.Errorf("case #%d want err = %v, get err = %v", i, c.err, err)
379                 }
380                 if c.err {
381                         continue
382                 }
383                 if !testutil.DeepEqual(c.inputView, c.fetchView) {
384                         t.Errorf("test case %d, want %v, get %v", i, c.fetchView, c.inputView)
385                 }
386         }
387 }
388
389 func TestDetachBlock(t *testing.T) {
390         cases := []struct {
391                 block     *bc.Block
392                 inputView *UtxoViewpoint
393                 fetchView *UtxoViewpoint
394                 gasOnlyTx bool
395                 err       bool
396         }{
397                 {
398                         block: &bc.Block{
399                                 BlockHeader: &bc.BlockHeader{
400                                         TransactionStatus: bc.NewTransactionStatus(),
401                                 },
402                                 Transactions: []*bc.Tx{
403                                         &bc.Tx{
404                                                 TxHeader: &bc.TxHeader{
405                                                         ResultIds: []*bc.Hash{},
406                                                 },
407                                                 SpentOutputIDs: []bc.Hash{
408                                                         bc.Hash{V0: 0},
409                                                 },
410                                                 Entries: defaultEntry,
411                                         },
412                                 },
413                         },
414                         inputView: NewUtxoViewpoint(),
415                         fetchView: &UtxoViewpoint{
416                                 Entries: map[bc.Hash]*storage.UtxoEntry{
417                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
418                                 },
419                         },
420                         err: false,
421                 },
422                 {
423                         block: &bc.Block{
424                                 BlockHeader: &bc.BlockHeader{
425                                         TransactionStatus: bc.NewTransactionStatus(),
426                                 },
427                                 Transactions: []*bc.Tx{
428                                         &bc.Tx{
429                                                 TxHeader: &bc.TxHeader{
430                                                         ResultIds: []*bc.Hash{
431                                                                 &bc.Hash{V0: 0},
432                                                                 &bc.Hash{V0: 1},
433                                                         },
434                                                 },
435                                                 SpentOutputIDs: []bc.Hash{},
436                                                 Entries:        coinbaseEntry,
437                                         },
438                                 },
439                         },
440                         inputView: &UtxoViewpoint{
441                                 Entries: map[bc.Hash]*storage.UtxoEntry{
442                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
443                                 },
444                         },
445                         fetchView: &UtxoViewpoint{
446                                 Entries: map[bc.Hash]*storage.UtxoEntry{
447                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
448                                 },
449                         },
450                         err: false,
451                 },
452                 {
453                         block: &bc.Block{
454                                 BlockHeader: &bc.BlockHeader{
455                                         TransactionStatus: bc.NewTransactionStatus(),
456                                 },
457                                 Transactions: []*bc.Tx{
458                                         &bc.Tx{
459                                                 TxHeader: &bc.TxHeader{
460                                                         ResultIds: []*bc.Hash{},
461                                                 },
462                                                 SpentOutputIDs: []bc.Hash{
463                                                         bc.Hash{V0: 0},
464                                                 },
465                                                 Entries: defaultEntry,
466                                         },
467                                 },
468                         },
469                         inputView: &UtxoViewpoint{
470                                 Entries: map[bc.Hash]*storage.UtxoEntry{
471                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
472                                 },
473                         },
474                         err: true,
475                 },
476                 {
477                         block: &bc.Block{
478                                 BlockHeader: &bc.BlockHeader{
479                                         TransactionStatus: bc.NewTransactionStatus(),
480                                 },
481                                 Transactions: []*bc.Tx{
482                                         &bc.Tx{
483                                                 TxHeader: &bc.TxHeader{
484                                                         ResultIds: []*bc.Hash{},
485                                                 },
486                                                 SpentOutputIDs: []bc.Hash{
487                                                         bc.Hash{V0: 0},
488                                                 },
489                                                 Entries: defaultEntry,
490                                         },
491                                 },
492                         },
493                         inputView: &UtxoViewpoint{
494                                 Entries: map[bc.Hash]*storage.UtxoEntry{
495                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
496                                 },
497                         },
498                         fetchView: &UtxoViewpoint{
499                                 Entries: map[bc.Hash]*storage.UtxoEntry{
500                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
501                                 },
502                         },
503                         err: false,
504                 },
505                 {
506                         block: &bc.Block{
507                                 BlockHeader: &bc.BlockHeader{
508                                         TransactionStatus: bc.NewTransactionStatus(),
509                                 },
510                                 Transactions: []*bc.Tx{
511                                         &bc.Tx{
512                                                 TxHeader: &bc.TxHeader{
513                                                         ResultIds: []*bc.Hash{},
514                                                 },
515                                                 SpentOutputIDs: []bc.Hash{
516                                                         bc.Hash{V1: 0},
517                                                         bc.Hash{V1: 1},
518                                                 },
519                                                 Entries: gasOnlyTxEntry,
520                                         },
521                                 },
522                         },
523                         inputView: &UtxoViewpoint{
524                                 Entries: map[bc.Hash]*storage.UtxoEntry{
525                                         bc.Hash{V1: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
526                                         bc.Hash{V1: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
527                                 },
528                         },
529                         fetchView: &UtxoViewpoint{
530                                 Entries: map[bc.Hash]*storage.UtxoEntry{
531                                         bc.Hash{V1: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
532                                         bc.Hash{V1: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
533                                 },
534                         },
535                         gasOnlyTx: true,
536                         err:       false,
537                 },
538                 {
539                         block: &bc.Block{
540                                 BlockHeader: &bc.BlockHeader{
541                                         TransactionStatus: bc.NewTransactionStatus(),
542                                 },
543                                 Transactions: []*bc.Tx{
544                                         &bc.Tx{
545                                                 TxHeader: &bc.TxHeader{
546                                                         ResultIds: []*bc.Hash{
547                                                                 &bc.Hash{V1: 0},
548                                                                 &bc.Hash{V1: 1},
549                                                         },
550                                                 },
551                                                 SpentOutputIDs: []bc.Hash{},
552                                                 Entries:        gasOnlyTxEntry,
553                                         },
554                                 },
555                         },
556                         inputView: NewUtxoViewpoint(),
557                         fetchView: NewUtxoViewpoint(),
558                         gasOnlyTx: true,
559                         err:       false,
560                 },
561                 {
562                         block: &bc.Block{
563                                 BlockHeader: &bc.BlockHeader{
564                                         TransactionStatus: bc.NewTransactionStatus(),
565                                 },
566                                 Transactions: []*bc.Tx{
567                                         &bc.Tx{
568                                                 TxHeader: &bc.TxHeader{
569                                                         ResultIds: []*bc.Hash{},
570                                                 },
571                                                 SpentOutputIDs: []bc.Hash{
572                                                         bc.Hash{V0: 0},
573                                                 },
574                                                 Entries: voteEntry,
575                                         },
576                                 },
577                         },
578                         inputView: NewUtxoViewpoint(),
579                         fetchView: &UtxoViewpoint{
580                                 Entries: map[bc.Hash]*storage.UtxoEntry{
581                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
582                                 },
583                         },
584                         err: false,
585                 },
586         }
587
588         for i, c := range cases {
589                 c.block.TransactionStatus.SetStatus(0, c.gasOnlyTx)
590                 if err := c.inputView.DetachBlock(c.block, c.block.TransactionStatus); c.err != (err != nil) {
591                         t.Errorf("case %d want err = %v, get err = %v", i, c.err, err)
592                 }
593                 if c.err {
594                         continue
595                 }
596                 if !testutil.DeepEqual(c.inputView, c.fetchView) {
597                         t.Errorf("test case %d, want %v, get %v", i, c.fetchView, c.inputView)
598                 }
599         }
600 }
601
602 func TestApplyCrossChainUTXO(t *testing.T) {
603         cases := []struct {
604                 desc         string
605                 block        *bc.Block
606                 tx           *bc.Tx
607                 prevUTXOView *UtxoViewpoint
608                 postUTXOView *UtxoViewpoint
609                 err          error
610         }{
611                 {
612                         desc: "normal test",
613                         block: &bc.Block{
614                                 BlockHeader: &bc.BlockHeader{
615                                         Height: 100,
616                                 },
617                         },
618                         tx: &bc.Tx{
619                                 TxHeader: &bc.TxHeader{
620                                         ResultIds: []*bc.Hash{},
621                                 },
622                                 MainchainOutputIDs: []bc.Hash{
623                                         bc.Hash{V0: 0},
624                                 },
625                                 Entries: voteEntry,
626                         },
627                         prevUTXOView: &UtxoViewpoint{
628                                 Entries: map[bc.Hash]*storage.UtxoEntry{
629                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, false),
630                                 },
631                         },
632                         postUTXOView: &UtxoViewpoint{
633                                 Entries: map[bc.Hash]*storage.UtxoEntry{
634                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CrosschainUTXOType, 100, true),
635                                 },
636                         },
637                         err: nil,
638                 },
639                 {
640                         desc: "test failed to find mainchain output entry",
641                         block: &bc.Block{
642                                 BlockHeader: &bc.BlockHeader{},
643                         },
644                         tx: &bc.Tx{
645                                 TxHeader: &bc.TxHeader{
646                                         ResultIds: []*bc.Hash{},
647                                 },
648                                 MainchainOutputIDs: []bc.Hash{
649                                         bc.Hash{V0: 0},
650                                 },
651                                 Entries: voteEntry,
652                         },
653                         prevUTXOView: &UtxoViewpoint{
654                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
655                         },
656                         postUTXOView: &UtxoViewpoint{
657                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
658                         },
659                         err: errors.New("fail to find mainchain output entry"),
660                 },
661                 {
662                         desc: "test mainchain output has been spent",
663                         block: &bc.Block{
664                                 BlockHeader: &bc.BlockHeader{},
665                         },
666                         tx: &bc.Tx{
667                                 TxHeader: &bc.TxHeader{
668                                         ResultIds: []*bc.Hash{},
669                                 },
670                                 MainchainOutputIDs: []bc.Hash{
671                                         bc.Hash{V0: 0},
672                                 },
673                                 Entries: voteEntry,
674                         },
675                         prevUTXOView: &UtxoViewpoint{
676                                 Entries: map[bc.Hash]*storage.UtxoEntry{
677                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, true),
678                                 },
679                         },
680                         postUTXOView: &UtxoViewpoint{
681                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
682                         },
683                         err: errors.New("mainchain output has been spent"),
684                 },
685         }
686
687         for i, c := range cases {
688                 if err := c.prevUTXOView.applyCrossChainUtxo(c.block, c.tx); err != nil {
689                         if err.Error() != c.err.Error() {
690                                 t.Errorf("test case #%d want err = %v, got err = %v", i, c.err, err)
691                         }
692                         continue
693                 }
694
695                 if !testutil.DeepEqual(c.prevUTXOView, c.postUTXOView) {
696                         t.Errorf("test case #%d, want %v, got %v", i, c.postUTXOView, c.prevUTXOView)
697                 }
698         }
699 }
700
701 func TestApplyOutputUTXO(t *testing.T) {
702         cases := []struct {
703                 desc         string
704                 block        *bc.Block
705                 tx           *bc.Tx
706                 statusFail   bool
707                 prevUTXOView *UtxoViewpoint
708                 postUTXOView *UtxoViewpoint
709                 err          error
710         }{
711                 {
712                         desc: "normal test IntraChainOutput,VoteOutput,Retirement",
713                         block: &bc.Block{
714                                 BlockHeader: &bc.BlockHeader{},
715                         },
716                         tx: &bc.Tx{
717                                 TxHeader: &bc.TxHeader{
718                                         ResultIds: []*bc.Hash{&bc.Hash{V0: 0}, &bc.Hash{V0: 1}, &bc.Hash{V0: 2}},
719                                 },
720                                 Entries: map[bc.Hash]bc.Entry{
721                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
722                                                 Source: &bc.ValueSource{
723                                                         Value: &bc.AssetAmount{
724                                                                 AssetId: &bc.AssetID{V0: 0},
725                                                                 Amount:  100,
726                                                         },
727                                                 },
728                                         },
729                                         bc.Hash{V0: 1}: &bc.VoteOutput{
730                                                 Source: &bc.ValueSource{
731                                                         Value: &bc.AssetAmount{
732                                                                 AssetId: &bc.AssetID{V0: 1},
733                                                         },
734                                                 },
735                                         },
736                                         bc.Hash{V0: 2}: &bc.Retirement{
737                                                 Source: &bc.ValueSource{
738                                                         Value: &bc.AssetAmount{
739                                                                 AssetId: &bc.AssetID{V0: 1},
740                                                         },
741                                                 },
742                                         },
743                                 },
744                         },
745                         prevUTXOView: &UtxoViewpoint{
746                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
747                         },
748                         postUTXOView: &UtxoViewpoint{
749                                 Entries: map[bc.Hash]*storage.UtxoEntry{
750                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
751                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
752                                 },
753                         },
754                         err: nil,
755                 },
756                 {
757                         desc: "test statusFail",
758                         block: &bc.Block{
759                                 BlockHeader: &bc.BlockHeader{},
760                         },
761                         tx: &bc.Tx{
762                                 TxHeader: &bc.TxHeader{
763                                         ResultIds: []*bc.Hash{&bc.Hash{V0: 0}, &bc.Hash{V0: 1}, &bc.Hash{V0: 2}},
764                                 },
765                                 Entries: map[bc.Hash]bc.Entry{
766                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
767                                                 Source: &bc.ValueSource{
768                                                         Value: &bc.AssetAmount{
769                                                                 AssetId: &bc.AssetID{V0: 0},
770                                                                 Amount:  100,
771                                                         },
772                                                 },
773                                         },
774                                         bc.Hash{V0: 1}: &bc.VoteOutput{
775                                                 Source: &bc.ValueSource{
776                                                         Value: &bc.AssetAmount{
777                                                                 AssetId: consensus.BTMAssetID,
778                                                         },
779                                                 },
780                                         },
781                                         bc.Hash{V0: 2}: &bc.Retirement{
782                                                 Source: &bc.ValueSource{
783                                                         Value: &bc.AssetAmount{
784                                                                 AssetId: &bc.AssetID{V0: 1},
785                                                         },
786                                                 },
787                                         },
788                                 },
789                         },
790                         statusFail: true,
791                         prevUTXOView: &UtxoViewpoint{
792                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
793                         },
794                         postUTXOView: &UtxoViewpoint{
795                                 Entries: map[bc.Hash]*storage.UtxoEntry{
796                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
797                                 },
798                         },
799                         err: nil,
800                 },
801                 {
802                         desc: "test failed on found id from tx entry",
803                         block: &bc.Block{
804                                 BlockHeader: &bc.BlockHeader{},
805                         },
806                         tx: &bc.Tx{
807                                 TxHeader: &bc.TxHeader{
808                                         ResultIds: []*bc.Hash{&bc.Hash{V0: 0}, &bc.Hash{V0: 1}, &bc.Hash{V0: 2}},
809                                 },
810                                 Entries: map[bc.Hash]bc.Entry{
811                                         bc.Hash{V0: 1}: &bc.VoteOutput{
812                                                 Source: &bc.ValueSource{
813                                                         Value: &bc.AssetAmount{
814                                                                 AssetId: consensus.BTMAssetID,
815                                                         },
816                                                 },
817                                         },
818                                         bc.Hash{V0: 2}: &bc.Retirement{
819                                                 Source: &bc.ValueSource{
820                                                         Value: &bc.AssetAmount{
821                                                                 AssetId: &bc.AssetID{V0: 1},
822                                                         },
823                                                 },
824                                         },
825                                 },
826                         },
827                         statusFail: false,
828                         prevUTXOView: &UtxoViewpoint{
829                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
830                         },
831                         postUTXOView: &UtxoViewpoint{
832                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
833                         },
834                         err: bc.ErrMissingEntry,
835                 },
836         }
837
838         for i, c := range cases {
839                 if err := c.prevUTXOView.applyOutputUtxo(c.block, c.tx, c.statusFail); err != nil {
840                         if errors.Root(err) != errors.Root(c.err) {
841                                 t.Errorf("test case #%d want err = %v, got err = %v", i, c.err.Error(), err.Error())
842                         }
843                         continue
844                 }
845
846                 if !testutil.DeepEqual(c.prevUTXOView, c.postUTXOView) {
847                         t.Errorf("test case #%d, want %v, got %v", i, c.postUTXOView, c.prevUTXOView)
848                 }
849         }
850 }
851
852 func TestApplySpendUTXO(t *testing.T) {
853         cases := []struct {
854                 desc         string
855                 block        *bc.Block
856                 tx           *bc.Tx
857                 statusFail   bool
858                 prevUTXOView *UtxoViewpoint
859                 postUTXOView *UtxoViewpoint
860                 err          error
861         }{
862                 {
863                         desc: "normal test",
864                         block: &bc.Block{
865                                 BlockHeader: &bc.BlockHeader{
866                                         Height: consensus.ActiveNetParams.VotePendingBlockNumber,
867                                 },
868                         },
869                         tx: &bc.Tx{
870                                 TxHeader:       &bc.TxHeader{},
871                                 SpentOutputIDs: []bc.Hash{{V0: 0}, {V0: 1}, {V0: 2}},
872                                 Entries: map[bc.Hash]bc.Entry{
873                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
874                                                 Source: &bc.ValueSource{
875                                                         Value: &bc.AssetAmount{
876                                                                 AssetId: &bc.AssetID{V0: 0},
877                                                                 Amount:  100,
878                                                         },
879                                                 },
880                                         },
881                                         bc.Hash{V0: 1}: &bc.VoteOutput{
882                                                 Source: &bc.ValueSource{
883                                                         Value: &bc.AssetAmount{
884                                                                 AssetId: &bc.AssetID{V0: 1},
885                                                         },
886                                                 },
887                                         },
888                                         bc.Hash{V0: 2}: &bc.IntraChainOutput{
889                                                 Source: &bc.ValueSource{
890                                                         Value: &bc.AssetAmount{
891                                                                 AssetId: consensus.BTMAssetID,
892                                                                 Amount:  100,
893                                                         },
894                                                 },
895                                         },
896                                 },
897                         },
898                         prevUTXOView: &UtxoViewpoint{
899                                 Entries: map[bc.Hash]*storage.UtxoEntry{
900                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
901                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
902                                         bc.Hash{V0: 2}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
903                                 },
904                         },
905                         postUTXOView: &UtxoViewpoint{
906                                 Entries: map[bc.Hash]*storage.UtxoEntry{
907                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
908                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
909                                         bc.Hash{V0: 2}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, true),
910                                 },
911                         },
912                         err: nil,
913                 },
914                 {
915                         desc: "test coinbase is not ready for use",
916                         block: &bc.Block{
917                                 BlockHeader: &bc.BlockHeader{
918                                         Height: consensus.ActiveNetParams.CoinbasePendingBlockNumber - 1,
919                                 },
920                         },
921                         tx: &bc.Tx{
922                                 TxHeader:       &bc.TxHeader{},
923                                 SpentOutputIDs: []bc.Hash{{V0: 1}, {V0: 2}},
924                                 Entries: map[bc.Hash]bc.Entry{
925                                         bc.Hash{V0: 1}: &bc.VoteOutput{
926                                                 Source: &bc.ValueSource{
927                                                         Value: &bc.AssetAmount{
928                                                                 AssetId: &bc.AssetID{V0: 1},
929                                                         },
930                                                 },
931                                         },
932                                         bc.Hash{V0: 2}: &bc.IntraChainOutput{
933                                                 Source: &bc.ValueSource{
934                                                         Value: &bc.AssetAmount{
935                                                                 AssetId: consensus.BTMAssetID,
936                                                                 Amount:  100,
937                                                         },
938                                                 },
939                                         },
940                                 },
941                         },
942                         prevUTXOView: &UtxoViewpoint{
943                                 Entries: map[bc.Hash]*storage.UtxoEntry{
944                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
945                                         bc.Hash{V0: 2}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
946                                 },
947                         },
948                         postUTXOView: &UtxoViewpoint{
949                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
950                         },
951                         err: errors.New("coinbase utxo is not ready for use"),
952                 },
953                 {
954                         desc: "test Coin is  within the voting lock time",
955                         block: &bc.Block{
956                                 BlockHeader: &bc.BlockHeader{
957                                         Height: consensus.ActiveNetParams.VotePendingBlockNumber - 1,
958                                 },
959                         },
960                         tx: &bc.Tx{
961                                 TxHeader:       &bc.TxHeader{},
962                                 SpentOutputIDs: []bc.Hash{{V0: 1}, {V0: 2}},
963                                 Entries: map[bc.Hash]bc.Entry{
964                                         bc.Hash{V0: 1}: &bc.VoteOutput{
965                                                 Source: &bc.ValueSource{
966                                                         Value: &bc.AssetAmount{
967                                                                 AssetId: &bc.AssetID{V0: 1},
968                                                         },
969                                                 },
970                                         },
971                                         bc.Hash{V0: 2}: &bc.IntraChainOutput{
972                                                 Source: &bc.ValueSource{
973                                                         Value: &bc.AssetAmount{
974                                                                 AssetId: consensus.BTMAssetID,
975                                                                 Amount:  100,
976                                                         },
977                                                 },
978                                         },
979                                 },
980                         },
981                         prevUTXOView: &UtxoViewpoint{
982                                 Entries: map[bc.Hash]*storage.UtxoEntry{
983                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
984                                         bc.Hash{V0: 2}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
985                                 },
986                         },
987                         postUTXOView: &UtxoViewpoint{
988                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
989                         },
990                         err: errors.New("Coin is  within the voting lock time"),
991                 },
992                 {
993                         desc: "test utxo has been spent",
994                         block: &bc.Block{
995                                 BlockHeader: &bc.BlockHeader{
996                                         Height: 0,
997                                 },
998                         },
999                         tx: &bc.Tx{
1000                                 TxHeader:       &bc.TxHeader{},
1001                                 SpentOutputIDs: []bc.Hash{{V0: 0}, {V0: 1}, {V0: 2}},
1002                                 Entries: map[bc.Hash]bc.Entry{
1003                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
1004                                                 Source: &bc.ValueSource{
1005                                                         Value: &bc.AssetAmount{
1006                                                                 AssetId: &bc.AssetID{V0: 0},
1007                                                                 Amount:  100,
1008                                                         },
1009                                                 },
1010                                         },
1011                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1012                                                 Source: &bc.ValueSource{
1013                                                         Value: &bc.AssetAmount{
1014                                                                 AssetId: &bc.AssetID{V0: 1},
1015                                                         },
1016                                                 },
1017                                         },
1018                                         bc.Hash{V0: 2}: &bc.IntraChainOutput{
1019                                                 Source: &bc.ValueSource{
1020                                                         Value: &bc.AssetAmount{
1021                                                                 AssetId: consensus.BTMAssetID,
1022                                                                 Amount:  100,
1023                                                         },
1024                                                 },
1025                                         },
1026                                 },
1027                         },
1028                         prevUTXOView: &UtxoViewpoint{
1029                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1030                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
1031                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
1032                                         bc.Hash{V0: 2}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
1033                                 },
1034                         },
1035                         postUTXOView: &UtxoViewpoint{
1036                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1037                         },
1038                         err: errors.New("utxo has been spent"),
1039                 },
1040                 {
1041                         desc: "test faild to find utxo entry",
1042                         block: &bc.Block{
1043                                 BlockHeader: &bc.BlockHeader{
1044                                         Height: 0,
1045                                 },
1046                         },
1047                         tx: &bc.Tx{
1048                                 TxHeader:       &bc.TxHeader{},
1049                                 SpentOutputIDs: []bc.Hash{{V0: 0}, {V0: 1}, {V0: 2}},
1050                                 Entries: map[bc.Hash]bc.Entry{
1051                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
1052                                                 Source: &bc.ValueSource{
1053                                                         Value: &bc.AssetAmount{
1054                                                                 AssetId: &bc.AssetID{V0: 0},
1055                                                                 Amount:  100,
1056                                                         },
1057                                                 },
1058                                         },
1059                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1060                                                 Source: &bc.ValueSource{
1061                                                         Value: &bc.AssetAmount{
1062                                                                 AssetId: &bc.AssetID{V0: 1},
1063                                                         },
1064                                                 },
1065                                         },
1066                                         bc.Hash{V0: 2}: &bc.IntraChainOutput{
1067                                                 Source: &bc.ValueSource{
1068                                                         Value: &bc.AssetAmount{
1069                                                                 AssetId: consensus.BTMAssetID,
1070                                                                 Amount:  100,
1071                                                         },
1072                                                 },
1073                                         },
1074                                 },
1075                         },
1076                         prevUTXOView: &UtxoViewpoint{
1077                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1078                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
1079                                         bc.Hash{V0: 2}: storage.NewUtxoEntry(storage.CoinbaseUTXOType, 0, false),
1080                                 },
1081                         },
1082                         postUTXOView: &UtxoViewpoint{
1083                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1084                         },
1085                         err: errors.New("fail to find utxo entry"),
1086                 },
1087         }
1088
1089         for i, c := range cases {
1090                 if err := c.prevUTXOView.applySpendUtxo(c.block, c.tx, c.statusFail); err != nil {
1091                         if err.Error() != c.err.Error() {
1092                                 t.Errorf("test case #%d want err = %v, got err = %v", i, err.Error(), c.err.Error())
1093                         }
1094                         continue
1095                 }
1096
1097                 if !testutil.DeepEqual(c.prevUTXOView, c.postUTXOView) {
1098                         t.Errorf("test case #%d, want %v, got %v", i, spew.Sdump(c.postUTXOView), spew.Sdump(c.prevUTXOView))
1099                 }
1100         }
1101 }
1102
1103 func TestDetachCrossChainUTXO(t *testing.T) {
1104         cases := []struct {
1105                 desc         string
1106                 tx           *bc.Tx
1107                 prevUTXOView *UtxoViewpoint
1108                 postUTXOView *UtxoViewpoint
1109                 err          error
1110         }{
1111                 {
1112                         desc: "normal test",
1113                         tx: &bc.Tx{
1114                                 TxHeader: &bc.TxHeader{
1115                                         ResultIds: []*bc.Hash{},
1116                                 },
1117                                 MainchainOutputIDs: []bc.Hash{
1118                                         bc.Hash{V0: 0},
1119                                 },
1120                                 Entries: voteEntry,
1121                         },
1122                         prevUTXOView: &UtxoViewpoint{
1123                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1124                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, true),
1125                                 },
1126                         },
1127                         postUTXOView: &UtxoViewpoint{
1128                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1129                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, false),
1130                                 },
1131                         },
1132                         err: nil,
1133                 },
1134                 {
1135                         desc: "test failed to find mainchain output entry",
1136                         tx: &bc.Tx{
1137                                 TxHeader: &bc.TxHeader{
1138                                         ResultIds: []*bc.Hash{},
1139                                 },
1140                                 MainchainOutputIDs: []bc.Hash{
1141                                         bc.Hash{V0: 0},
1142                                 },
1143                                 Entries: voteEntry,
1144                         },
1145                         prevUTXOView: &UtxoViewpoint{
1146                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1147                         },
1148                         postUTXOView: &UtxoViewpoint{
1149                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1150                         },
1151                         err: errors.New("fail to find mainchain output entry"),
1152                 },
1153                 {
1154                         desc: "test revert output is unspent",
1155                         tx: &bc.Tx{
1156                                 TxHeader: &bc.TxHeader{
1157                                         ResultIds: []*bc.Hash{},
1158                                 },
1159                                 MainchainOutputIDs: []bc.Hash{
1160                                         bc.Hash{V0: 0},
1161                                 },
1162                                 Entries: voteEntry,
1163                         },
1164                         prevUTXOView: &UtxoViewpoint{
1165                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1166                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.CrosschainUTXOType, 0, false),
1167                                 },
1168                         },
1169                         postUTXOView: &UtxoViewpoint{
1170                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1171                         },
1172                         err: errors.New("mainchain output is unspent"),
1173                 },
1174         }
1175
1176         for i, c := range cases {
1177                 if err := c.prevUTXOView.detachCrossChainUtxo(c.tx); err != nil {
1178                         if err.Error() != c.err.Error() {
1179                                 t.Errorf("test case #%d want err = %v, got err = %v", i, c.err, err)
1180                         }
1181                         continue
1182                 }
1183
1184                 if !testutil.DeepEqual(c.prevUTXOView, c.postUTXOView) {
1185                         t.Errorf("test case #%d, want %v, got %v", i, c.postUTXOView, c.prevUTXOView)
1186                 }
1187         }
1188 }
1189
1190 func TestDetachOutputUTXO(t *testing.T) {
1191         cases := []struct {
1192                 desc         string
1193                 tx           *bc.Tx
1194                 statusFail   bool
1195                 prevUTXOView *UtxoViewpoint
1196                 postUTXOView *UtxoViewpoint
1197                 err          error
1198         }{
1199                 {
1200                         desc: "normal test IntraChainOutput,VoteOutput",
1201                         tx: &bc.Tx{
1202                                 TxHeader: &bc.TxHeader{
1203                                         ResultIds: []*bc.Hash{&bc.Hash{V0: 0}, &bc.Hash{V0: 1}, &bc.Hash{V0: 2}},
1204                                 },
1205                                 Entries: map[bc.Hash]bc.Entry{
1206                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
1207                                                 Source: &bc.ValueSource{
1208                                                         Value: &bc.AssetAmount{
1209                                                                 AssetId: &bc.AssetID{V0: 0},
1210                                                                 Amount:  100,
1211                                                         },
1212                                                 },
1213                                         },
1214                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1215                                                 Source: &bc.ValueSource{
1216                                                         Value: &bc.AssetAmount{
1217                                                                 AssetId: &bc.AssetID{V0: 1},
1218                                                         },
1219                                                 },
1220                                         },
1221                                         bc.Hash{V0: 2}: &bc.Retirement{
1222                                                 Source: &bc.ValueSource{
1223                                                         Value: &bc.AssetAmount{
1224                                                                 AssetId: &bc.AssetID{V0: 1},
1225                                                         },
1226                                                 },
1227                                         },
1228                                 },
1229                         },
1230                         prevUTXOView: &UtxoViewpoint{
1231                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1232                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
1233                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
1234                                 },
1235                         },
1236                         postUTXOView: &UtxoViewpoint{
1237                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1238                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
1239                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
1240                                 },
1241                         },
1242                         err: nil,
1243                 },
1244                 {
1245                         desc: "test statusFail",
1246                         tx: &bc.Tx{
1247                                 TxHeader: &bc.TxHeader{
1248                                         ResultIds: []*bc.Hash{&bc.Hash{V0: 0}, &bc.Hash{V0: 1}},
1249                                 },
1250                                 Entries: map[bc.Hash]bc.Entry{
1251                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
1252                                                 Source: &bc.ValueSource{
1253                                                         Value: &bc.AssetAmount{
1254                                                                 AssetId: &bc.AssetID{V0: 0},
1255                                                                 Amount:  100,
1256                                                         },
1257                                                 },
1258                                         },
1259                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1260                                                 Source: &bc.ValueSource{
1261                                                         Value: &bc.AssetAmount{
1262                                                                 AssetId: consensus.BTMAssetID,
1263                                                         },
1264                                                 },
1265                                         },
1266                                 },
1267                         },
1268                         statusFail: true,
1269                         prevUTXOView: &UtxoViewpoint{
1270                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1271                         },
1272                         postUTXOView: &UtxoViewpoint{
1273                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1274                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
1275                                 },
1276                         },
1277                         err: nil,
1278                 },
1279                 {
1280                         desc: "test failed on found id from tx entry",
1281                         tx: &bc.Tx{
1282                                 TxHeader: &bc.TxHeader{
1283                                         ResultIds: []*bc.Hash{&bc.Hash{V0: 0}, &bc.Hash{V0: 1}, &bc.Hash{V0: 2}},
1284                                 },
1285                                 Entries: map[bc.Hash]bc.Entry{
1286                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1287                                                 Source: &bc.ValueSource{
1288                                                         Value: &bc.AssetAmount{
1289                                                                 AssetId: consensus.BTMAssetID,
1290                                                         },
1291                                                 },
1292                                         },
1293                                         bc.Hash{V0: 2}: &bc.Retirement{
1294                                                 Source: &bc.ValueSource{
1295                                                         Value: &bc.AssetAmount{
1296                                                                 AssetId: &bc.AssetID{V0: 1},
1297                                                         },
1298                                                 },
1299                                         },
1300                                 },
1301                         },
1302                         statusFail: false,
1303                         prevUTXOView: &UtxoViewpoint{
1304                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1305                         },
1306                         postUTXOView: &UtxoViewpoint{
1307                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1308                         },
1309                         err: bc.ErrMissingEntry,
1310                 },
1311         }
1312
1313         for i, c := range cases {
1314                 if err := c.prevUTXOView.detachOutputUtxo(c.tx, c.statusFail); err != nil {
1315                         if errors.Root(err) != errors.Root(c.err) {
1316                                 t.Errorf("test case #%d want err = %v, got err = %v", i, c.err.Error(), err.Error())
1317                         }
1318                         continue
1319                 }
1320
1321                 if !testutil.DeepEqual(c.prevUTXOView, c.postUTXOView) {
1322                         t.Errorf("test case #%d, want %v, got %v", i, c.postUTXOView, c.prevUTXOView)
1323                 }
1324         }
1325 }
1326
1327 func TestDetachSpendUTXO(t *testing.T) {
1328         cases := []struct {
1329                 desc         string
1330                 tx           *bc.Tx
1331                 statusFail   bool
1332                 prevUTXOView *UtxoViewpoint
1333                 postUTXOView *UtxoViewpoint
1334                 err          error
1335         }{
1336                 {
1337                         desc: "normal test",
1338                         tx: &bc.Tx{
1339                                 TxHeader:       &bc.TxHeader{},
1340                                 SpentOutputIDs: []bc.Hash{{V0: 0}, {V0: 1}},
1341                                 Entries: map[bc.Hash]bc.Entry{
1342                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
1343                                                 Source: &bc.ValueSource{
1344                                                         Value: &bc.AssetAmount{
1345                                                                 AssetId: &bc.AssetID{V0: 0},
1346                                                                 Amount:  100,
1347                                                         },
1348                                                 },
1349                                         },
1350                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1351                                                 Source: &bc.ValueSource{
1352                                                         Value: &bc.AssetAmount{
1353                                                                 AssetId: &bc.AssetID{V0: 1},
1354                                                         },
1355                                                 },
1356                                         },
1357                                 },
1358                         },
1359                         prevUTXOView: &UtxoViewpoint{
1360                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1361                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, true),
1362                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
1363                                 },
1364                         },
1365                         postUTXOView: &UtxoViewpoint{
1366                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1367                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
1368                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, false),
1369                                 },
1370                         },
1371                         err: nil,
1372                 },
1373                 {
1374                         desc: "test utxo has been spent",
1375                         tx: &bc.Tx{
1376                                 TxHeader:       &bc.TxHeader{},
1377                                 SpentOutputIDs: []bc.Hash{{V0: 0}, {V0: 1}, {V0: 2}},
1378                                 Entries: map[bc.Hash]bc.Entry{
1379                                         bc.Hash{V0: 0}: &bc.IntraChainOutput{
1380                                                 Source: &bc.ValueSource{
1381                                                         Value: &bc.AssetAmount{
1382                                                                 AssetId: consensus.BTMAssetID,
1383                                                                 Amount:  100,
1384                                                         },
1385                                                 },
1386                                         },
1387                                         bc.Hash{V0: 1}: &bc.VoteOutput{
1388                                                 Source: &bc.ValueSource{
1389                                                         Value: &bc.AssetAmount{
1390                                                                 AssetId: &bc.AssetID{V0: 1},
1391                                                         },
1392                                                 },
1393                                         },
1394                                         bc.Hash{V0: 2}: &bc.IntraChainOutput{
1395                                                 Source: &bc.ValueSource{
1396                                                         Value: &bc.AssetAmount{
1397                                                                 AssetId: consensus.BTMAssetID,
1398                                                                 Amount:  100,
1399                                                         },
1400                                                 },
1401                                         },
1402                                 },
1403                         },
1404                         prevUTXOView: &UtxoViewpoint{
1405                                 Entries: map[bc.Hash]*storage.UtxoEntry{
1406                                         bc.Hash{V0: 0}: storage.NewUtxoEntry(storage.NormalUTXOType, 0, false),
1407                                         bc.Hash{V0: 1}: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true),
1408                                 },
1409                         },
1410                         postUTXOView: &UtxoViewpoint{
1411                                 Entries: map[bc.Hash]*storage.UtxoEntry{},
1412                         },
1413                         err: errors.New("try to revert an unspent utxo"),
1414                 },
1415         }
1416
1417         for i, c := range cases {
1418                 if err := c.prevUTXOView.detachSpendUtxo(c.tx, c.statusFail); err != nil {
1419                         if err.Error() != c.err.Error() {
1420                                 t.Errorf("test case #%d want err = %v, got err = %v", i, err.Error(), c.err.Error())
1421                         }
1422                         continue
1423                 }
1424
1425                 if !testutil.DeepEqual(c.prevUTXOView, c.postUTXOView) {
1426                         t.Errorf("test case #%d, want %v, got %v", i, spew.Sdump(c.postUTXOView), spew.Sdump(c.prevUTXOView))
1427                 }
1428         }
1429 }