package database

import (
	"bytes"
	"os"
	"sort"
	"testing"

	"github.com/vapor/common"
	"github.com/vapor/testutil"

	"github.com/vapor/blockchain/signers"
	"github.com/vapor/crypto/ed25519/chainkd"

	acc "github.com/vapor/account"
	dbm "github.com/vapor/database/leveldb"
	"github.com/vapor/protocol/bc"
)

func TestDeleteAccount(t *testing.T) {
	cases := []struct {
		accounts      []*acc.Account
		deleteAccount *acc.Account
		want          []*acc.Account
	}{
		{
			accounts:      []*acc.Account{},
			deleteAccount: &acc.Account{},
			want:          []*acc.Account{},
		},
		{
			accounts: []*acc.Account{},
			deleteAccount: &acc.Account{
				ID:    "id-1",
				Alias: "alias-1",
			},
			want: []*acc.Account{},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID:    "id-1",
					Alias: "alias-1",
				},
				&acc.Account{
					ID:    "id-2",
					Alias: "alias-2",
				},
			},
			deleteAccount: &acc.Account{},
			want: []*acc.Account{
				&acc.Account{
					ID:    "id-1",
					Alias: "alias-1",
				},
				&acc.Account{
					ID:    "id-2",
					Alias: "alias-2",
				},
			},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID:    "id-1",
					Alias: "alias-1",
				},
				&acc.Account{
					ID:    "id-2",
					Alias: "alias-2",
				},
			},
			deleteAccount: &acc.Account{
				ID:    "id-3",
				Alias: "alias-3",
			},
			want: []*acc.Account{
				&acc.Account{
					ID:    "id-1",
					Alias: "alias-1",
				},
				&acc.Account{
					ID:    "id-2",
					Alias: "alias-2",
				},
			},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID:    "id-1",
					Alias: "alias-1",
				},
				&acc.Account{
					ID:    "id-2",
					Alias: "alias-2",
				},
			},
			deleteAccount: &acc.Account{
				ID:    "id-1",
				Alias: "alias-1",
			},
			want: []*acc.Account{
				&acc.Account{
					ID:    "id-2",
					Alias: "alias-2",
				},
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		// store mock accounts
		for _, a := range c.accounts {
			if err := as.SetAccount(a); err != nil {
				t.Fatal(err)
			}
		}

		// delete account
		if err := as.DeleteAccount(c.deleteAccount); err != nil {
			t.Fatal(err)
		}

		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		// get account by deleteAccount.ID, it should print ErrFindAccount
		if _, err := as.GetAccountByID(c.deleteAccount.ID); err != acc.ErrFindAccount {
			t.Fatal(err)
		}

		for _, a := range c.want {
			if _, err := as.GetAccountByID(a.ID); err != nil {
				t.Errorf("case %v: cann't find account, err: %v", i, err)
			}

			if _, err := as.GetAccountByAlias(a.Alias); err != nil {
				t.Errorf("case %v: cann't find account, err: %v", i, err)
			}
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestDeleteStandardUTXO(t *testing.T) {
	cases := []struct {
		utxos      []*acc.UTXO
		deleteUTXO *acc.UTXO
		want       []*acc.UTXO
	}{
		{
			utxos:      []*acc.UTXO{},
			deleteUTXO: &acc.UTXO{},
			want:       []*acc.UTXO{},
		},
		{
			utxos: []*acc.UTXO{
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x3e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
			},
			deleteUTXO: &acc.UTXO{
				OutputID: bc.NewHash([32]byte{0x3e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
			},
			want: []*acc.UTXO{},
		},
		{
			utxos: []*acc.UTXO{
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x3e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x2e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x3f, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x5e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x6e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x7e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
			},
			deleteUTXO: &acc.UTXO{},
			want: []*acc.UTXO{
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x3e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x2e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x3f, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x5e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x6e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x7e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
			},
		},
		{
			utxos: []*acc.UTXO{},
			deleteUTXO: &acc.UTXO{
				OutputID: bc.NewHash([32]byte{0x3e, 0x94, 0x5d, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
			},
			want: []*acc.UTXO{},
		},
		{
			utxos: []*acc.UTXO{
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x0e, 0x04, 0x50, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x00, 0x01, 0x02, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x01, 0x01, 0x02, 0x39, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
			},
			deleteUTXO: &acc.UTXO{
				OutputID: bc.NewHash([32]byte{0x01, 0x01, 0x02, 0x39, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
			},
			want: []*acc.UTXO{
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x0e, 0x04, 0x50, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x00, 0x01, 0x02, 0x35, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
				&acc.UTXO{
					OutputID: bc.NewHash([32]byte{0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				},
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		// store mock utxos
		for _, utxo := range c.utxos {
			if err := as.SetStandardUTXO(utxo.OutputID, utxo); err != nil {
				t.Fatal(err)
			}
		}

		// delete utxo
		as.DeleteStandardUTXO(c.deleteUTXO.OutputID)
		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotUTXOs, err := as.ListUTXOs()
		if err != nil {
			t.Fatal(err)
		}

		sort.Sort(SortUTXOs(gotUTXOs))
		sort.Sort(SortUTXOs(c.want))

		if !testutil.DeepEqual(gotUTXOs, c.want) {
			t.Errorf("case %v: got Delete Standard UTXOs, got: %v, want: %v.", i, gotUTXOs, c.want)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestGetAccountIndex(t *testing.T) {
	cases := []struct {
		account      *acc.Account
		currentIndex uint64
		want         uint64
	}{
		{
			account: &acc.Account{
				Signer: &signers.Signer{
					XPubs: []chainkd.XPub{
						[64]byte{0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
						[64]byte{0x09, 0x09, 0x09, 0x01, 0x01, 0x00, 0x04, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
					},
					KeyIndex: uint64(0),
				},
			},
			currentIndex: uint64(0),
			want:         uint64(0),
		},
		{
			account: &acc.Account{
				Signer: &signers.Signer{
					XPubs: []chainkd.XPub{
						[64]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
						[64]byte{0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
					},
					KeyIndex: uint64(1),
				},
			},
			currentIndex: uint64(1),
			want:         uint64(1),
		},
		{
			account: &acc.Account{
				Signer: &signers.Signer{
					XPubs: []chainkd.XPub{
						[64]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
						[64]byte{0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
					},
					KeyIndex: uint64(9),
				},
			},
			currentIndex: uint64(1),
			want:         uint64(9),
		},
		{
			account: &acc.Account{
				Signer: &signers.Signer{
					XPubs: []chainkd.XPub{
						[64]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
						[64]byte{0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
					},
					KeyIndex: uint64(10),
				},
			},
			currentIndex: uint64(88),
			want:         uint64(88),
		},
		{
			account: &acc.Account{
				Signer: &signers.Signer{
					XPubs:    []chainkd.XPub{},
					KeyIndex: uint64(0),
				},
			},
			currentIndex: uint64(0),
			want:         uint64(0),
		},
		{
			account: &acc.Account{
				Signer: &signers.Signer{
					XPubs: []chainkd.XPub{
						[64]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c, 0x01, 0x01, 0x51, 0x31, 0x71, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c},
					},
					KeyIndex: uint64(1),
				},
			},
			currentIndex: uint64(77),
			want:         uint64(77),
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		v := as.(*AccountStore)
		v.db.Set(accountIndexKey(c.account.XPubs), common.Unit64ToBytes(c.currentIndex))
		as.SetAccountIndex(c.account)
		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}
		gotIndex := as.GetAccountIndex(c.account.XPubs)
		if !testutil.DeepEqual(gotIndex, c.want) {
			t.Errorf("case %v: got incorrect account index, got: %v, want: %v.", i, gotIndex, c.want)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestGetBip44ContractIndex(t *testing.T) {
	cases := []struct {
		accountID string
		change    bool
		index     uint64
	}{
		{
			accountID: "",
			change:    false,
			index:     uint64(0),
		},
		{
			accountID: "account1",
			change:    true,
			index:     uint64(0),
		},
		{
			accountID: "account1",
			change:    false,
			index:     uint64(0),
		},
		{
			accountID: "account1",
			change:    true,
			index:     uint64(100),
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		as.SetBip44ContractIndex(c.accountID, c.change, c.index)
		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotIndex := as.GetBip44ContractIndex(c.accountID, c.change)
		if !testutil.DeepEqual(gotIndex, c.index) {
			t.Errorf("case %v: got incorrect bip44 contract index, got: %v, want: %v.", i, gotIndex, c.index)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestGetCoinbaseArbitrary(t *testing.T) {
	cases := []struct {
		arbitrary []byte
	}{
		{
			arbitrary: []byte{},
		},
		{
			arbitrary: []byte("test arbitrary"),
		},
		{
			arbitrary: []byte("test arbitrary test arbitrary test arbitrary test arbitrary test arbitrary"),
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		as.SetCoinbaseArbitrary(c.arbitrary)
		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotArbitrary := as.GetCoinbaseArbitrary()
		if !testutil.DeepEqual(gotArbitrary, c.arbitrary) {
			t.Errorf("case %v: got incorrect arbitrary, got: %v, want: %v.", i, gotArbitrary, c.arbitrary)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestGetContractIndex(t *testing.T) {
	cases := []struct {
		accountID string
		index     uint64
	}{
		{
			accountID: "",
			index:     uint64(0),
		},
		{
			accountID: "account1",
			index:     uint64(0),
		},
		{
			accountID: "",
			index:     uint64(1000),
		},
		{
			accountID: "account1",
			index:     uint64(8888),
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		as.SetContractIndex(c.accountID, c.index)
		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotIndex := as.GetContractIndex(c.accountID)
		if !testutil.DeepEqual(gotIndex, c.index) {
			t.Errorf("case %v: got contract index, got: %v, want: %v.", i, gotIndex, c.index)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestGetControlProgram(t *testing.T) {
	cases := []struct {
		hash    bc.Hash
		program *acc.CtrlProgram
	}{
		{
			hash:    bc.NewHash([32]byte{}),
			program: &acc.CtrlProgram{},
		},
		{
			hash:    bc.NewHash([32]byte{0x01, 0x01, 0x02, 0x39, 0x70, 0x30, 0xd4, 0x3b, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
			program: &acc.CtrlProgram{},
		},
		{
			hash: bc.NewHash([32]byte{}),
			program: &acc.CtrlProgram{
				AccountID: "account1",
				Address:   "address",
			},
		},
		{
			hash: bc.NewHash([32]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
			program: &acc.CtrlProgram{
				AccountID: "account1",
				Address:   "address",
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		as.SetControlProgram(c.hash, c.program)
		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotProgram, err := as.GetControlProgram(c.hash)
		if err != nil {
			t.Fatal(err)
		}

		if !testutil.DeepEqual(gotProgram, c.program) {
			t.Errorf("case %v: got control program, got: %v, want: %v.", i, gotProgram, c.program)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestGetMiningAddress(t *testing.T) {
	cases := []struct {
		program *acc.CtrlProgram
	}{
		{
			program: &acc.CtrlProgram{},
		},
		{
			program: &acc.CtrlProgram{
				AccountID: "account1",
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		if err := as.SetMiningAddress(c.program); err != nil {
			t.Fatal(err)
		}

		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotProgram, err := as.GetMiningAddress()
		if err != nil {
			t.Fatal(err)
		}

		if !testutil.DeepEqual(gotProgram, c.program) {
			t.Errorf("case %v: got mining address got: %v, want: %v.", i, gotProgram, c.program)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

func TestListAccounts(t *testing.T) {
	cases := []struct {
		accounts []*acc.Account
		id       string
		want     []*acc.Account
	}{
		{
			accounts: []*acc.Account{},
			id:       "",
			want:     []*acc.Account{},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID: "account1",
				},
			},
			id: "account",
			want: []*acc.Account{
				&acc.Account{
					ID: "account1",
				},
			},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID: "account2",
				},
				&acc.Account{
					ID: "test1",
				},
			},
			id: "account",
			want: []*acc.Account{
				&acc.Account{
					ID: "account2",
				},
			},
		},
		{
			accounts: []*acc.Account{},
			id:       "account",
			want:     []*acc.Account{},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID: "account1",
				},
				&acc.Account{
					ID: "test1",
				},
			},
			id: "",
			want: []*acc.Account{
				&acc.Account{
					ID: "account1",
				},
				&acc.Account{
					ID: "test1",
				},
			},
		},
		{
			accounts: []*acc.Account{
				&acc.Account{
					ID:    "account1",
					Alias: "alias1",
				},
				&acc.Account{
					ID:    "test1",
					Alias: "alias1",
				},
				&acc.Account{
					ID:    "account11",
					Alias: "alias1",
				},
				&acc.Account{
					ID:    "account2",
					Alias: "alias1",
				},
				&acc.Account{
					ID:    "account112",
					Alias: "alias1",
				},
			},
			id: "account1",
			want: []*acc.Account{
				&acc.Account{
					ID:    "account1",
					Alias: "alias1",
				},
				&acc.Account{
					ID:    "account11",
					Alias: "alias1",
				},
				&acc.Account{
					ID:    "account112",
					Alias: "alias1",
				},
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		for _, a := range c.accounts {
			if err := as.SetAccount(a); err != nil {
				t.Fatal(err)
			}
		}

		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotAccounts, err := as.ListAccounts(c.id)
		if err != nil {
			t.Fatal(err)
		}

		sort.Sort(SortAccounts(gotAccounts))
		sort.Sort(SortAccounts(c.want))

		if !testutil.DeepEqual(gotAccounts, c.want) {
			t.Errorf("case %v: list accounts, got: %v, want: %v.", i, gotAccounts, c.want)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

type SortAccounts []*acc.Account

func (s SortAccounts) Len() int { return len(s) }
func (s SortAccounts) Less(i, j int) bool {
	return bytes.Compare([]byte(s[i].ID), []byte(s[j].ID)) < 0
}
func (s SortAccounts) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func TestListControlPrograms(t *testing.T) {
	cases := []struct {
		hashs    []bc.Hash
		programs []*acc.CtrlProgram
	}{
		{
			hashs:    []bc.Hash{},
			programs: []*acc.CtrlProgram{},
		},
		{
			hashs: []bc.Hash{
				bc.NewHash([32]byte{}),
			},
			programs: []*acc.CtrlProgram{
				&acc.CtrlProgram{},
			},
		},
		{
			hashs: []bc.Hash{
				bc.NewHash([32]byte{}),
				bc.NewHash([32]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
				bc.NewHash([32]byte{0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
			},
			programs: []*acc.CtrlProgram{
				&acc.CtrlProgram{
					AccountID:      "account0",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account1",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         false,
				},
				&acc.CtrlProgram{
					AccountID:      "account2",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         false,
				},
				&acc.CtrlProgram{
					AccountID:      "account3",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account4",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account5",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account6",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account7",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account8",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
				&acc.CtrlProgram{
					AccountID:      "account9",
					Address:        "address",
					KeyIndex:       uint64(0),
					ControlProgram: []byte("program"),
					Change:         true,
				},
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		for j := 0; j < len(c.hashs); j++ {
			if err := as.SetControlProgram(c.hashs[j], c.programs[j]); err != nil {
				t.Fatal(err)
			}
		}

		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotPrograms, err := as.ListControlPrograms()
		if err != nil {
			t.Fatal(err)
		}

		sort.Sort(SortPrograms(gotPrograms))
		sort.Sort(SortPrograms(c.programs))

		if !testutil.DeepEqual(gotPrograms, c.programs) {
			t.Errorf("case %v: list control programs, got: %v, want: %v.", i, gotPrograms, c.programs)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

type SortPrograms []*acc.CtrlProgram

func (s SortPrograms) Len() int { return len(s) }
func (s SortPrograms) Less(i, j int) bool {
	return bytes.Compare([]byte(s[i].AccountID), []byte(s[j].AccountID)) < 0
}
func (s SortPrograms) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

func TestListUTXOs(t *testing.T) {
	cases := []struct {
		utxos []*acc.UTXO
	}{
		{
			utxos: []*acc.UTXO{},
		},
		{
			utxos: []*acc.UTXO{
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{}),
					Address:   "address0",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
			},
		},
		{
			utxos: []*acc.UTXO{
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address0",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
			},
		},
		{
			utxos: []*acc.UTXO{
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address0",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address1",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address2",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address3",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address4",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address5",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address6",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address7",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address8",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
				&acc.UTXO{
					OutputID:  bc.NewHash([32]byte{0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3d, 0xe3, 0xdd, 0x80, 0x67, 0x29, 0x9a, 0x5e, 0x09, 0xf9, 0xfb, 0x2b, 0xad, 0x5f, 0x92, 0xc8, 0x69, 0xd1, 0x42, 0x39, 0x74, 0x9a, 0xd1, 0x1c}),
					Address:   "address9",
					Amount:    uint64(0),
					Change:    true,
					AccountID: "account",
					SourcePos: uint64(0),
				},
			},
		},
	}

	for i, c := range cases {
		testDB := dbm.NewDB("testdb", "leveldb", "temp")
		accountStore := NewAccountStore(testDB)
		as := accountStore.InitBatch()
		for j := 0; j < len(c.utxos); j++ {
			if err := as.SetStandardUTXO(c.utxos[j].OutputID, c.utxos[j]); err != nil {
				t.Fatal(err)
			}
		}

		if err := as.CommitBatch(); err != nil {
			t.Fatal(err)
		}

		gotUTXOs, err := as.ListUTXOs()
		if err != nil {
			t.Fatal(err)
		}

		sort.Sort(SortUTXOs(gotUTXOs))
		sort.Sort(SortUTXOs(c.utxos))

		if !testutil.DeepEqual(gotUTXOs, c.utxos) {
			t.Errorf("case %v: list utxos, got: %v, want: %v.", i, gotUTXOs, c.utxos)
		}

		testDB.Close()
		os.RemoveAll("temp")
	}
}

type SortUTXOs []*acc.UTXO

func (s SortUTXOs) Len() int { return len(s) }
func (s SortUTXOs) Less(i, j int) bool {
	return bytes.Compare(s[i].OutputID.Bytes(), s[j].OutputID.Bytes()) < 0
}
func (s SortUTXOs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
