9 log "github.com/sirupsen/logrus"
10 "github.com/vapor/crypto/ed25519/chainkd"
11 dbm "github.com/vapor/database/leveldb"
12 "github.com/vapor/protocol/bc"
13 "github.com/vapor/testutil"
16 func TestAddUnconfirmedUtxo(t *testing.T) {
24 unconfirmed: map[bc.Hash]*UTXO{},
27 unconfirmed: map[bc.Hash]*UTXO{},
33 unconfirmed: map[bc.Hash]*UTXO{},
36 unconfirmed: map[bc.Hash]*UTXO{
37 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
41 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
46 unconfirmed: map[bc.Hash]*UTXO{
47 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
51 unconfirmed: map[bc.Hash]*UTXO{
52 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
56 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
61 unconfirmed: map[bc.Hash]*UTXO{
62 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
66 unconfirmed: map[bc.Hash]*UTXO{
67 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
68 bc.NewHash([32]byte{0x02}): &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
69 bc.NewHash([32]byte{0x03}): &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
73 &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
74 &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
79 for i, c := range cases {
80 c.before.AddUnconfirmedUtxo(c.addUtxos)
81 if !testutil.DeepEqual(c.before, c.after) {
82 t.Errorf("case %d: got %v want %v", i, c.before, c.after)
87 func TestCancel(t *testing.T) {
95 reserved: map[bc.Hash]uint64{},
96 reservations: map[uint64]*reservation{},
99 reserved: map[bc.Hash]uint64{},
100 reservations: map[uint64]*reservation{},
106 reserved: map[bc.Hash]uint64{
107 bc.NewHash([32]byte{0x01}): 1,
109 reservations: map[uint64]*reservation{
113 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
116 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
121 reserved: map[bc.Hash]uint64{},
122 reservations: map[uint64]*reservation{},
128 reserved: map[bc.Hash]uint64{
129 bc.NewHash([32]byte{0x01}): 1,
131 reservations: map[uint64]*reservation{
135 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
138 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
143 reserved: map[bc.Hash]uint64{
144 bc.NewHash([32]byte{0x01}): 1,
146 reservations: map[uint64]*reservation{
150 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
153 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
161 reserved: map[bc.Hash]uint64{
162 bc.NewHash([32]byte{0x01}): 1,
163 bc.NewHash([32]byte{0x02}): 3,
164 bc.NewHash([32]byte{0x03}): 3,
165 bc.NewHash([32]byte{0x04}): 3,
167 reservations: map[uint64]*reservation{
171 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
174 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
179 &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
180 &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
181 &UTXO{OutputID: bc.NewHash([32]byte{0x04})},
184 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
189 reserved: map[bc.Hash]uint64{
190 bc.NewHash([32]byte{0x01}): 1,
192 reservations: map[uint64]*reservation{
196 &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
199 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
207 for i, c := range cases {
208 c.before.cancel(c.cancelRid)
209 if !testutil.DeepEqual(c.before, c.after) {
210 t.Errorf("case %d: got %v want %v", i, c.before, c.after)
215 func TestRemoveUnconfirmedUtxo(t *testing.T) {
219 removeUtxos []*bc.Hash
223 unconfirmed: map[bc.Hash]*UTXO{},
226 unconfirmed: map[bc.Hash]*UTXO{},
228 removeUtxos: []*bc.Hash{},
232 unconfirmed: map[bc.Hash]*UTXO{
233 bc.Hash{V0: 1}: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
237 unconfirmed: map[bc.Hash]*UTXO{},
239 removeUtxos: []*bc.Hash{
245 unconfirmed: map[bc.Hash]*UTXO{
246 bc.Hash{V0: 1}: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
247 bc.Hash{V0: 2}: &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
248 bc.Hash{V0: 3}: &UTXO{OutputID: bc.NewHash([32]byte{0x03})},
249 bc.Hash{V0: 4}: &UTXO{OutputID: bc.NewHash([32]byte{0x04})},
250 bc.Hash{V0: 5}: &UTXO{OutputID: bc.NewHash([32]byte{0x05})},
254 unconfirmed: map[bc.Hash]*UTXO{
255 bc.Hash{V0: 2}: &UTXO{OutputID: bc.NewHash([32]byte{0x02})},
256 bc.Hash{V0: 4}: &UTXO{OutputID: bc.NewHash([32]byte{0x04})},
259 removeUtxos: []*bc.Hash{
267 for i, c := range cases {
268 c.before.RemoveUnconfirmedUtxo(c.removeUtxos)
269 if !testutil.DeepEqual(c.before, c.after) {
270 t.Errorf("case %d: got %v want %v", i, c.before, c.after)
275 func TestReserve(t *testing.T) {
276 currentHeight := func() uint64 { return 9527 }
277 testDB := dbm.NewDB("testdb", "leveldb", "temp")
283 accountStore := newMockAccountStore(testDB)
296 currentHeight: currentHeight,
297 reserved: map[bc.Hash]uint64{},
298 reservations: map[uint64]*reservation{},
302 currentHeight: currentHeight,
303 reserved: map[bc.Hash]uint64{},
304 reservations: map[uint64]*reservation{},
307 err: ErrInsufficient,
312 currentHeight: currentHeight,
313 unconfirmed: map[bc.Hash]*UTXO{
314 bc.NewHash([32]byte{0x01}): &UTXO{
315 OutputID: bc.NewHash([32]byte{0x01}),
316 AccountID: "testAccount",
320 reserved: map[bc.Hash]uint64{},
321 reservations: map[uint64]*reservation{},
325 currentHeight: currentHeight,
326 unconfirmed: map[bc.Hash]*UTXO{
327 bc.NewHash([32]byte{0x01}): &UTXO{
328 OutputID: bc.NewHash([32]byte{0x01}),
329 AccountID: "testAccount",
333 reserved: map[bc.Hash]uint64{},
334 reservations: map[uint64]*reservation{},
337 err: ErrInsufficient,
342 currentHeight: currentHeight,
343 unconfirmed: map[bc.Hash]*UTXO{
344 bc.NewHash([32]byte{0x01}): &UTXO{
345 OutputID: bc.NewHash([32]byte{0x01}),
346 AccountID: "testAccount",
351 reserved: map[bc.Hash]uint64{},
352 reservations: map[uint64]*reservation{},
356 currentHeight: currentHeight,
357 unconfirmed: map[bc.Hash]*UTXO{
358 bc.NewHash([32]byte{0x01}): &UTXO{
359 OutputID: bc.NewHash([32]byte{0x01}),
360 AccountID: "testAccount",
365 reserved: map[bc.Hash]uint64{},
366 reservations: map[uint64]*reservation{},
374 currentHeight: currentHeight,
375 unconfirmed: map[bc.Hash]*UTXO{
376 bc.NewHash([32]byte{0x01}): &UTXO{
377 OutputID: bc.NewHash([32]byte{0x01}),
378 AccountID: "testAccount",
382 reserved: map[bc.Hash]uint64{
383 bc.NewHash([32]byte{0x01}): 0,
385 reservations: map[uint64]*reservation{},
389 currentHeight: currentHeight,
390 unconfirmed: map[bc.Hash]*UTXO{
391 bc.NewHash([32]byte{0x01}): &UTXO{
392 OutputID: bc.NewHash([32]byte{0x01}),
393 AccountID: "testAccount",
397 reserved: map[bc.Hash]uint64{
398 bc.NewHash([32]byte{0x01}): 0,
400 reservations: map[uint64]*reservation{},
408 currentHeight: currentHeight,
409 unconfirmed: map[bc.Hash]*UTXO{
410 bc.NewHash([32]byte{0x01}): &UTXO{
411 OutputID: bc.NewHash([32]byte{0x01}),
412 AccountID: "testAccount",
416 reserved: map[bc.Hash]uint64{},
417 reservations: map[uint64]*reservation{},
421 currentHeight: currentHeight,
422 unconfirmed: map[bc.Hash]*UTXO{
423 bc.NewHash([32]byte{0x01}): &UTXO{
424 OutputID: bc.NewHash([32]byte{0x01}),
425 AccountID: "testAccount",
429 reserved: map[bc.Hash]uint64{
430 bc.NewHash([32]byte{0x01}): 1,
432 reservations: map[uint64]*reservation{
437 OutputID: bc.NewHash([32]byte{0x01}),
438 AccountID: "testAccount",
443 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
449 exp: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
454 currentHeight: currentHeight,
456 unconfirmed: map[bc.Hash]*UTXO{
457 bc.NewHash([32]byte{0x01}): &UTXO{
458 OutputID: bc.NewHash([32]byte{0x01}),
459 AccountID: "testAccount",
462 bc.NewHash([32]byte{0x02}): &UTXO{
463 OutputID: bc.NewHash([32]byte{0x02}),
464 AccountID: "testAccount",
467 bc.NewHash([32]byte{0x03}): &UTXO{
468 OutputID: bc.NewHash([32]byte{0x03}),
469 AccountID: "testAccount",
473 reserved: map[bc.Hash]uint64{
474 bc.NewHash([32]byte{0x01}): 1,
476 reservations: map[uint64]*reservation{},
480 currentHeight: currentHeight,
481 unconfirmed: map[bc.Hash]*UTXO{
482 bc.NewHash([32]byte{0x01}): &UTXO{
483 OutputID: bc.NewHash([32]byte{0x01}),
484 AccountID: "testAccount",
487 bc.NewHash([32]byte{0x02}): &UTXO{
488 OutputID: bc.NewHash([32]byte{0x02}),
489 AccountID: "testAccount",
492 bc.NewHash([32]byte{0x03}): &UTXO{
493 OutputID: bc.NewHash([32]byte{0x03}),
494 AccountID: "testAccount",
498 reserved: map[bc.Hash]uint64{
499 bc.NewHash([32]byte{0x01}): 1,
500 bc.NewHash([32]byte{0x02}): 2,
501 bc.NewHash([32]byte{0x03}): 2,
503 reservations: map[uint64]*reservation{
508 OutputID: bc.NewHash([32]byte{0x03}),
509 AccountID: "testAccount",
513 OutputID: bc.NewHash([32]byte{0x02}),
514 AccountID: "testAccount",
519 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
525 exp: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
530 currentHeight: currentHeight,
531 unconfirmed: map[bc.Hash]*UTXO{
532 bc.NewHash([32]byte{0x01}): &UTXO{
533 OutputID: bc.NewHash([32]byte{0x01}),
534 AccountID: "testAccount",
536 Vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
539 reserved: map[bc.Hash]uint64{},
540 reservations: map[uint64]*reservation{},
544 currentHeight: currentHeight,
545 unconfirmed: map[bc.Hash]*UTXO{
546 bc.NewHash([32]byte{0x01}): &UTXO{
547 OutputID: bc.NewHash([32]byte{0x01}),
548 AccountID: "testAccount",
550 Vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
553 reserved: map[bc.Hash]uint64{
554 bc.NewHash([32]byte{0x01}): 1,
556 reservations: map[uint64]*reservation{
561 OutputID: bc.NewHash([32]byte{0x01}),
562 AccountID: "testAccount",
564 Vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
568 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
574 exp: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
575 vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
579 for i, c := range cases {
580 if _, err := c.before.Reserve("testAccount", &bc.AssetID{}, c.reserveAmount, true, c.vote, c.exp); err != c.err {
581 t.Errorf("case %d: got error %v want error %v", i, err, c.err)
583 checkUtxoKeeperEqual(t, i, &c.before, &c.after)
587 func TestReserveParticular(t *testing.T) {
588 currentHeight := func() uint64 { return 9527 }
589 testDB := dbm.NewDB("testdb", "leveldb", "temp")
590 defer os.RemoveAll("temp")
592 accountStore := newMockAccountStore(testDB)
604 currentHeight: currentHeight,
605 unconfirmed: map[bc.Hash]*UTXO{
606 bc.NewHash([32]byte{0x01}): &UTXO{
607 OutputID: bc.NewHash([32]byte{0x01}),
608 AccountID: "testAccount",
612 reserved: map[bc.Hash]uint64{
613 bc.NewHash([32]byte{0x01}): 0,
615 reservations: map[uint64]*reservation{},
619 currentHeight: currentHeight,
620 unconfirmed: map[bc.Hash]*UTXO{
621 bc.NewHash([32]byte{0x01}): &UTXO{
622 OutputID: bc.NewHash([32]byte{0x01}),
623 AccountID: "testAccount",
627 reserved: map[bc.Hash]uint64{
628 bc.NewHash([32]byte{0x01}): 0,
630 reservations: map[uint64]*reservation{},
632 reserveHash: bc.NewHash([32]byte{0x01}),
638 currentHeight: currentHeight,
639 unconfirmed: map[bc.Hash]*UTXO{
640 bc.NewHash([32]byte{0x01}): &UTXO{
641 OutputID: bc.NewHash([32]byte{0x01}),
642 AccountID: "testAccount",
647 reserved: map[bc.Hash]uint64{},
648 reservations: map[uint64]*reservation{},
652 currentHeight: currentHeight,
653 unconfirmed: map[bc.Hash]*UTXO{
654 bc.NewHash([32]byte{0x01}): &UTXO{
655 OutputID: bc.NewHash([32]byte{0x01}),
656 AccountID: "testAccount",
661 reserved: map[bc.Hash]uint64{},
662 reservations: map[uint64]*reservation{},
664 reserveHash: bc.NewHash([32]byte{0x01}),
670 currentHeight: currentHeight,
671 unconfirmed: map[bc.Hash]*UTXO{
672 bc.NewHash([32]byte{0x01}): &UTXO{
673 OutputID: bc.NewHash([32]byte{0x01}),
674 AccountID: "testAccount",
678 reserved: map[bc.Hash]uint64{},
679 reservations: map[uint64]*reservation{},
683 currentHeight: currentHeight,
684 unconfirmed: map[bc.Hash]*UTXO{
685 bc.NewHash([32]byte{0x01}): &UTXO{
686 OutputID: bc.NewHash([32]byte{0x01}),
687 AccountID: "testAccount",
691 reserved: map[bc.Hash]uint64{
692 bc.NewHash([32]byte{0x01}): 1,
694 reservations: map[uint64]*reservation{
699 OutputID: bc.NewHash([32]byte{0x01}),
700 AccountID: "testAccount",
705 expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
709 reserveHash: bc.NewHash([32]byte{0x01}),
711 exp: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC),
715 for i, c := range cases {
716 if _, err := c.before.ReserveParticular(c.reserveHash, true, c.exp); err != c.err {
717 t.Errorf("case %d: got error %v want error %v", i, err, c.err)
719 checkUtxoKeeperEqual(t, i, &c.before, &c.after)
723 func TestExpireReservation(t *testing.T) {
724 before := &utxoKeeper{
725 reservations: map[uint64]*reservation{
726 1: &reservation{expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC)},
727 2: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
728 3: &reservation{expiry: time.Date(2016, 8, 10, 0, 0, 0, 0, time.UTC)},
729 4: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
730 5: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
733 after := &utxoKeeper{
734 reservations: map[uint64]*reservation{
735 2: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
736 4: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
737 5: &reservation{expiry: time.Date(3016, 8, 10, 0, 0, 0, 0, time.UTC)},
740 before.expireReservation(time.Date(2017, 8, 10, 0, 0, 0, 0, time.UTC))
741 checkUtxoKeeperEqual(t, 0, before, after)
744 func TestFindUtxos(t *testing.T) {
745 currentHeight := func() uint64 { return 9527 }
746 testDB := dbm.NewDB("testdb", "leveldb", "temp")
752 accountStore := newMockAccountStore(testDB)
759 immatureAmount uint64
765 currentHeight: currentHeight,
766 unconfirmed: map[bc.Hash]*UTXO{},
769 useUnconfirmed: true,
770 wantUtxos: []*UTXO{},
776 currentHeight: currentHeight,
777 unconfirmed: map[bc.Hash]*UTXO{},
781 OutputID: bc.NewHash([32]byte{0x01}),
782 AccountID: "testAccount",
786 OutputID: bc.NewHash([32]byte{0x02}),
787 AccountID: "testAccount",
788 AssetID: bc.AssetID{V0: 6},
792 useUnconfirmed: false,
795 OutputID: bc.NewHash([32]byte{0x01}),
796 AccountID: "testAccount",
805 currentHeight: currentHeight,
806 unconfirmed: map[bc.Hash]*UTXO{},
810 OutputID: bc.NewHash([32]byte{0x02}),
811 AccountID: "testAccount",
816 useUnconfirmed: false,
817 wantUtxos: []*UTXO{},
823 currentHeight: currentHeight,
824 unconfirmed: map[bc.Hash]*UTXO{
825 bc.NewHash([32]byte{0x01}): &UTXO{
826 OutputID: bc.NewHash([32]byte{0x01}),
827 AccountID: "testAccount",
834 OutputID: bc.NewHash([32]byte{0x02}),
835 AccountID: "testAccount",
839 useUnconfirmed: false,
842 OutputID: bc.NewHash([32]byte{0x02}),
843 AccountID: "testAccount",
852 currentHeight: currentHeight,
853 unconfirmed: map[bc.Hash]*UTXO{
854 bc.NewHash([32]byte{0x11}): &UTXO{
855 OutputID: bc.NewHash([32]byte{0x01}),
856 AccountID: "testAccount",
863 OutputID: bc.NewHash([32]byte{0x02}),
864 AccountID: "testAccount",
868 useUnconfirmed: true,
871 OutputID: bc.NewHash([32]byte{0x02}),
872 AccountID: "testAccount",
876 OutputID: bc.NewHash([32]byte{0x01}),
877 AccountID: "testAccount",
886 currentHeight: currentHeight,
887 unconfirmed: map[bc.Hash]*UTXO{
888 bc.NewHash([32]byte{0x01}): &UTXO{
889 OutputID: bc.NewHash([32]byte{0x01}),
890 AccountID: "testAccount",
893 bc.NewHash([32]byte{0x02}): &UTXO{
894 OutputID: bc.NewHash([32]byte{0x02}),
902 OutputID: bc.NewHash([32]byte{0x03}),
903 AccountID: "testAccount",
907 OutputID: bc.NewHash([32]byte{0x04}),
912 useUnconfirmed: true,
915 OutputID: bc.NewHash([32]byte{0x03}),
916 AccountID: "testAccount",
920 OutputID: bc.NewHash([32]byte{0x01}),
921 AccountID: "testAccount",
930 currentHeight: currentHeight,
931 unconfirmed: map[bc.Hash]*UTXO{},
935 OutputID: bc.NewHash([32]byte{0x01}),
936 AccountID: "testAccount",
938 Vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
941 useUnconfirmed: false,
944 OutputID: bc.NewHash([32]byte{0x01}),
945 AccountID: "testAccount",
947 Vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
951 vote: []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
955 for i, c := range cases {
956 for _, u := range c.dbUtxos {
957 if err := c.uk.store.SetStandardUTXO(u.OutputID, u); err != nil {
962 gotUtxos, immatureAmount := c.uk.findUtxos("testAccount", &bc.AssetID{}, c.useUnconfirmed, c.vote)
963 if !testutil.DeepEqual(gotUtxos, c.wantUtxos) {
964 t.Errorf("case %d: got %v want %v", i, gotUtxos, c.wantUtxos)
966 if immatureAmount != c.immatureAmount {
967 t.Errorf("case %d: got %v want %v", i, immatureAmount, c.immatureAmount)
970 for _, u := range c.dbUtxos {
971 c.uk.store.DeleteStandardUTXO(u.OutputID)
976 func TestFindUTXO(t *testing.T) {
977 currentHeight := func() uint64 { return 9527 }
978 testDB := dbm.NewDB("testdb", "leveldb", "temp")
979 defer os.RemoveAll("temp")
981 accountStore := newMockAccountStore(testDB)
985 dbUtxos map[string]*UTXO
994 currentHeight: currentHeight,
995 unconfirmed: map[bc.Hash]*UTXO{},
997 dbUtxos: map[string]*UTXO{},
998 outHash: bc.NewHash([32]byte{0x01}),
1004 store: accountStore,
1005 currentHeight: currentHeight,
1006 unconfirmed: map[bc.Hash]*UTXO{
1007 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1010 dbUtxos: map[string]*UTXO{},
1011 outHash: bc.NewHash([32]byte{0x01}),
1013 useUnconfirmed: false,
1018 store: accountStore,
1019 currentHeight: currentHeight,
1020 unconfirmed: map[bc.Hash]*UTXO{
1021 bc.NewHash([32]byte{0x01}): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1024 dbUtxos: map[string]*UTXO{},
1025 outHash: bc.NewHash([32]byte{0x01}),
1026 wantUtxo: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1027 useUnconfirmed: true,
1032 store: accountStore,
1033 currentHeight: currentHeight,
1034 unconfirmed: map[bc.Hash]*UTXO{},
1036 dbUtxos: map[string]*UTXO{
1037 string(StandardUTXOKey(bc.NewHash([32]byte{0x01}))): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1039 outHash: bc.NewHash([32]byte{0x01}),
1040 wantUtxo: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1041 useUnconfirmed: false,
1046 store: accountStore,
1047 currentHeight: currentHeight,
1048 unconfirmed: map[bc.Hash]*UTXO{},
1050 dbUtxos: map[string]*UTXO{
1051 string(ContractUTXOKey(bc.NewHash([32]byte{0x01}))): &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1053 outHash: bc.NewHash([32]byte{0x01}),
1054 wantUtxo: &UTXO{OutputID: bc.NewHash([32]byte{0x01})},
1055 useUnconfirmed: false,
1060 for i, c := range cases {
1061 for k, u := range c.dbUtxos {
1062 data, err := json.Marshal(u)
1066 testDB.Set([]byte(k), data)
1069 gotUtxo, err := c.uk.findUtxo(c.outHash, c.useUnconfirmed)
1070 if !testutil.DeepEqual(gotUtxo, c.wantUtxo) {
1071 t.Errorf("case %d: got %v want %v", i, gotUtxo, c.wantUtxo)
1074 t.Errorf("case %d: got %v want %v", i, err, c.err)
1077 for _, u := range c.dbUtxos {
1078 c.uk.store.DeleteStandardUTXO(u.OutputID)
1083 func TestOptUTXOs(t *testing.T) {
1090 reservedAmount uint64
1094 reserved: map[bc.Hash]uint64{
1095 bc.NewHash([32]byte{0x01}): 1,
1100 wantUtxos: []*UTXO{},
1106 reserved: map[bc.Hash]uint64{
1107 bc.NewHash([32]byte{0x01}): 1,
1111 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1114 wantUtxos: []*UTXO{},
1120 reserved: map[bc.Hash]uint64{
1121 bc.NewHash([32]byte{0x01}): 1,
1125 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1126 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1127 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1131 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1132 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1139 reserved: map[bc.Hash]uint64{
1140 bc.NewHash([32]byte{0x01}): 1,
1141 bc.NewHash([32]byte{0x02}): 2,
1142 bc.NewHash([32]byte{0x03}): 3,
1146 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1147 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1148 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1151 wantUtxos: []*UTXO{},
1158 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1159 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 3},
1160 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 5},
1164 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1172 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1173 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1174 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1178 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1179 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1187 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1188 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 1},
1189 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 1},
1190 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 1},
1191 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 1},
1192 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 1},
1193 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 6},
1197 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 6},
1205 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1206 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 1},
1207 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 1},
1208 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 1},
1209 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 1},
1210 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 1},
1211 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 6},
1215 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 1},
1216 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 1},
1217 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 1},
1218 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 1},
1219 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1227 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1228 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1229 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1230 &UTXO{OutputID: bc.NewHash([32]byte{0x07}), Amount: 7},
1231 &UTXO{OutputID: bc.NewHash([32]byte{0x11}), Amount: 11},
1232 &UTXO{OutputID: bc.NewHash([32]byte{0x13}), Amount: 13},
1233 &UTXO{OutputID: bc.NewHash([32]byte{0x23}), Amount: 23},
1234 &UTXO{OutputID: bc.NewHash([32]byte{0x31}), Amount: 31},
1238 &UTXO{OutputID: bc.NewHash([32]byte{0x07}), Amount: 7},
1239 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1240 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1248 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1252 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1260 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1261 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1262 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1263 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 4},
1264 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1265 &UTXO{OutputID: bc.NewHash([32]byte{0x06}), Amount: 6},
1266 &UTXO{OutputID: bc.NewHash([32]byte{0x07}), Amount: 7},
1267 &UTXO{OutputID: bc.NewHash([32]byte{0x08}), Amount: 8},
1268 &UTXO{OutputID: bc.NewHash([32]byte{0x09}), Amount: 9},
1269 &UTXO{OutputID: bc.NewHash([32]byte{0x10}), Amount: 10},
1270 &UTXO{OutputID: bc.NewHash([32]byte{0x11}), Amount: 11},
1271 &UTXO{OutputID: bc.NewHash([32]byte{0x12}), Amount: 12},
1275 &UTXO{OutputID: bc.NewHash([32]byte{0x05}), Amount: 5},
1276 &UTXO{OutputID: bc.NewHash([32]byte{0x04}), Amount: 4},
1277 &UTXO{OutputID: bc.NewHash([32]byte{0x03}), Amount: 3},
1278 &UTXO{OutputID: bc.NewHash([32]byte{0x02}), Amount: 2},
1279 &UTXO{OutputID: bc.NewHash([32]byte{0x01}), Amount: 1},
1286 for i, c := range cases {
1287 got, optAmount, reservedAmount := c.uk.optUTXOs(c.input, c.inputAmount)
1288 if !testutil.DeepEqual(got, c.wantUtxos) {
1289 t.Errorf("case %d: utxos got %v want %v", i, got, c.wantUtxos)
1291 if optAmount != c.optAmount {
1292 t.Errorf("case %d: utxos got %v want %v", i, optAmount, c.optAmount)
1294 if reservedAmount != c.reservedAmount {
1295 t.Errorf("case %d: reservedAmount got %v want %v", i, reservedAmount, c.reservedAmount)
1300 func checkUtxoKeeperEqual(t *testing.T, i int, a, b *utxoKeeper) {
1301 if !testutil.DeepEqual(a.unconfirmed, b.unconfirmed) {
1302 t.Errorf("case %d: unconfirmed got %v want %v", i, a.unconfirmed, b.unconfirmed)
1304 if !testutil.DeepEqual(a.reserved, b.reserved) {
1305 t.Errorf("case %d: reserved got %v want %v", i, a.reserved, b.reserved)
1307 if !testutil.DeepEqual(a.reservations, b.reservations) {
1308 t.Errorf("case %d: reservations got %v want %v", i, a.reservations, b.reservations)
1312 type mockAccountStore struct {
1317 // NewAccountStore create new AccountStore.
1318 func newMockAccountStore(db dbm.DB) *mockAccountStore {
1319 return &mockAccountStore{
1326 UTXOPrefix = []byte{0x00, 0x3a}
1327 SUTXOPrefix = []byte{0x01, 0x3a}
1330 // StandardUTXOKey makes an account unspent outputs key to store
1331 func StandardUTXOKey(id bc.Hash) []byte {
1333 return append(UTXOPrefix, []byte(name)...)
1336 // ContractUTXOKey makes a smart contract unspent outputs key to store
1337 func ContractUTXOKey(id bc.Hash) []byte {
1339 return append(SUTXOPrefix, []byte(name)...)
1342 func (store *mockAccountStore) InitBatch() error { return nil }
1343 func (store *mockAccountStore) CommitBatch() error { return nil }
1344 func (store *mockAccountStore) DeleteAccount(*Account) error { return nil }
1345 func (store *mockAccountStore) GetAccountByAlias(string) (*Account, error) { return nil, nil }
1346 func (store *mockAccountStore) GetAccountByID(string) (*Account, error) { return nil, nil }
1347 func (store *mockAccountStore) GetAccountIndex([]chainkd.XPub) uint64 { return 0 }
1348 func (store *mockAccountStore) GetBip44ContractIndex(string, bool) uint64 { return 0 }
1349 func (store *mockAccountStore) GetCoinbaseArbitrary() []byte { return nil }
1350 func (store *mockAccountStore) GetContractIndex(string) uint64 { return 0 }
1351 func (store *mockAccountStore) GetControlProgram(bc.Hash) (*CtrlProgram, error) { return nil, nil }
1352 func (store *mockAccountStore) GetMiningAddress() (*CtrlProgram, error) { return nil, nil }
1353 func (store *mockAccountStore) ListAccounts(string) ([]*Account, error) { return nil, nil }
1354 func (store *mockAccountStore) ListControlPrograms() ([]*CtrlProgram, error) { return nil, nil }
1355 func (store *mockAccountStore) SetAccount(*Account) error { return nil }
1356 func (store *mockAccountStore) SetAccountIndex(*Account) error { return nil }
1357 func (store *mockAccountStore) SetBip44ContractIndex(string, bool, uint64) { return }
1358 func (store *mockAccountStore) SetCoinbaseArbitrary([]byte) { return }
1359 func (store *mockAccountStore) SetContractIndex(string, uint64) { return }
1360 func (store *mockAccountStore) SetControlProgram(bc.Hash, *CtrlProgram) error { return nil }
1361 func (store *mockAccountStore) SetMiningAddress(*CtrlProgram) error { return nil }
1363 // DeleteStandardUTXO delete utxo by outpu id
1364 func (store *mockAccountStore) DeleteStandardUTXO(outputID bc.Hash) {
1365 if store.batch == nil {
1366 store.accountDB.Delete(StandardUTXOKey(outputID))
1368 store.batch.Delete(StandardUTXOKey(outputID))
1372 // GetUTXO get standard utxo by id
1373 func (store *mockAccountStore) GetUTXO(outid bc.Hash) (*UTXO, error) {
1375 if data := store.accountDB.Get(StandardUTXOKey(outid)); data != nil {
1376 return u, json.Unmarshal(data, u)
1378 if data := store.accountDB.Get(ContractUTXOKey(outid)); data != nil {
1379 return u, json.Unmarshal(data, u)
1381 return nil, ErrMatchUTXO
1384 // ListUTXOs get utxos by accountID
1385 func (store *mockAccountStore) ListUTXOs() []*UTXO {
1386 utxoIter := store.accountDB.IteratorPrefix([]byte(UTXOPrefix))
1387 defer utxoIter.Release()
1390 for utxoIter.Next() {
1392 if err := json.Unmarshal(utxoIter.Value(), utxo); err != nil {
1393 log.WithFields(log.Fields{"module": logModule, "err": err}).Error("utxoKeeper findUtxos fail on unmarshal utxo")
1396 utxos = append(utxos, utxo)
1401 // SetStandardUTXO set standard utxo
1402 func (store *mockAccountStore) SetStandardUTXO(outputID bc.Hash, utxo *UTXO) error {
1403 data, err := json.Marshal(utxo)
1407 if store.batch == nil {
1408 store.accountDB.Set(StandardUTXOKey(outputID), data)
1410 store.batch.Set(StandardUTXOKey(outputID), data)