func newDummyCredit(t *testing.T, dbtx walletdb.ReadWriteTx, pool *Pool, series uint32, index Index, branch Branch,
	txHash []byte, outpointIdx uint32) Credit {
	var hash chainhash.Hash
	if err := hash.SetBytes(txHash); err != nil {
		t.Fatal(err)
	}
	TstEnsureUsedAddr(t, dbtx, pool, series, branch, index)
	addr := TstNewWithdrawalAddress(t, dbtx, pool, series, branch, index)
	c := wtxmgr.Credit{
		OutPoint: wire.OutPoint{
			Hash:  hash,
			Index: outpointIdx,
		},
	}
	return newCredit(c, *addr)
}
newDummyCredit creates a new Credit with the given hash and outpointIdx,
locked to the votingpool address identified by the given
series/index/branch.
package votingpool
import (
	"bytes"
	"reflect"
	"sort"
	"testing"
	"github.com/gcash/bchd/chaincfg/chainhash"
	"github.com/gcash/bchd/wire"
	"github.com/gcash/bchutil"
	"github.com/gcash/bchwallet/walletdb"
	"github.com/gcash/bchwallet/wtxmgr"
)
var (
	dustThreshold bchutil.Amount = 1e4
)
func TestGetEligibleInputs(t *testing.T) {
	tearDown, db, pool, store := TstCreatePoolAndTxStore(t)
	defer tearDown()
	dbtx, err := db.BeginReadWriteTx()
	if err != nil {
		t.Fatal(err)
	}
	defer dbtx.Commit()
	ns, addrmgrNs := TstRWNamespaces(dbtx)
	series := []TstSeriesDef{
		{ReqSigs: 2, PubKeys: TstPubKeys[1:4], SeriesID: 1},
		{ReqSigs: 2, PubKeys: TstPubKeys[3:6], SeriesID: 2},
	}
	TstCreateSeries(t, dbtx, pool, series)
	scripts := append(
		getPKScriptsForAddressRange(t, dbtx, pool, 1, 0, 2, 0, 4),
		getPKScriptsForAddressRange(t, dbtx, pool, 2, 0, 2, 0, 6)...)
	expNoEligibleInputs := 2 * len(scripts)
	eligibleAmounts := []int64{int64(dustThreshold + 1), int64(dustThreshold + 1)}
	var inputs []wtxmgr.Credit
	for i := 0; i < len(scripts); i++ {
		created := TstCreateCreditsOnStore(t, dbtx, store, scripts[i], eligibleAmounts)
		inputs = append(inputs, created...)
	}
	startAddr := TstNewWithdrawalAddress(t, dbtx, pool, 1, 0, 0)
	lastSeriesID := uint32(2)
	currentBlock := TstInputsBlock + eligibleInputMinConfirmations + 1
	var eligibles []Credit
	txmgrNs := dbtx.ReadBucket(txmgrNamespaceKey)
	TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
		eligibles, err = pool.getEligibleInputs(ns, addrmgrNs,
			store, txmgrNs, *startAddr, lastSeriesID, dustThreshold, currentBlock,
			eligibleInputMinConfirmations)
	})
	if err != nil {
		t.Fatal("InputSelection failed:", err)
	}
	if len(eligibles) != expNoEligibleInputs {
		t.Fatalf("Wrong number of eligible inputs returned. Got: %d, want: %d.",
			len(eligibles), expNoEligibleInputs)
	}
	if !sort.IsSorted(sort.Reverse(byAddress(eligibles))) {
		t.Fatal("Eligible inputs are not sorted.")
	}
	checkUniqueness(t, eligibles)
}
func TestNextAddrWithVaryingHighestIndices(t *testing.T) {
	tearDown, db, pool := TstCreatePool(t)
	defer tearDown()
	dbtx, err := db.BeginReadWriteTx()
	if err != nil {
		t.Fatal(err)
	}
	defer dbtx.Commit()
	ns, addrmgrNs := TstRWNamespaces(dbtx)
	series := []TstSeriesDef{
		{ReqSigs: 2, PubKeys: TstPubKeys[1:4], SeriesID: 1},
	}
	TstCreateSeries(t, dbtx, pool, series)
	stopSeriesID := uint32(2)
	TstEnsureUsedAddr(t, dbtx, pool, 1, Branch(0), 2)
	TstEnsureUsedAddr(t, dbtx, pool, 1, Branch(1), 1)
	addr := TstNewWithdrawalAddress(t, dbtx, pool, 1, 0, 1)
	TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
		addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
	})
	if err != nil {
		t.Fatalf("Failed to get next address: %v", err)
	}
	checkWithdrawalAddressMatches(t, addr, 1, Branch(1), 1)
	TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
		addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
	})
	if err != nil {
		t.Fatalf("Failed to get next address: %v", err)
	}
	checkWithdrawalAddressMatches(t, addr, 1, Branch(0), 2)
	TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
		addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
	})
	if err != nil {
		t.Fatalf("Failed to get next address: %v", err)
	}
	if addr != nil {
		t.Fatalf("Wrong next addr; got '%s', want 'nil'", addr.addrIdentifier())
	}
}
func TestNextAddr(t *testing.T) {
	tearDown, db, pool := TstCreatePool(t)
	defer tearDown()
	dbtx, err := db.BeginReadWriteTx()
	if err != nil {
		t.Fatal(err)
	}
	defer dbtx.Commit()
	ns, addrmgrNs := TstRWNamespaces(dbtx)
	series := []TstSeriesDef{
		{ReqSigs: 2, PubKeys: TstPubKeys[1:4], SeriesID: 1},
		{ReqSigs: 2, PubKeys: TstPubKeys[3:6], SeriesID: 2},
	}
	TstCreateSeries(t, dbtx, pool, series)
	stopSeriesID := uint32(3)
	lastIdx := Index(10)
	for _, i := range []int{0, 1, 2, 3} {
		TstEnsureUsedAddr(t, dbtx, pool, 1, Branch(i), lastIdx)
	}
	addr := TstNewWithdrawalAddress(t, dbtx, pool, 1, 0, lastIdx-1)
	for _, i := range []int{1, 2, 3} {
		TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
			addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
		})
		if err != nil {
			t.Fatalf("Failed to get next address: %v", err)
		}
		checkWithdrawalAddressMatches(t, addr, 1, Branch(i), lastIdx-1)
	}
	for _, i := range []int{0, 1, 2, 3} {
		TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
			addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
		})
		if err != nil {
			t.Fatalf("Failed to get next address: %v", err)
		}
		checkWithdrawalAddressMatches(t, addr, 1, Branch(i), lastIdx)
	}
	for _, i := range []int{0, 1, 2, 3} {
		TstEnsureUsedAddr(t, dbtx, pool, 2, Branch(i), lastIdx)
	}
	for _, i := range []int{0, 1, 2, 3} {
		TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
			addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
		})
		if err != nil {
			t.Fatalf("Failed to get next address: %v", err)
		}
		checkWithdrawalAddressMatches(t, addr, 2, Branch(i), 0)
	}
	addr = TstNewWithdrawalAddress(t, dbtx, pool, 2, 3, lastIdx)
	TstRunWithManagerUnlocked(t, pool.Manager(), addrmgrNs, func() {
		addr, err = nextAddr(pool, ns, addrmgrNs, addr.seriesID, addr.branch, addr.index, stopSeriesID)
	})
	if err != nil {
		t.Fatalf("Failed to get next address: %v", err)
	}
	if addr != nil {
		t.Fatalf("Wrong WithdrawalAddress; got %s, want nil", addr.addrIdentifier())
	}
}
func TestEligibleInputsAreEligible(t *testing.T) {
	tearDown, db, pool := TstCreatePool(t)
	defer tearDown()
	dbtx, err := db.BeginReadWriteTx()
	if err != nil {
		t.Fatal(err)
	}
	defer dbtx.Commit()
	var chainHeight int32 = 1000
	_, credits := tstCreateCreditsOnNewSeries(t, dbtx, pool, []int64{int64(dustThreshold)})
	c := credits[0]
	c.BlockMeta.Height = int32(eligibleInputMinConfirmations)
	if !pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
		t.Errorf("Input is not eligible and it should be.")
	}
}
func TestNonEligibleInputsAreNotEligible(t *testing.T) {
	tearDown, db, pool := TstCreatePool(t)
	defer tearDown()
	dbtx, err := db.BeginReadWriteTx()
	if err != nil {
		t.Fatal(err)
	}
	defer dbtx.Commit()
	var chainHeight int32 = 1000
	_, credits := tstCreateCreditsOnNewSeries(t, dbtx, pool, []int64{int64(dustThreshold - 1)})
	c := credits[0]
	c.BlockMeta.Height = int32(eligibleInputMinConfirmations)
	if pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
		t.Errorf("Input is eligible and it should not be.")
	}
	_, credits = tstCreateCreditsOnNewSeries(t, dbtx, pool, []int64{int64(dustThreshold)})
	c = credits[0]
	c.BlockMeta.Height = int32(902)
	if pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) {
		t.Errorf("Input is eligible and it should not be.")
	}
}
func TestCreditSortingByAddress(t *testing.T) {
	teardown, db, pool := TstCreatePool(t)
	defer teardown()
	dbtx, err := db.BeginReadWriteTx()
	if err != nil {
		t.Fatal(err)
	}
	defer dbtx.Commit()
	series := []TstSeriesDef{
		{ReqSigs: 2, PubKeys: TstPubKeys[1:4], SeriesID: 1},
		{ReqSigs: 2, PubKeys: TstPubKeys[3:6], SeriesID: 2},
	}
	TstCreateSeries(t, dbtx, pool, series)
	shaHash0 := bytes.Repeat([]byte{0}, 32)
	shaHash1 := bytes.Repeat([]byte{1}, 32)
	shaHash2 := bytes.Repeat([]byte{2}, 32)
	c0 := newDummyCredit(t, dbtx, pool, 1, 0, 0, shaHash0, 0)
	c1 := newDummyCredit(t, dbtx, pool, 1, 0, 0, shaHash0, 1)
	c2 := newDummyCredit(t, dbtx, pool, 1, 0, 0, shaHash1, 0)
	c3 := newDummyCredit(t, dbtx, pool, 1, 0, 0, shaHash2, 0)
	c4 := newDummyCredit(t, dbtx, pool, 1, 0, 1, shaHash0, 0)
	c5 := newDummyCredit(t, dbtx, pool, 1, 1, 0, shaHash0, 0)
	c6 := newDummyCredit(t, dbtx, pool, 2, 0, 0, shaHash0, 0)
	randomCredits := [][]Credit{
		{c6, c5, c4, c3, c2, c1, c0},
		{c2, c1, c0, c6, c5, c4, c3},
		{c6, c4, c5, c2, c3, c0, c1},
	}
	want := []Credit{c0, c1, c2, c3, c4, c5, c6}
	for _, random := range randomCredits {
		sort.Sort(byAddress(random))
		got := random
		if len(got) != len(want) {
			t.Fatalf("Sorted Credit slice size wrong: Got: %d, want: %d",
				len(got), len(want))
		}
		for idx := 0; idx < len(want); idx++ {
			if !reflect.DeepEqual(got[idx], want[idx]) {
				t.Errorf("Wrong output index. Got: %v, want: %v",
					got[idx], want[idx])
			}
		}
	}
}