OSDN Git Service

50b2d77e76ebb4d88ba9b9a7f973897b8a606b2e
[bytom/vapor.git] / account / utxo_keeper_test.go
1 package account
2
3 import (
4         "encoding/json"
5         "io/ioutil"
6         "os"
7         "testing"
8         "time"
9
10         "github.com/golang/groupcache/lru"
11         "github.com/vapor/blockchain/txbuilder"
12         "github.com/vapor/crypto/ed25519/chainkd"
13         dbm "github.com/vapor/database/leveldb"
14         "github.com/vapor/protocol/bc"
15         "github.com/vapor/testutil"
16 )
17
18 func TestAddUnconfirmedUtxo(t *testing.T) {
19         cases := []struct {
20                 before   utxoKeeper
21                 after    utxoKeeper
22                 addUtxos []*UTXO
23         }{
24                 {
25                         before: utxoKeeper{
26                                 unconfirmed: map[bc.Hash]*UTXO{},
27                         },
28                         after: utxoKeeper{
29                                 unconfirmed: map[bc.Hash]*UTXO{},
30                         },
31                         addUtxos: []*UTXO{},
32                 },
33                 {
34                         before: utxoKeeper{
35                                 unconfirmed: map[bc.Hash]*UTXO{},
36                         },
37                         after: utxoKeeper{
38                                 unconfirmed: map[bc.Hash]*UTXO{
39                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
40                                 },
41                         },
42                         addUtxos: []*UTXO{
43                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
44                         },
45                 },
46                 {
47                         before: utxoKeeper{
48                                 unconfirmed: map[bc.Hash]*UTXO{
49                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
50                                 },
51                         },
52                         after: utxoKeeper{
53                                 unconfirmed: map[bc.Hash]*UTXO{
54                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
55                                 },
56                         },
57                         addUtxos: []*UTXO{
58                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
59                         },
60                 },
61                 {
62                         before: utxoKeeper{
63                                 unconfirmed: map[bc.Hash]*UTXO{
64                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
65                                 },
66                         },
67                         after: utxoKeeper{
68                                 unconfirmed: map[bc.Hash]*UTXO{
69                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
70                                         bc.NewHash([32]byte{0x02}): &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
71                                         bc.NewHash([32]byte{0x03}): &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
72                                 },
73                         },
74                         addUtxos: []*UTXO{
75                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
76                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
77                         },
78                 },
79         }
80
81         for i, c := range cases {
82                 c.before.AddUnconfirmedUtxo(c.addUtxos)
83                 if !testutil.DeepEqual(c.before, c.after) {
84                         t.Errorf("case %d: got %v want %v", i, c.before, c.after)
85                 }
86         }
87 }
88
89 func TestCancel(t *testing.T) {
90         cases := []struct {
91                 before    utxoKeeper
92                 after     utxoKeeper
93                 cancelRid uint64
94         }{
95                 {
96                         before: utxoKeeper{
97                                 reserved:     map[bc.Hash]uint64{},
98                                 reservations: map[uint64]*reservation{},
99                         },
100                         after: utxoKeeper{
101                                 reserved:     map[bc.Hash]uint64{},
102                                 reservations: map[uint64]*reservation{},
103                         },
104                         cancelRid: 0,
105                 },
106                 {
107                         before: utxoKeeper{
108                                 reserved: map[bc.Hash]uint64{
109                                         bc.NewHash([32]byte{0x01}): 1,
110                                 },
111                                 reservations: map[uint64]*reservation{
112                                         1: &reservation{
113                                                 id: 1,
114                                                 utxos: []*UTXO{
115                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
116                                                 },
117                                                 change: 9527,
118                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
119                                         },
120                                 },
121                         },
122                         after: utxoKeeper{
123                                 reserved:     map[bc.Hash]uint64{},
124                                 reservations: map[uint64]*reservation{},
125                         },
126                         cancelRid: 1,
127                 },
128                 {
129                         before: utxoKeeper{
130                                 reserved: map[bc.Hash]uint64{
131                                         bc.NewHash([32]byte{0x01}): 1,
132                                 },
133                                 reservations: map[uint64]*reservation{
134                                         1: &reservation{
135                                                 id: 1,
136                                                 utxos: []*UTXO{
137                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
138                                                 },
139                                                 change: 9527,
140                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
141                                         },
142                                 },
143                         },
144                         after: utxoKeeper{
145                                 reserved: map[bc.Hash]uint64{
146                                         bc.NewHash([32]byte{0x01}): 1,
147                                 },
148                                 reservations: map[uint64]*reservation{
149                                         1: &reservation{
150                                                 id: 1,
151                                                 utxos: []*UTXO{
152                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
153                                                 },
154                                                 change: 9527,
155                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
156                                         },
157                                 },
158                         },
159                         cancelRid: 2,
160                 },
161                 {
162                         before: utxoKeeper{
163                                 reserved: map[bc.Hash]uint64{
164                                         bc.NewHash([32]byte{0x01}): 1,
165                                         bc.NewHash([32]byte{0x02}): 3,
166                                         bc.NewHash([32]byte{0x03}): 3,
167                                         bc.NewHash([32]byte{0x04}): 3,
168                                 },
169                                 reservations: map[uint64]*reservation{
170                                         1: &reservation{
171                                                 id: 1,
172                                                 utxos: []*UTXO{
173                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
174                                                 },
175                                                 change: 9527,
176                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
177                                         },
178                                         3: &reservation{
179                                                 id: 3,
180                                                 utxos: []*UTXO{
181                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
182                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
183                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x04})},
184                                                 },
185                                                 change: 9528,
186                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
187                                         },
188                                 },
189                         },
190                         after: utxoKeeper{
191                                 reserved: map[bc.Hash]uint64{
192                                         bc.NewHash([32]byte{0x01}): 1,
193                                 },
194                                 reservations: map[uint64]*reservation{
195                                         1: &reservation{
196                                                 id: 1,
197                                                 utxos: []*UTXO{
198                                                         &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
199                                                 },
200                                                 change: 9527,
201                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
202                                         },
203                                 },
204                         },
205                         cancelRid: 3,
206                 },
207         }
208
209         for i, c := range cases {
210                 c.before.cancel(c.cancelRid)
211                 if !testutil.DeepEqual(c.before, c.after) {
212                         t.Errorf("case %d: got %v want %v", i, c.before, c.after)
213                 }
214         }
215 }
216
217 func TestRemoveUnconfirmedUtxo(t *testing.T) {
218         cases := []struct {
219                 before      utxoKeeper
220                 after       utxoKeeper
221                 removeUtxos []*bc.Hash
222         }{
223                 {
224                         before: utxoKeeper{
225                                 unconfirmed: map[bc.Hash]*UTXO{},
226                         },
227                         after: utxoKeeper{
228                                 unconfirmed: map[bc.Hash]*UTXO{},
229                         },
230                         removeUtxos: []*bc.Hash{},
231                 },
232                 {
233                         before: utxoKeeper{
234                                 unconfirmed: map[bc.Hash]*UTXO{
235                                         bc.Hash{V0: 1}: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
236                                 },
237                         },
238                         after: utxoKeeper{
239                                 unconfirmed: map[bc.Hash]*UTXO{},
240                         },
241                         removeUtxos: []*bc.Hash{
242                                 &bc.Hash{V0: 1},
243                         },
244                 },
245                 {
246                         before: utxoKeeper{
247                                 unconfirmed: map[bc.Hash]*UTXO{
248                                         bc.Hash{V0: 1}: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
249                                         bc.Hash{V0: 2}: &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
250                                         bc.Hash{V0: 3}: &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
251                                         bc.Hash{V0: 4}: &UTXO{OutputID: bc.NewHash([32]byte{0x04})},
252                                         bc.Hash{V0: 5}: &UTXO{OutputID: bc.NewHash([32]byte{0x05})},
253                                 },
254                         },
255                         after: utxoKeeper{
256                                 unconfirmed: map[bc.Hash]*UTXO{
257                                         bc.Hash{V0: 2}: &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
258                                         bc.Hash{V0: 4}: &UTXO{OutputID: bc.NewHash([32]byte{0x04})},
259                                 },
260                         },
261                         removeUtxos: []*bc.Hash{
262                                 &bc.Hash{V0: 1},
263                                 &bc.Hash{V0: 3},
264                                 &bc.Hash{V0: 5},
265                         },
266                 },
267         }
268
269         for i, c := range cases {
270                 c.before.RemoveUnconfirmedUtxo(c.removeUtxos)
271                 if !testutil.DeepEqual(c.before, c.after) {
272                         t.Errorf("case %d: got %v want %v", i, c.before, c.after)
273                 }
274         }
275 }
276
277 func TestReserve(t *testing.T) {
278         currentHeight := func() uint64 { return 9527 }
279         testDB := dbm.NewDB("testdb", "leveldb", "temp")
280         defer func() {
281                 testDB.Close()
282                 os.RemoveAll("temp")
283         }()
284
285         accountStore := newMockAccountStore(testDB)
286
287         cases := []struct {
288                 before        utxoKeeper
289                 after         utxoKeeper
290                 err           error
291                 reserveAmount uint64
292                 exp           time.Time
293                 vote          []byte
294         }{
295                 {
296                         before: utxoKeeper{
297                                 store:         accountStore,
298                                 currentHeight: currentHeight,
299                                 reserved:      map[bc.Hash]uint64{},
300                                 reservations:  map[uint64]*reservation{},
301                         },
302                         after: utxoKeeper{
303                                 store:         accountStore,
304                                 currentHeight: currentHeight,
305                                 reserved:      map[bc.Hash]uint64{},
306                                 reservations:  map[uint64]*reservation{},
307                         },
308                         reserveAmount: 1,
309                         err:           ErrInsufficient,
310                 },
311                 {
312                         before: utxoKeeper{
313                                 store:         accountStore,
314                                 currentHeight: currentHeight,
315                                 unconfirmed: map[bc.Hash]*UTXO{
316                                         bc.NewHash([32]byte{0x01}): &UTXO{
317                                                 OutputID:  bc.NewHash([32]byte{0x01}),
318                                                 AccountID: "testAccount",
319                                                 Amount:    3,
320                                         },
321                                 },
322                                 reserved:     map[bc.Hash]uint64{},
323                                 reservations: map[uint64]*reservation{},
324                         },
325                         after: utxoKeeper{
326                                 store:         accountStore,
327                                 currentHeight: currentHeight,
328                                 unconfirmed: map[bc.Hash]*UTXO{
329                                         bc.NewHash([32]byte{0x01}): &UTXO{
330                                                 OutputID:  bc.NewHash([32]byte{0x01}),
331                                                 AccountID: "testAccount",
332                                                 Amount:    3,
333                                         },
334                                 },
335                                 reserved:     map[bc.Hash]uint64{},
336                                 reservations: map[uint64]*reservation{},
337                         },
338                         reserveAmount: 4,
339                         err:           ErrInsufficient,
340                 },
341                 {
342                         before: utxoKeeper{
343                                 store:         accountStore,
344                                 currentHeight: currentHeight,
345                                 unconfirmed: map[bc.Hash]*UTXO{
346                                         bc.NewHash([32]byte{0x01}): &UTXO{
347                                                 OutputID:    bc.NewHash([32]byte{0x01}),
348                                                 AccountID:   "testAccount",
349                                                 Amount:      3,
350                                                 ValidHeight: 9528,
351                                         },
352                                 },
353                                 reserved:     map[bc.Hash]uint64{},
354                                 reservations: map[uint64]*reservation{},
355                         },
356                         after: utxoKeeper{
357                                 store:         accountStore,
358                                 currentHeight: currentHeight,
359                                 unconfirmed: map[bc.Hash]*UTXO{
360                                         bc.NewHash([32]byte{0x01}): &UTXO{
361                                                 OutputID:    bc.NewHash([32]byte{0x01}),
362                                                 AccountID:   "testAccount",
363                                                 Amount:      3,
364                                                 ValidHeight: 9528,
365                                         },
366                                 },
367                                 reserved:     map[bc.Hash]uint64{},
368                                 reservations: map[uint64]*reservation{},
369                         },
370                         reserveAmount: 3,
371                         err:           ErrImmature,
372                 },
373                 {
374                         before: utxoKeeper{
375                                 store:         accountStore,
376                                 currentHeight: currentHeight,
377                                 unconfirmed: map[bc.Hash]*UTXO{
378                                         bc.NewHash([32]byte{0x01}): &UTXO{
379                                                 OutputID:  bc.NewHash([32]byte{0x01}),
380                                                 AccountID: "testAccount",
381                                                 Amount:    3,
382                                         },
383                                 },
384                                 reserved: map[bc.Hash]uint64{
385                                         bc.NewHash([32]byte{0x01}): 0,
386                                 },
387                                 reservations: map[uint64]*reservation{},
388                         },
389                         after: utxoKeeper{
390                                 store:         accountStore,
391                                 currentHeight: currentHeight,
392                                 unconfirmed: map[bc.Hash]*UTXO{
393                                         bc.NewHash([32]byte{0x01}): &UTXO{
394                                                 OutputID:  bc.NewHash([32]byte{0x01}),
395                                                 AccountID: "testAccount",
396                                                 Amount:    3,
397                                         },
398                                 },
399                                 reserved: map[bc.Hash]uint64{
400                                         bc.NewHash([32]byte{0x01}): 0,
401                                 },
402                                 reservations: map[uint64]*reservation{},
403                         },
404                         reserveAmount: 3,
405                         err:           ErrReserved,
406                 },
407                 {
408                         before: utxoKeeper{
409                                 store:         accountStore,
410                                 currentHeight: currentHeight,
411                                 unconfirmed: map[bc.Hash]*UTXO{
412                                         bc.NewHash([32]byte{0x01}): &UTXO{
413                                                 OutputID:  bc.NewHash([32]byte{0x01}),
414                                                 AccountID: "testAccount",
415                                                 Amount:    3,
416                                         },
417                                 },
418                                 reserved:     map[bc.Hash]uint64{},
419                                 reservations: map[uint64]*reservation{},
420                         },
421                         after: utxoKeeper{
422                                 store:         accountStore,
423                                 currentHeight: currentHeight,
424                                 unconfirmed: map[bc.Hash]*UTXO{
425                                         bc.NewHash([32]byte{0x01}): &UTXO{
426                                                 OutputID:  bc.NewHash([32]byte{0x01}),
427                                                 AccountID: "testAccount",
428                                                 Amount:    3,
429                                         },
430                                 },
431                                 reserved: map[bc.Hash]uint64{
432                                         bc.NewHash([32]byte{0x01}): 1,
433                                 },
434                                 reservations: map[uint64]*reservation{
435                                         1: &reservation{
436                                                 id: 1,
437                                                 utxos: []*UTXO{
438                                                         &UTXO{
439                                                                 OutputID:  bc.NewHash([32]byte{0x01}),
440                                                                 AccountID: "testAccount",
441                                                                 Amount:    3,
442                                                         },
443                                                 },
444                                                 change: 1,
445                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
446                                         },
447                                 },
448                         },
449                         reserveAmount: 2,
450                         err:           nil,
451                         exp:           time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
452                 },
453                 {
454                         before: utxoKeeper{
455                                 store:         accountStore,
456                                 currentHeight: currentHeight,
457                                 nextIndex:     1,
458                                 unconfirmed: map[bc.Hash]*UTXO{
459                                         bc.NewHash([32]byte{0x01}): &UTXO{
460                                                 OutputID:  bc.NewHash([32]byte{0x01}),
461                                                 AccountID: "testAccount",
462                                                 Amount:    3,
463                                         },
464                                         bc.NewHash([32]byte{0x02}): &UTXO{
465                                                 OutputID:  bc.NewHash([32]byte{0x02}),
466                                                 AccountID: "testAccount",
467                                                 Amount:    5,
468                                         },
469                                         bc.NewHash([32]byte{0x03}): &UTXO{
470                                                 OutputID:  bc.NewHash([32]byte{0x03}),
471                                                 AccountID: "testAccount",
472                                                 Amount:    7,
473                                         },
474                                 },
475                                 reserved: map[bc.Hash]uint64{
476                                         bc.NewHash([32]byte{0x01}): 1,
477                                 },
478                                 reservations: map[uint64]*reservation{},
479                         },
480                         after: utxoKeeper{
481                                 store:         accountStore,
482                                 currentHeight: currentHeight,
483                                 unconfirmed: map[bc.Hash]*UTXO{
484                                         bc.NewHash([32]byte{0x01}): &UTXO{
485                                                 OutputID:  bc.NewHash([32]byte{0x01}),
486                                                 AccountID: "testAccount",
487                                                 Amount:    3,
488                                         },
489                                         bc.NewHash([32]byte{0x02}): &UTXO{
490                                                 OutputID:  bc.NewHash([32]byte{0x02}),
491                                                 AccountID: "testAccount",
492                                                 Amount:    5,
493                                         },
494                                         bc.NewHash([32]byte{0x03}): &UTXO{
495                                                 OutputID:  bc.NewHash([32]byte{0x03}),
496                                                 AccountID: "testAccount",
497                                                 Amount:    7,
498                                         },
499                                 },
500                                 reserved: map[bc.Hash]uint64{
501                                         bc.NewHash([32]byte{0x01}): 1,
502                                         bc.NewHash([32]byte{0x02}): 2,
503                                         bc.NewHash([32]byte{0x03}): 2,
504                                 },
505                                 reservations: map[uint64]*reservation{
506                                         2: &reservation{
507                                                 id: 2,
508                                                 utxos: []*UTXO{
509                                                         &UTXO{
510                                                                 OutputID:  bc.NewHash([32]byte{0x03}),
511                                                                 AccountID: "testAccount",
512                                                                 Amount:    7,
513                                                         },
514                                                         &UTXO{
515                                                                 OutputID:  bc.NewHash([32]byte{0x02}),
516                                                                 AccountID: "testAccount",
517                                                                 Amount:    5,
518                                                         },
519                                                 },
520                                                 change: 4,
521                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
522                                         },
523                                 },
524                         },
525                         reserveAmount: 8,
526                         err:           nil,
527                         exp:           time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
528                 },
529                 {
530                         before: utxoKeeper{
531                                 store:         accountStore,
532                                 currentHeight: currentHeight,
533                                 unconfirmed: map[bc.Hash]*UTXO{
534                                         bc.NewHash([32]byte{0x01}): &UTXO{
535                                                 OutputID:  bc.NewHash([32]byte{0x01}),
536                                                 AccountID: "testAccount",
537                                                 Amount:    3,
538                                                 Vote:      []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
539                                         },
540                                 },
541                                 reserved:     map[bc.Hash]uint64{},
542                                 reservations: map[uint64]*reservation{},
543                         },
544                         after: utxoKeeper{
545                                 store:         accountStore,
546                                 currentHeight: currentHeight,
547                                 unconfirmed: map[bc.Hash]*UTXO{
548                                         bc.NewHash([32]byte{0x01}): &UTXO{
549                                                 OutputID:  bc.NewHash([32]byte{0x01}),
550                                                 AccountID: "testAccount",
551                                                 Amount:    3,
552                                                 Vote:      []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
553                                         },
554                                 },
555                                 reserved: map[bc.Hash]uint64{
556                                         bc.NewHash([32]byte{0x01}): 1,
557                                 },
558                                 reservations: map[uint64]*reservation{
559                                         1: &reservation{
560                                                 id: 1,
561                                                 utxos: []*UTXO{
562                                                         &UTXO{
563                                                                 OutputID:  bc.NewHash([32]byte{0x01}),
564                                                                 AccountID: "testAccount",
565                                                                 Amount:    3,
566                                                                 Vote:      []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
567                                                         },
568                                                 },
569                                                 change: 1,
570                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
571                                         },
572                                 },
573                         },
574                         reserveAmount: 2,
575                         err:           nil,
576                         exp:           time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
577                         vote:          []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
578                 },
579         }
580
581         for i, c := range cases {
582                 if _, err := c.before.Reserve("testAccount", &bc.AssetID{}, c.reserveAmount, true, c.vote, c.exp); err != c.err {
583                         t.Errorf("case %d: got error %v want error %v", i, err, c.err)
584                 }
585                 checkUtxoKeeperEqual(t, i, &c.before, &c.after)
586         }
587 }
588
589 func TestReserveParticular(t *testing.T) {
590         currentHeight := func() uint64 { return 9527 }
591         testDB := dbm.NewDB("testdb", "leveldb", "temp")
592         defer os.RemoveAll("temp")
593
594         accountStore := newMockAccountStore(testDB)
595
596         cases := []struct {
597                 before      utxoKeeper
598                 after       utxoKeeper
599                 err         error
600                 reserveHash bc.Hash
601                 exp         time.Time
602         }{
603                 {
604                         before: utxoKeeper{
605                                 store:         accountStore,
606                                 currentHeight: currentHeight,
607                                 unconfirmed: map[bc.Hash]*UTXO{
608                                         bc.NewHash([32]byte{0x01}): &UTXO{
609                                                 OutputID:  bc.NewHash([32]byte{0x01}),
610                                                 AccountID: "testAccount",
611                                                 Amount:    3,
612                                         },
613                                 },
614                                 reserved: map[bc.Hash]uint64{
615                                         bc.NewHash([32]byte{0x01}): 0,
616                                 },
617                                 reservations: map[uint64]*reservation{},
618                         },
619                         after: utxoKeeper{
620                                 store:         accountStore,
621                                 currentHeight: currentHeight,
622                                 unconfirmed: map[bc.Hash]*UTXO{
623                                         bc.NewHash([32]byte{0x01}): &UTXO{
624                                                 OutputID:  bc.NewHash([32]byte{0x01}),
625                                                 AccountID: "testAccount",
626                                                 Amount:    3,
627                                         },
628                                 },
629                                 reserved: map[bc.Hash]uint64{
630                                         bc.NewHash([32]byte{0x01}): 0,
631                                 },
632                                 reservations: map[uint64]*reservation{},
633                         },
634                         reserveHash: bc.NewHash([32]byte{0x01}),
635                         err:         ErrReserved,
636                 },
637                 {
638                         before: utxoKeeper{
639                                 store:         accountStore,
640                                 currentHeight: currentHeight,
641                                 unconfirmed: map[bc.Hash]*UTXO{
642                                         bc.NewHash([32]byte{0x01}): &UTXO{
643                                                 OutputID:    bc.NewHash([32]byte{0x01}),
644                                                 AccountID:   "testAccount",
645                                                 Amount:      3,
646                                                 ValidHeight: 9528,
647                                         },
648                                 },
649                                 reserved:     map[bc.Hash]uint64{},
650                                 reservations: map[uint64]*reservation{},
651                         },
652                         after: utxoKeeper{
653                                 store:         accountStore,
654                                 currentHeight: currentHeight,
655                                 unconfirmed: map[bc.Hash]*UTXO{
656                                         bc.NewHash([32]byte{0x01}): &UTXO{
657                                                 OutputID:    bc.NewHash([32]byte{0x01}),
658                                                 AccountID:   "testAccount",
659                                                 Amount:      3,
660                                                 ValidHeight: 9528,
661                                         },
662                                 },
663                                 reserved:     map[bc.Hash]uint64{},
664                                 reservations: map[uint64]*reservation{},
665                         },
666                         reserveHash: bc.NewHash([32]byte{0x01}),
667                         err:         ErrImmature,
668                 },
669                 {
670                         before: utxoKeeper{
671                                 store:         accountStore,
672                                 currentHeight: currentHeight,
673                                 unconfirmed: map[bc.Hash]*UTXO{
674                                         bc.NewHash([32]byte{0x01}): &UTXO{
675                                                 OutputID:  bc.NewHash([32]byte{0x01}),
676                                                 AccountID: "testAccount",
677                                                 Amount:    3,
678                                         },
679                                 },
680                                 reserved:     map[bc.Hash]uint64{},
681                                 reservations: map[uint64]*reservation{},
682                         },
683                         after: utxoKeeper{
684                                 store:         accountStore,
685                                 currentHeight: currentHeight,
686                                 unconfirmed: map[bc.Hash]*UTXO{
687                                         bc.NewHash([32]byte{0x01}): &UTXO{
688                                                 OutputID:  bc.NewHash([32]byte{0x01}),
689                                                 AccountID: "testAccount",
690                                                 Amount:    3,
691                                         },
692                                 },
693                                 reserved: map[bc.Hash]uint64{
694                                         bc.NewHash([32]byte{0x01}): 1,
695                                 },
696                                 reservations: map[uint64]*reservation{
697                                         1: &reservation{
698                                                 id: 1,
699                                                 utxos: []*UTXO{
700                                                         &UTXO{
701                                                                 OutputID:  bc.NewHash([32]byte{0x01}),
702                                                                 AccountID: "testAccount",
703                                                                 Amount:    3,
704                                                         },
705                                                 },
706                                                 change: 0,
707                                                 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
708                                         },
709                                 },
710                         },
711                         reserveHash: bc.NewHash([32]byte{0x01}),
712                         err:         nil,
713                         exp:         time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
714                 },
715         }
716
717         for i, c := range cases {
718                 if _, err := c.before.ReserveParticular(c.reserveHash, true, c.exp); err != c.err {
719                         t.Errorf("case %d: got error %v want error %v", i, err, c.err)
720                 }
721                 checkUtxoKeeperEqual(t, i, &c.before, &c.after)
722         }
723 }
724
725 func TestExpireReservation(t *testing.T) {
726         before := &utxoKeeper{
727                 reservations: map[uint64]*reservation{
728                         1: &reservation{expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC)},
729                         2: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
730                         3: &reservation{expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC)},
731                         4: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
732                         5: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
733                 },
734         }
735         after := &utxoKeeper{
736                 reservations: map[uint64]*reservation{
737                         2: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
738                         4: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
739                         5: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
740                 },
741         }
742         before.expireReservation(time.Date(2017, 8, 10, 0, 0, 0, 0, time.UTC))
743         checkUtxoKeeperEqual(t, 0, before, after)
744 }
745
746 func TestFindUtxos(t *testing.T) {
747         currentHeight := func() uint64 { return 9527 }
748         testDB := dbm.NewDB("testdb", "leveldb", "temp")
749         defer func() {
750                 testDB.Close()
751                 os.RemoveAll("temp")
752         }()
753
754         accountStore := newMockAccountStore(testDB)
755
756         cases := []struct {
757                 uk             utxoKeeper
758                 dbUtxos        []*UTXO
759                 useUnconfirmed bool
760                 wantUtxos      []*UTXO
761                 immatureAmount uint64
762                 vote           []byte
763         }{
764                 {
765                         uk: utxoKeeper{
766                                 store:         accountStore,
767                                 currentHeight: currentHeight,
768                                 unconfirmed:   map[bc.Hash]*UTXO{},
769                         },
770                         dbUtxos:        []*UTXO{},
771                         useUnconfirmed: true,
772                         wantUtxos:      []*UTXO{},
773                         immatureAmount: 0,
774                 },
775                 {
776                         uk: utxoKeeper{
777                                 store:         accountStore,
778                                 currentHeight: currentHeight,
779                                 unconfirmed:   map[bc.Hash]*UTXO{},
780                         },
781                         dbUtxos: []*UTXO{
782                                 &UTXO{
783                                         OutputID:  bc.NewHash([32]byte{0x01}),
784                                         AccountID: "testAccount",
785                                         Amount:    3,
786                                 },
787                                 &UTXO{
788                                         OutputID:  bc.NewHash([32]byte{0x02}),
789                                         AccountID: "testAccount",
790                                         AssetID:   bc.AssetID{V0: 6},
791                                         Amount:    3,
792                                 },
793                         },
794                         useUnconfirmed: false,
795                         wantUtxos: []*UTXO{
796                                 &UTXO{
797                                         OutputID:  bc.NewHash([32]byte{0x01}),
798                                         AccountID: "testAccount",
799                                         Amount:    3,
800                                 },
801                         },
802                         immatureAmount: 0,
803                 },
804                 {
805                         uk: utxoKeeper{
806                                 store:         accountStore,
807                                 currentHeight: currentHeight,
808                                 unconfirmed:   map[bc.Hash]*UTXO{},
809                         },
810                         dbUtxos: []*UTXO{
811                                 &UTXO{
812                                         OutputID:    bc.NewHash([32]byte{0x02}),
813                                         AccountID:   "testAccount",
814                                         Amount:      3,
815                                         ValidHeight: 9528,
816                                 },
817                         },
818                         useUnconfirmed: false,
819                         wantUtxos:      []*UTXO{},
820                         immatureAmount: 3,
821                 },
822                 {
823                         uk: utxoKeeper{
824                                 store:         accountStore,
825                                 currentHeight: currentHeight,
826                                 unconfirmed: map[bc.Hash]*UTXO{
827                                         bc.NewHash([32]byte{0x01}): &UTXO{
828                                                 OutputID:  bc.NewHash([32]byte{0x01}),
829                                                 AccountID: "testAccount",
830                                                 Amount:    3,
831                                         },
832                                 },
833                         },
834                         dbUtxos: []*UTXO{
835                                 &UTXO{
836                                         OutputID:  bc.NewHash([32]byte{0x02}),
837                                         AccountID: "testAccount",
838                                         Amount:    3,
839                                 },
840                         },
841                         useUnconfirmed: false,
842                         wantUtxos: []*UTXO{
843                                 &UTXO{
844                                         OutputID:  bc.NewHash([32]byte{0x02}),
845                                         AccountID: "testAccount",
846                                         Amount:    3,
847                                 },
848                         },
849                         immatureAmount: 0,
850                 },
851                 {
852                         uk: utxoKeeper{
853                                 store:         accountStore,
854                                 currentHeight: currentHeight,
855                                 unconfirmed: map[bc.Hash]*UTXO{
856                                         bc.NewHash([32]byte{0x11}): &UTXO{
857                                                 OutputID:  bc.NewHash([32]byte{0x01}),
858                                                 AccountID: "testAccount",
859                                                 Amount:    3,
860                                         },
861                                 },
862                         },
863                         dbUtxos: []*UTXO{
864                                 &UTXO{
865                                         OutputID:  bc.NewHash([32]byte{0x02}),
866                                         AccountID: "testAccount",
867                                         Amount:    3,
868                                 },
869                         },
870                         useUnconfirmed: true,
871                         wantUtxos: []*UTXO{
872                                 &UTXO{
873                                         OutputID:  bc.NewHash([32]byte{0x02}),
874                                         AccountID: "testAccount",
875                                         Amount:    3,
876                                 },
877                                 &UTXO{
878                                         OutputID:  bc.NewHash([32]byte{0x01}),
879                                         AccountID: "testAccount",
880                                         Amount:    3,
881                                 },
882                         },
883                         immatureAmount: 0,
884                 },
885                 {
886                         uk: utxoKeeper{
887                                 store:         accountStore,
888                                 currentHeight: currentHeight,
889                                 unconfirmed: map[bc.Hash]*UTXO{
890                                         bc.NewHash([32]byte{0x01}): &UTXO{
891                                                 OutputID:  bc.NewHash([32]byte{0x01}),
892                                                 AccountID: "testAccount",
893                                                 Amount:    1,
894                                         },
895                                         bc.NewHash([32]byte{0x02}): &UTXO{
896                                                 OutputID:  bc.NewHash([32]byte{0x02}),
897                                                 AccountID: "notMe",
898                                                 Amount:    2,
899                                         },
900                                 },
901                         },
902                         dbUtxos: []*UTXO{
903                                 &UTXO{
904                                         OutputID:  bc.NewHash([32]byte{0x03}),
905                                         AccountID: "testAccount",
906                                         Amount:    3,
907                                 },
908                                 &UTXO{
909                                         OutputID:  bc.NewHash([32]byte{0x04}),
910                                         AccountID: "notMe",
911                                         Amount:    4,
912                                 },
913                         },
914                         useUnconfirmed: true,
915                         wantUtxos: []*UTXO{
916                                 &UTXO{
917                                         OutputID:  bc.NewHash([32]byte{0x03}),
918                                         AccountID: "testAccount",
919                                         Amount:    3,
920                                 },
921                                 &UTXO{
922                                         OutputID:  bc.NewHash([32]byte{0x01}),
923                                         AccountID: "testAccount",
924                                         Amount:    1,
925                                 },
926                         },
927                         immatureAmount: 0,
928                 },
929                 {
930                         uk: utxoKeeper{
931                                 store:         accountStore,
932                                 currentHeight: currentHeight,
933                                 unconfirmed:   map[bc.Hash]*UTXO{},
934                         },
935                         dbUtxos: []*UTXO{
936                                 &UTXO{
937                                         OutputID:  bc.NewHash([32]byte{0x01}),
938                                         AccountID: "testAccount",
939                                         Amount:    6,
940                                         Vote:      []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
941                                 },
942                         },
943                         useUnconfirmed: false,
944                         wantUtxos: []*UTXO{
945                                 &UTXO{
946                                         OutputID:  bc.NewHash([32]byte{0x01}),
947                                         AccountID: "testAccount",
948                                         Amount:    6,
949                                         Vote:      []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
950                                 },
951                         },
952                         immatureAmount: 0,
953                         vote:           []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
954                 },
955         }
956
957         for i, c := range cases {
958                 for _, u := range c.dbUtxos {
959                         if err := c.uk.store.SetStandardUTXO(u.OutputID, u); err != nil {
960                                 t.Error(err)
961                         }
962                 }
963
964                 gotUtxos, immatureAmount := c.uk.findUtxos("testAccount", &bc.AssetID{}, c.useUnconfirmed, c.vote)
965                 if !testutil.DeepEqual(gotUtxos, c.wantUtxos) {
966                         t.Errorf("case %d: got %v want %v", i, gotUtxos, c.wantUtxos)
967                 }
968                 if immatureAmount != c.immatureAmount {
969                         t.Errorf("case %d: got %v want %v", i, immatureAmount, c.immatureAmount)
970                 }
971
972                 for _, u := range c.dbUtxos {
973                         c.uk.store.DeleteStandardUTXO(u.OutputID)
974                 }
975         }
976 }
977
978 func TestFindUTXO(t *testing.T) {
979         currentHeight := func() uint64 { return 9527 }
980         testDB := dbm.NewDB("testdb", "leveldb", "temp")
981         defer os.RemoveAll("temp")
982
983         accountStore := newMockAccountStore(testDB)
984
985         cases := []struct {
986                 uk             utxoKeeper
987                 dbUtxos        map[string]*UTXO
988                 outHash        bc.Hash
989                 useUnconfirmed bool
990                 wantUtxo       *UTXO
991                 err            error
992         }{
993                 {
994                         uk: utxoKeeper{
995                                 store:         accountStore,
996                                 currentHeight: currentHeight,
997                                 unconfirmed:   map[bc.Hash]*UTXO{},
998                         },
999                         dbUtxos:  map[string]*UTXO{},
1000                         outHash:  bc.NewHash([32]byte{0x01}),
1001                         wantUtxo: nil,
1002                         err:      ErrMatchUTXO,
1003                 },
1004                 {
1005                         uk: utxoKeeper{
1006                                 store:         accountStore,
1007                                 currentHeight: currentHeight,
1008                                 unconfirmed: map[bc.Hash]*UTXO{
1009                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1010                                 },
1011                         },
1012                         dbUtxos:        map[string]*UTXO{},
1013                         outHash:        bc.NewHash([32]byte{0x01}),
1014                         wantUtxo:       nil,
1015                         useUnconfirmed: false,
1016                         err:            ErrMatchUTXO,
1017                 },
1018                 {
1019                         uk: utxoKeeper{
1020                                 store:         accountStore,
1021                                 currentHeight: currentHeight,
1022                                 unconfirmed: map[bc.Hash]*UTXO{
1023                                         bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1024                                 },
1025                         },
1026                         dbUtxos:        map[string]*UTXO{},
1027                         outHash:        bc.NewHash([32]byte{0x01}),
1028                         wantUtxo:       &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1029                         useUnconfirmed: true,
1030                         err:            nil,
1031                 },
1032                 {
1033                         uk: utxoKeeper{
1034                                 store:         accountStore,
1035                                 currentHeight: currentHeight,
1036                                 unconfirmed:   map[bc.Hash]*UTXO{},
1037                         },
1038                         dbUtxos: map[string]*UTXO{
1039                                 string(StandardUTXOKey(bc.NewHash([32]byte{0x01}))): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1040                         },
1041                         outHash:        bc.NewHash([32]byte{0x01}),
1042                         wantUtxo:       &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1043                         useUnconfirmed: false,
1044                         err:            nil,
1045                 },
1046                 {
1047                         uk: utxoKeeper{
1048                                 store:         accountStore,
1049                                 currentHeight: currentHeight,
1050                                 unconfirmed:   map[bc.Hash]*UTXO{},
1051                         },
1052                         dbUtxos: map[string]*UTXO{
1053                                 string(ContractUTXOKey(bc.NewHash([32]byte{0x01}))): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1054                         },
1055                         outHash:        bc.NewHash([32]byte{0x01}),
1056                         wantUtxo:       &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1057                         useUnconfirmed: false,
1058                         err:            nil,
1059                 },
1060         }
1061
1062         for i, c := range cases {
1063                 for k, u := range c.dbUtxos {
1064                         data, err := json.Marshal(u)
1065                         if err != nil {
1066                                 t.Error(err)
1067                         }
1068                         testDB.Set([]byte(k), data)
1069                 }
1070
1071                 gotUtxo, err := c.uk.findUtxo(c.outHash, c.useUnconfirmed)
1072                 if !testutil.DeepEqual(gotUtxo, c.wantUtxo) {
1073                         t.Errorf("case %d: got %v want %v", i, gotUtxo, c.wantUtxo)
1074                 }
1075                 if err != c.err {
1076                         t.Errorf("case %d: got %v want %v", i, err, c.err)
1077                 }
1078
1079                 for _, u := range c.dbUtxos {
1080                         c.uk.store.DeleteStandardUTXO(u.OutputID)
1081                 }
1082         }
1083 }
1084
1085 func TestOptUTXOs(t *testing.T) {
1086         cases := []struct {
1087                 uk             utxoKeeper
1088                 input          []*UTXO
1089                 inputAmount    uint64
1090                 wantUtxos      []*UTXO
1091                 optAmount      uint64
1092                 reservedAmount uint64
1093         }{
1094                 {
1095                         uk: utxoKeeper{
1096                                 reserved: map[bc.Hash]uint64{
1097                                         bc.NewHash([32]byte{0x01}): 1,
1098                                 },
1099                         },
1100                         input:          []*UTXO{},
1101                         inputAmount:    13,
1102                         wantUtxos:      []*UTXO{},
1103                         optAmount:      0,
1104                         reservedAmount: 0,
1105                 },
1106                 {
1107                         uk: utxoKeeper{
1108                                 reserved: map[bc.Hash]uint64{
1109                                         bc.NewHash([32]byte{0x01}): 1,
1110                                 },
1111                         },
1112                         input: []*UTXO{
1113                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1114                         },
1115                         inputAmount:    13,
1116                         wantUtxos:      []*UTXO{},
1117                         optAmount:      0,
1118                         reservedAmount: 1,
1119                 },
1120                 {
1121                         uk: utxoKeeper{
1122                                 reserved: map[bc.Hash]uint64{
1123                                         bc.NewHash([32]byte{0x01}): 1,
1124                                 },
1125                         },
1126                         input: []*UTXO{
1127                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1128                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1129                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1130                         },
1131                         inputAmount: 13,
1132                         wantUtxos: []*UTXO{
1133                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1134                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1135                         },
1136                         optAmount:      8,
1137                         reservedAmount: 1,
1138                 },
1139                 {
1140                         uk: utxoKeeper{
1141                                 reserved: map[bc.Hash]uint64{
1142                                         bc.NewHash([32]byte{0x01}): 1,
1143                                         bc.NewHash([32]byte{0x02}): 2,
1144                                         bc.NewHash([32]byte{0x03}): 3,
1145                                 },
1146                         },
1147                         input: []*UTXO{
1148                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1149                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1150                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1151                         },
1152                         inputAmount:    1,
1153                         wantUtxos:      []*UTXO{},
1154                         optAmount:      0,
1155                         reservedAmount: 9,
1156                 },
1157                 {
1158                         uk: utxoKeeper{},
1159                         input: []*UTXO{
1160                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1161                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1162                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1163                         },
1164                         inputAmount: 1,
1165                         wantUtxos: []*UTXO{
1166                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1167                         },
1168                         optAmount:      1,
1169                         reservedAmount: 0,
1170                 },
1171                 {
1172                         uk: utxoKeeper{},
1173                         input: []*UTXO{
1174                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1175                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1176                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1177                         },
1178                         inputAmount: 5,
1179                         wantUtxos: []*UTXO{
1180                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1181                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1182                         },
1183                         optAmount:      5,
1184                         reservedAmount: 0,
1185                 },
1186                 {
1187                         uk: utxoKeeper{},
1188                         input: []*UTXO{
1189                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1190                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 1},
1191                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 1},
1192                                 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 1},
1193                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 1},
1194                                 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 1},
1195                                 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 6},
1196                         },
1197                         inputAmount: 6,
1198                         wantUtxos: []*UTXO{
1199                                 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 6},
1200                         },
1201                         optAmount:      6,
1202                         reservedAmount: 0,
1203                 },
1204                 {
1205                         uk: utxoKeeper{},
1206                         input: []*UTXO{
1207                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1208                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 1},
1209                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 1},
1210                                 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 1},
1211                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 1},
1212                                 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 1},
1213                                 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 6},
1214                         },
1215                         inputAmount: 5,
1216                         wantUtxos: []*UTXO{
1217                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 1},
1218                                 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 1},
1219                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 1},
1220                                 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 1},
1221                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1222                         },
1223                         optAmount:      5,
1224                         reservedAmount: 0,
1225                 },
1226                 {
1227                         uk: utxoKeeper{},
1228                         input: []*UTXO{
1229                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1230                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1231                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1232                                 &UTXO{OutputID: bc.NewHash([32]byte{0x07}), Amount: 7},
1233                                 &UTXO{OutputID: bc.NewHash([32]byte{0x11}), Amount: 11},
1234                                 &UTXO{OutputID: bc.NewHash([32]byte{0x13}), Amount: 13},
1235                                 &UTXO{OutputID: bc.NewHash([32]byte{0x23}), Amount: 23},
1236                                 &UTXO{OutputID: bc.NewHash([32]byte{0x31}), Amount: 31},
1237                         },
1238                         inputAmount: 13,
1239                         wantUtxos: []*UTXO{
1240                                 &UTXO{OutputID: bc.NewHash([32]byte{0x07}), Amount: 7},
1241                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1242                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1243                         },
1244                         optAmount:      15,
1245                         reservedAmount: 0,
1246                 },
1247                 {
1248                         uk: utxoKeeper{},
1249                         input: []*UTXO{
1250                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1251                         },
1252                         inputAmount: 1,
1253                         wantUtxos: []*UTXO{
1254                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1255                         },
1256                         optAmount:      1,
1257                         reservedAmount: 0,
1258                 },
1259                 {
1260                         uk: utxoKeeper{},
1261                         input: []*UTXO{
1262                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1263                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1264                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1265                                 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 4},
1266                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1267                                 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 6},
1268                                 &UTXO{OutputID: bc.NewHash([32]byte{0x07}), Amount: 7},
1269                                 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 8},
1270                                 &UTXO{OutputID: bc.NewHash([32]byte{0x09}), Amount: 9},
1271                                 &UTXO{OutputID: bc.NewHash([32]byte{0x10}), Amount: 10},
1272                                 &UTXO{OutputID: bc.NewHash([32]byte{0x11}), Amount: 11},
1273                                 &UTXO{OutputID: bc.NewHash([32]byte{0x12}), Amount: 12},
1274                         },
1275                         inputAmount: 15,
1276                         wantUtxos: []*UTXO{
1277                                 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1278                                 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 4},
1279                                 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1280                                 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1281                                 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1282                         },
1283                         optAmount:      15,
1284                         reservedAmount: 0,
1285                 },
1286         }
1287
1288         for i, c := range cases {
1289                 got, optAmount, reservedAmount := c.uk.optUTXOs(c.input, c.inputAmount)
1290                 if !testutil.DeepEqual(got, c.wantUtxos) {
1291                         t.Errorf("case %d: utxos got %v want %v", i, got, c.wantUtxos)
1292                 }
1293                 if optAmount != c.optAmount {
1294                         t.Errorf("case %d: utxos got %v want %v", i, optAmount, c.optAmount)
1295                 }
1296                 if reservedAmount != c.reservedAmount {
1297                         t.Errorf("case %d: reservedAmount got %v want %v", i, reservedAmount, c.reservedAmount)
1298                 }
1299         }
1300 }
1301
1302 func checkUtxoKeeperEqual(t *testing.T, i int, a, b *utxoKeeper) {
1303         if !testutil.DeepEqual(a.unconfirmed, b.unconfirmed) {
1304                 t.Errorf("case %d: unconfirmed got %v want %v", i, a.unconfirmed, b.unconfirmed)
1305         }
1306         if !testutil.DeepEqual(a.reserved, b.reserved) {
1307                 t.Errorf("case %d: reserved got %v want %v", i, a.reserved, b.reserved)
1308         }
1309         if !testutil.DeepEqual(a.reservations, b.reservations) {
1310                 t.Errorf("case %d: reservations got %v want %v", i, a.reservations, b.reservations)
1311         }
1312 }
1313
1314 const (
1315         utxoPrefix byte = iota //UTXOPrefix is StandardUTXOKey prefix
1316         contractPrefix
1317         contractIndexPrefix
1318         accountPrefix // AccountPrefix is account ID prefix
1319         accountIndexPrefix
1320 )
1321
1322 // leveldb key prefix
1323 var (
1324         colon               byte = 0x3a
1325         accountStore             = []byte("AS:")
1326         UTXOPrefix               = append(accountStore, utxoPrefix, colon)
1327         ContractPrefix           = append(accountStore, contractPrefix, colon)
1328         ContractIndexPrefix      = append(accountStore, contractIndexPrefix, colon)
1329         AccountPrefix            = append(accountStore, accountPrefix, colon) // AccountPrefix is account ID prefix
1330         AccountIndexPrefix       = append(accountStore, accountIndexPrefix, colon)
1331 )
1332
1333 const (
1334         sutxoPrefix byte = iota //SUTXOPrefix is ContractUTXOKey prefix
1335         accountAliasPrefix
1336         txPrefix            //TxPrefix is wallet database transactions prefix
1337         txIndexPrefix       //TxIndexPrefix is wallet database tx index prefix
1338         unconfirmedTxPrefix //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
1339         globalTxIndexPrefix //GlobalTxIndexPrefix is wallet database global tx index prefix
1340         walletKey
1341         miningAddressKey
1342         coinbaseAbKey
1343         recoveryKey //recoveryKey key for db store recovery info.
1344 )
1345
1346 var (
1347         walletStore         = []byte("WS:")
1348         SUTXOPrefix         = append(walletStore, sutxoPrefix, colon)
1349         AccountAliasPrefix  = append(walletStore, accountAliasPrefix, colon)
1350         TxPrefix            = append(walletStore, txPrefix, colon)            //TxPrefix is wallet database transactions prefix
1351         TxIndexPrefix       = append(walletStore, txIndexPrefix, colon)       //TxIndexPrefix is wallet database tx index prefix
1352         UnconfirmedTxPrefix = append(walletStore, unconfirmedTxPrefix, colon) //UnconfirmedTxPrefix is txpool unconfirmed transactions prefix
1353         GlobalTxIndexPrefix = append(walletStore, globalTxIndexPrefix, colon) //GlobalTxIndexPrefix is wallet database global tx index prefix
1354         WalletKey           = append(walletStore, walletKey)
1355         MiningAddressKey    = append(walletStore, miningAddressKey)
1356         CoinbaseAbKey       = append(walletStore, coinbaseAbKey)
1357         RecoveryKey         = append(walletStore, recoveryKey)
1358 )
1359
1360 type mockAccountStore struct {
1361         db    dbm.DB
1362         batch dbm.Batch
1363 }
1364
1365 // NewAccountStore create new AccountStore.
1366 func newMockAccountStore(db dbm.DB) *mockAccountStore {
1367         return &mockAccountStore{
1368                 db:    db,
1369                 batch: nil,
1370         }
1371 }
1372
1373 // StandardUTXOKey makes an account unspent outputs key to store
1374 func StandardUTXOKey(id bc.Hash) []byte {
1375         return append(UTXOPrefix, id.Bytes()...)
1376 }
1377
1378 // ContractUTXOKey makes a smart contract unspent outputs key to store
1379 func ContractUTXOKey(id bc.Hash) []byte {
1380         return append(SUTXOPrefix, id.Bytes()...)
1381 }
1382
1383 func (store *mockAccountStore) InitBatch() AccountStore                         { return nil }
1384 func (store *mockAccountStore) CommitBatch() error                              { return nil }
1385 func (store *mockAccountStore) DeleteAccount(*Account) error                    { return nil }
1386 func (store *mockAccountStore) GetAccountByAlias(string) (*Account, error)      { return nil, nil }
1387 func (store *mockAccountStore) GetAccountByID(string) (*Account, error)         { return nil, nil }
1388 func (store *mockAccountStore) GetAccountIndex([]chainkd.XPub) uint64           { return 0 }
1389 func (store *mockAccountStore) GetBip44ContractIndex(string, bool) uint64       { return 0 }
1390 func (store *mockAccountStore) GetCoinbaseArbitrary() []byte                    { return nil }
1391 func (store *mockAccountStore) GetContractIndex(string) uint64                  { return 0 }
1392 func (store *mockAccountStore) GetControlProgram(bc.Hash) (*CtrlProgram, error) { return nil, nil }
1393 func (store *mockAccountStore) GetMiningAddress() (*CtrlProgram, error)         { return nil, nil }
1394 func (store *mockAccountStore) ListAccounts(string) ([]*Account, error)         { return nil, nil }
1395 func (store *mockAccountStore) ListControlPrograms() ([]*CtrlProgram, error)    { return nil, nil }
1396 func (store *mockAccountStore) SetAccount(*Account) error                       { return nil }
1397 func (store *mockAccountStore) SetAccountIndex(*Account)                        { return }
1398 func (store *mockAccountStore) SetBip44ContractIndex(string, bool, uint64)      { return }
1399 func (store *mockAccountStore) SetCoinbaseArbitrary([]byte)                     { return }
1400 func (store *mockAccountStore) SetContractIndex(string, uint64)                 { return }
1401 func (store *mockAccountStore) SetControlProgram(bc.Hash, *CtrlProgram) error   { return nil }
1402 func (store *mockAccountStore) SetMiningAddress(*CtrlProgram) error             { return nil }
1403
1404 // DeleteStandardUTXO delete utxo by outpu id
1405 func (store *mockAccountStore) DeleteStandardUTXO(outputID bc.Hash) {
1406         if store.batch == nil {
1407                 store.db.Delete(StandardUTXOKey(outputID))
1408         } else {
1409                 store.batch.Delete(StandardUTXOKey(outputID))
1410         }
1411 }
1412
1413 // GetUTXO get standard utxo by id
1414 func (store *mockAccountStore) GetUTXO(outid bc.Hash) (*UTXO, error) {
1415         u := new(UTXO)
1416         if data := store.db.Get(StandardUTXOKey(outid)); data != nil {
1417                 return u, json.Unmarshal(data, u)
1418         }
1419         if data := store.db.Get(ContractUTXOKey(outid)); data != nil {
1420                 return u, json.Unmarshal(data, u)
1421         }
1422         return nil, ErrMatchUTXO
1423 }
1424
1425 // ListUTXOs get utxos by accountID
1426 func (store *mockAccountStore) ListUTXOs() ([]*UTXO, error) {
1427         utxoIter := store.db.IteratorPrefix([]byte(UTXOPrefix))
1428         defer utxoIter.Release()
1429
1430         utxos := []*UTXO{}
1431         for utxoIter.Next() {
1432                 utxo := new(UTXO)
1433                 if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
1434                         return nil, err
1435                 }
1436                 utxos = append(utxos, utxo)
1437         }
1438         return utxos, nil
1439 }
1440
1441 // SetStandardUTXO set standard utxo
1442 func (store *mockAccountStore) SetStandardUTXO(outputID bc.Hash, utxo *UTXO) error {
1443         data, err := json.Marshal(utxo)
1444         if err != nil {
1445                 return err
1446         }
1447         if store.batch == nil {
1448                 store.db.Set(StandardUTXOKey(outputID), data)
1449         } else {
1450                 store.batch.Set(StandardUTXOKey(outputID), data)
1451         }
1452         return nil
1453 }
1454
1455 func mockAccountManager(t *testing.T) *Manager {
1456         dirPath, err := ioutil.TempDir(".", "")
1457         if err != nil {
1458                 t.Fatal(err)
1459         }
1460         defer os.RemoveAll(dirPath)
1461
1462         testDB := dbm.NewDB("testdb", "memdb", dirPath)
1463         accountStore := newMockAccountStore(testDB)
1464         bestBlockHeight := func() uint64 { return 9527 }
1465
1466         return &Manager{
1467                 store:       accountStore,
1468                 chain:       nil,
1469                 utxoKeeper:  newUtxoKeeper(bestBlockHeight, accountStore),
1470                 cache:       lru.New(maxAccountCache),
1471                 aliasCache:  lru.New(maxAccountCache),
1472                 delayedACPs: make(map[*txbuilder.TemplateBuilder][]*CtrlProgram),
1473         }
1474 }