From 847c7d8df9c8a317f47a2e586efc7ddd61df90b2 Mon Sep 17 00:00:00 2001 From: Guilherme Salgado Date: Tue, 7 Apr 2015 16:40:13 +0100 Subject: [PATCH] Migrate votingpool tests to use wtxmgr --- votingpool/example_test.go | 22 ++- votingpool/factory_test.go | 133 +++++++++++------- votingpool/input_selection.go | 99 ++++++------- votingpool/input_selection_wb_test.go | 137 +++++++----------- votingpool/withdrawal.go | 56 +++++--- votingpool/withdrawal_test.go | 2 +- votingpool/withdrawal_wb_test.go | 194 ++++++++++++-------------- 7 files changed, 316 insertions(+), 327 deletions(-) diff --git a/votingpool/example_test.go b/votingpool/example_test.go index 7901063..202f6fe 100644 --- a/votingpool/example_test.go +++ b/votingpool/example_test.go @@ -26,11 +26,11 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwallet/legacy/txstore" "github.com/btcsuite/btcwallet/votingpool" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/walletdb" _ "github.com/btcsuite/btcwallet/walletdb/bdb" + "github.com/btcsuite/btcwallet/wtxmgr" ) var ( @@ -166,8 +166,7 @@ func Example_empowerSeries() { // } -// This example demonstrates how to empower a series by loading the private -// key for one of the series' public keys. +// This example demonstrates how to use the Pool.StartWithdrawal method. func Example_startWithdrawal() { // Create the address manager and votingpool DB namespace. See the example // for the Create() function for more info on how this is done. @@ -319,11 +318,22 @@ func exampleCreatePoolAndSeries(mgr *waddrmgr.Manager, vpNamespace walletdb.Name return pool, seriesID, nil } -func exampleCreateTxStore() (*txstore.Store, func(), error) { - dir, err := ioutil.TempDir("", "tx.bin") +func exampleCreateTxStore() (*wtxmgr.Store, func(), error) { + dir, err := ioutil.TempDir("", "pool_test_txstore") + if err != nil { + return nil, nil, err + } + db, err := walletdb.Create("bdb", filepath.Join(dir, "txstore.db")) + if err != nil { + return nil, nil, err + } + wtxmgrNamespace, err := db.Namespace([]byte("testtxstore")) + if err != nil { + return nil, nil, err + } + s, err := wtxmgr.Create(wtxmgrNamespace) if err != nil { return nil, nil, err } - s := txstore.New(dir) return s, func() { os.RemoveAll(dir) }, nil } diff --git a/votingpool/factory_test.go b/votingpool/factory_test.go index 917fded..56f324e 100644 --- a/votingpool/factory_test.go +++ b/votingpool/factory_test.go @@ -25,15 +25,16 @@ import ( "path/filepath" "sync/atomic" "testing" + "time" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/hdkeychain" - "github.com/btcsuite/btcwallet/legacy/txstore" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/walletdb" + "github.com/btcsuite/btcwallet/wtxmgr" ) var ( @@ -51,11 +52,10 @@ func getUniqueID() uint32 { } // createWithdrawalTx creates a withdrawalTx with the given input and output amounts. -func createWithdrawalTx(t *testing.T, pool *Pool, store *txstore.Store, inputAmounts []int64, - outputAmounts []int64) *withdrawalTx { +func createWithdrawalTx(t *testing.T, pool *Pool, inputAmounts []int64, outputAmounts []int64) *withdrawalTx { net := pool.Manager().ChainParams() tx := newWithdrawalTx() - _, credits := TstCreateCredits(t, pool, inputAmounts, store) + _, credits := TstCreateCreditsOnNewSeries(t, pool, inputAmounts) for _, c := range credits { tx.addInput(c) } @@ -136,12 +136,23 @@ func TstCreatePkScript(t *testing.T, p *Pool, seriesID uint32, branch Branch, id return pkScript } -func TstCreateTxStore(t *testing.T) (store *txstore.Store, tearDown func()) { - dir, err := ioutil.TempDir("", "tx.bin") +func TstCreateTxStore(t *testing.T) (store *wtxmgr.Store, tearDown func()) { + dir, err := ioutil.TempDir("", "pool_test_txstore") if err != nil { - t.Fatalf("Failed to create db file: %v", err) + t.Fatalf("Failed to create txstore dir: %v", err) + } + db, err := walletdb.Create("bdb", filepath.Join(dir, "txstore.db")) + if err != nil { + t.Fatalf("Failed to create walletdb: %v", err) + } + wtxmgrNamespace, err := db.Namespace([]byte("testtxstore")) + if err != nil { + t.Fatalf("Failed to create walletdb namespace: %v", err) + } + s, err := wtxmgr.Create(wtxmgrNamespace) + if err != nil { + t.Fatalf("Failed to create txstore: %v", err) } - s := txstore.New(dir) return s, func() { os.RemoveAll(dir) } } @@ -201,15 +212,12 @@ func TstCreateSeriesDef(t *testing.T, pool *Pool, reqSigs uint32, keys []*hdkeyc pubkey, _ := key.Neuter() pubKeys[i] = pubkey.String() } - seriesID := uint32(len(pool.seriesLookup)) - if seriesID == 0 { - seriesID++ - } + seriesID := uint32(len(pool.seriesLookup)) + 1 return TstSeriesDef{ ReqSigs: reqSigs, SeriesID: seriesID, PubKeys: pubKeys, PrivKeys: privKeys} } -func TstCreatePoolAndTxStore(t *testing.T) (tearDown func(), pool *Pool, store *txstore.Store) { +func TstCreatePoolAndTxStore(t *testing.T) (tearDown func(), pool *Pool, store *wtxmgr.Store) { mgrTearDown, _, pool := TstCreatePool(t) store, storeTearDown := TstCreateTxStore(t) tearDown = func() { @@ -219,12 +227,11 @@ func TstCreatePoolAndTxStore(t *testing.T) (tearDown func(), pool *Pool, store * return tearDown, pool, store } -// TstCreateCredits creates a new Series (with a unique ID) and a slice of -// credits locked to the series' address with branch==1 and index==0. The new -// Series will use a 2-of-3 configuration and will be empowered with all of its -// private keys. -func TstCreateCredits(t *testing.T, pool *Pool, amounts []int64, store *txstore.Store) ( - uint32, []Credit) { +// TstCreateCreditsOnNewSeries creates a new Series (with a unique ID) and a +// slice of credits locked to the series' address with branch==1 and index==0. +// The new Series will use a 2-of-3 configuration and will be empowered with +// all of its private keys. +func TstCreateCreditsOnNewSeries(t *testing.T, pool *Pool, amounts []int64) (uint32, []credit) { masters := []*hdkeychain.ExtendedKey{ TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)), TstCreateMasterKey(t, bytes.Repeat(uint32ToBytes(getUniqueID()), 4)), @@ -232,56 +239,84 @@ func TstCreateCredits(t *testing.T, pool *Pool, amounts []int64, store *txstore. } def := TstCreateSeriesDef(t, pool, 2, masters) TstCreateSeries(t, pool, []TstSeriesDef{def}) - return def.SeriesID, TstCreateCreditsOnSeries(t, pool, def.SeriesID, amounts, store) + return def.SeriesID, TstCreateSeriesCredits(t, pool, def.SeriesID, amounts) } -// TstCreateCreditsOnSeries creates a slice of credits locked to the given -// series' address with branch==1 and index==0. -func TstCreateCreditsOnSeries(t *testing.T, pool *Pool, seriesID uint32, amounts []int64, - store *txstore.Store) []Credit { +// TstCreateSeriesCredits creates a new credit for every item in the amounts +// slice, locked to the given series' address with branch==1 and index==0. +func TstCreateSeriesCredits(t *testing.T, pool *Pool, seriesID uint32, amounts []int64) []credit { + addr := TstNewWithdrawalAddress(t, pool, seriesID, Branch(1), Index(0)) + pkScript, err := txscript.PayToAddrScript(addr.addr) + if err != nil { + t.Fatal(err) + } + msgTx := createMsgTx(pkScript, amounts) + txSha := msgTx.TxSha() + credits := make([]credit, len(amounts)) + for i := range msgTx.TxOut { + c := wtxmgr.Credit{ + OutPoint: wire.OutPoint{ + Hash: txSha, + Index: uint32(i), + }, + BlockMeta: wtxmgr.BlockMeta{ + Block: wtxmgr.Block{Height: TstInputsBlock}, + }, + Amount: btcutil.Amount(msgTx.TxOut[i].Value), + PkScript: msgTx.TxOut[i].PkScript, + } + credits[i] = newCredit(c, *addr) + } + return credits +} + +// TstCreateSeriesCreditsOnStore inserts a new credit in the given store for +// every item in the amounts slice. These credits are locked to the votingpool +// address composed of the given seriesID, branch==1 and index==0. +func TstCreateSeriesCreditsOnStore(t *testing.T, pool *Pool, seriesID uint32, amounts []int64, + store *wtxmgr.Store) []credit { branch := Branch(1) idx := Index(0) pkScript := TstCreatePkScript(t, pool, seriesID, branch, idx) - eligible := make([]Credit, len(amounts)) - for i, credit := range TstCreateInputs(t, store, pkScript, amounts) { + eligible := make([]credit, len(amounts)) + for i, credit := range TstCreateCreditsOnStore(t, store, pkScript, amounts) { eligible[i] = newCredit(credit, *TstNewWithdrawalAddress(t, pool, seriesID, branch, idx)) } return eligible } -// TstCreateInputs is a convenience function. See TstCreateInputsOnBlock -// for a more flexible version. -func TstCreateInputs(t *testing.T, store *txstore.Store, pkScript []byte, amounts []int64) []txstore.Credit { - return TstCreateInputsOnBlock(t, store, 1, pkScript, amounts) -} - -// TstCreateInputsOnBlock creates a number of inputs by creating a transaction -// with a number of outputs corresponding to the elements of the amounts slice. -// -// The transaction is added to a block and the index and blockheight must be -// specified. -func TstCreateInputsOnBlock(t *testing.T, s *txstore.Store, - blockTxIndex int, pkScript []byte, amounts []int64) []txstore.Credit { +// TstCreateCreditsOnStore inserts a new credit in the given store for +// every item in the amounts slice. +func TstCreateCreditsOnStore(t *testing.T, s *wtxmgr.Store, pkScript []byte, + amounts []int64) []wtxmgr.Credit { msgTx := createMsgTx(pkScript, amounts) - block := &txstore.Block{ - Height: TstInputsBlock, + meta := &wtxmgr.BlockMeta{ + Block: wtxmgr.Block{Height: TstInputsBlock}, } - tx := btcutil.NewTx(msgTx) - tx.SetIndex(blockTxIndex) - - r, err := s.InsertTx(tx, block) + rec, err := wtxmgr.NewTxRecordFromMsgTx(msgTx, time.Now()) if err != nil { + t.Fatal(err) + } + + if err := s.InsertTx(rec, meta); err != nil { t.Fatal("Failed to create inputs: ", err) } - credits := make([]txstore.Credit, len(msgTx.TxOut)) + credits := make([]wtxmgr.Credit, len(msgTx.TxOut)) for i := range msgTx.TxOut { - credit, err := r.AddCredit(uint32(i), false) - if err != nil { + if err := s.AddCredit(rec, meta, uint32(i), false); err != nil { t.Fatal("Failed to create inputs: ", err) } - credits[i] = credit + credits[i] = wtxmgr.Credit{ + OutPoint: wire.OutPoint{ + Hash: rec.Hash, + Index: uint32(i), + }, + BlockMeta: *meta, + Amount: btcutil.Amount(msgTx.TxOut[i].Value), + PkScript: msgTx.TxOut[i].PkScript, + } } return credits } diff --git a/votingpool/input_selection.go b/votingpool/input_selection.go index cb345df..33206ef 100644 --- a/votingpool/input_selection.go +++ b/votingpool/input_selection.go @@ -22,61 +22,31 @@ import ( "sort" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwallet/legacy/txstore" + "github.com/btcsuite/btcwallet/wtxmgr" ) const eligibleInputMinConfirmations = 100 -// Credit is an abstraction over txstore.Credit used in the construction of +// credit is an abstraction over wtxmgr.Credit used in the construction of // voting pool withdrawal transactions. -type Credit interface { - TxSha() *wire.ShaHash - OutputIndex() uint32 - Address() WithdrawalAddress - Amount() btcutil.Amount - OutPoint() *wire.OutPoint - TxOut() *wire.TxOut -} - -// credit implements the Credit interface. type credit struct { - txstore.Credit + wtxmgr.Credit addr WithdrawalAddress } -// newCredit initialises a new credit. -func newCredit(c txstore.Credit, addr WithdrawalAddress) *credit { - return &credit{Credit: c, addr: addr} +func newCredit(c wtxmgr.Credit, addr WithdrawalAddress) credit { + return credit{Credit: c, addr: addr} } func (c *credit) String() string { - return fmt.Sprintf("credit of %v to %v", c.Amount(), c.Address()) + return fmt.Sprintf("credit of %v locked to %v", c.Amount, c.addr) } -// TxSha returns the sha hash of the underlying transaction. -func (c *credit) TxSha() *wire.ShaHash { - return c.Credit.TxRecord.Tx().Sha() -} - -// OutputIndex returns the outputindex of the ouput in the underlying -// transaction. -func (c *credit) OutputIndex() uint32 { - return c.Credit.OutputIndex -} - -// Address returns the voting pool address. -func (c *credit) Address() WithdrawalAddress { - return c.addr -} - -// Compile time check that credit implements Credit interface. -var _ Credit = (*credit)(nil) - // byAddress defines the methods needed to satisify sort.Interface to sort a -// slice of Credits by their address. -type byAddress []Credit +// slice of credits by their address. +type byAddress []credit func (c byAddress) Len() int { return len(c) } func (c byAddress) Swap(i, j int) { c[i], c[j] = c[j], c[i] } @@ -86,8 +56,8 @@ func (c byAddress) Swap(i, j int) { c[i], c[j] = c[j], c[i] } // the lexicographic ordering defined on the tuple (SeriesID, Index, // Branch, TxSha, OutputIndex). func (c byAddress) Less(i, j int) bool { - iAddr := c[i].Address() - jAddr := c[j].Address() + iAddr := c[i].addr + jAddr := c[j].addr if iAddr.seriesID < jAddr.seriesID { return true } @@ -112,7 +82,7 @@ func (c byAddress) Less(i, j int) bool { } // The seriesID, index, and branch are equal, so compare hash. - txidComparison := bytes.Compare(c[i].TxSha().Bytes(), c[j].TxSha().Bytes()) + txidComparison := bytes.Compare(c[i].OutPoint.Hash.Bytes(), c[j].OutPoint.Hash.Bytes()) if txidComparison < 0 { return true } @@ -122,14 +92,14 @@ func (c byAddress) Less(i, j int) bool { // The seriesID, index, branch, and hash are equal, so compare output // index. - return c[i].OutputIndex() < c[j].OutputIndex() + return c[i].OutPoint.Index < c[j].OutPoint.Index } // getEligibleInputs returns eligible inputs with addresses between startAddress // and the last used address of lastSeriesID. -func (p *Pool) getEligibleInputs(store *txstore.Store, startAddress WithdrawalAddress, +func (p *Pool) getEligibleInputs(store *wtxmgr.Store, startAddress WithdrawalAddress, lastSeriesID uint32, dustThreshold btcutil.Amount, chainHeight int32, - minConf int) ([]Credit, error) { + minConf int) ([]credit, error) { if p.Series(lastSeriesID) == nil { str := fmt.Sprintf("lastSeriesID (%d) does not exist", lastSeriesID) @@ -143,15 +113,16 @@ func (p *Pool) getEligibleInputs(store *txstore.Store, startAddress WithdrawalAd if err != nil { return nil, err } - var inputs []Credit + var inputs []credit address := startAddress for { log.Debugf("Looking for eligible inputs at address %v", address.addrIdentifier()) if candidates, ok := addrMap[address.addr.EncodeAddress()]; ok { - var eligibles []Credit + var eligibles []credit for _, c := range candidates { - if p.isCreditEligible(c, minConf, chainHeight, dustThreshold) { - eligibles = append(eligibles, newCredit(c, address)) + candidate := newCredit(c, address) + if p.isCreditEligible(candidate, minConf, chainHeight, dustThreshold) { + eligibles = append(eligibles, candidate) } } // Make sure the eligibles are correctly sorted. @@ -237,11 +208,11 @@ func (p *Pool) highestUsedSeriesIndex(seriesID uint32) (Index, error) { // groupCreditsByAddr converts a slice of credits to a map from the string // representation of an encoded address to the unspent outputs associated with // that address. -func groupCreditsByAddr(credits []txstore.Credit, chainParams *chaincfg.Params) ( - map[string][]txstore.Credit, error) { - addrMap := make(map[string][]txstore.Credit) +func groupCreditsByAddr(credits []wtxmgr.Credit, chainParams *chaincfg.Params) ( + map[string][]wtxmgr.Credit, error) { + addrMap := make(map[string][]wtxmgr.Credit) for _, c := range credits { - _, addrs, _, err := c.Addresses(chainParams) + _, addrs, _, err := txscript.ExtractPkScriptAddrs(c.PkScript, chainParams) if err != nil { return nil, newError(ErrInputSelection, "failed to obtain input address", err) } @@ -255,7 +226,7 @@ func groupCreditsByAddr(credits []txstore.Credit, chainParams *chaincfg.Params) if v, ok := addrMap[encAddr]; ok { addrMap[encAddr] = append(v, c) } else { - addrMap[encAddr] = []txstore.Credit{c} + addrMap[encAddr] = []wtxmgr.Credit{c} } } @@ -265,12 +236,12 @@ func groupCreditsByAddr(credits []txstore.Credit, chainParams *chaincfg.Params) // isCreditEligible tests a given credit for eligibilty with respect // to number of confirmations, the dust threshold and that it is not // the charter output. -func (p *Pool) isCreditEligible(c txstore.Credit, minConf int, chainHeight int32, +func (p *Pool) isCreditEligible(c credit, minConf int, chainHeight int32, dustThreshold btcutil.Amount) bool { - if c.Amount() < dustThreshold { + if c.Amount < dustThreshold { return false } - if !c.Confirmed(minConf, chainHeight) { + if confirms(c.BlockMeta.Block.Height, chainHeight) < int32(minConf) { return false } if p.isCharterOutput(c) { @@ -282,6 +253,18 @@ func (p *Pool) isCreditEligible(c txstore.Credit, minConf int, chainHeight int32 // isCharterOutput - TODO: In order to determine this, we need the txid // and the output index of the current charter output, which we don't have yet. -func (p *Pool) isCharterOutput(c txstore.Credit) bool { +func (p *Pool) isCharterOutput(c credit) bool { return false } + +// confirms returns the number of confirmations for a transaction in a block at +// height txHeight (or -1 for an unconfirmed tx) given the chain height +// curHeight. +func confirms(txHeight, curHeight int32) int32 { + switch { + case txHeight == -1, txHeight > curHeight: + return 0 + default: + return curHeight - txHeight + 1 + } +} diff --git a/votingpool/input_selection_wb_test.go b/votingpool/input_selection_wb_test.go index a6a0cf2..7593924 100644 --- a/votingpool/input_selection_wb_test.go +++ b/votingpool/input_selection_wb_test.go @@ -24,7 +24,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwallet/legacy/txstore" + "github.com/btcsuite/btcwallet/wtxmgr" ) var ( @@ -48,17 +48,16 @@ func TestGetEligibleInputs(t *testing.T) { // Create two eligible inputs locked to each of the PKScripts above. expNoEligibleInputs := 2 * len(scripts) eligibleAmounts := []int64{int64(dustThreshold + 1), int64(dustThreshold + 1)} - var inputs []txstore.Credit + var inputs []wtxmgr.Credit for i := 0; i < len(scripts); i++ { - txIndex := int(i) + 1 - created := TstCreateInputsOnBlock(t, store, txIndex, scripts[i], eligibleAmounts) + created := TstCreateCreditsOnStore(t, store, scripts[i], eligibleAmounts) inputs = append(inputs, created...) } startAddr := TstNewWithdrawalAddress(t, pool, 1, 0, 0) lastSeriesID := uint32(2) currentBlock := int32(TstInputsBlock + eligibleInputMinConfirmations + 1) - var eligibles []Credit + var eligibles []credit var err error TstRunWithManagerUnlocked(t, pool.Manager(), func() { eligibles, err = pool.getEligibleInputs( @@ -213,23 +212,14 @@ func TestNextAddr(t *testing.T) { } func TestEligibleInputsAreEligible(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - seriesID := uint32(1) - branch := Branch(0) - index := Index(0) - // create the series - series := []TstSeriesDef{{ReqSigs: 3, PubKeys: TstPubKeys[1:6], SeriesID: seriesID}} - TstCreateSeries(t, pool, series) - - // Create the input. - pkScript := TstCreatePkScript(t, pool, seriesID, branch, index) var chainHeight int32 = 1000 - c := TstCreateInputs(t, store, pkScript, []int64{int64(dustThreshold)})[0] - - // Make sure credits is old enough to pass the minConf check. - c.BlockHeight = int32(eligibleInputMinConfirmations) + _, credits := TstCreateCreditsOnNewSeries(t, pool, []int64{int64(dustThreshold)}) + c := credits[0] + // Make sure credit is old enough to pass the minConf check. + c.BlockMeta.Height = int32(eligibleInputMinConfirmations) if !pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) { t.Errorf("Input is not eligible and it should be.") @@ -237,36 +227,28 @@ func TestEligibleInputsAreEligible(t *testing.T) { } func TestNonEligibleInputsAreNotEligible(t *testing.T) { - tearDown, pool, store1 := TstCreatePoolAndTxStore(t) - store2, storeTearDown2 := TstCreateTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - defer storeTearDown2() - seriesID := uint32(1) - branch := Branch(0) - index := Index(0) - // create the series - series := []TstSeriesDef{{ReqSigs: 3, PubKeys: TstPubKeys[1:6], SeriesID: seriesID}} - TstCreateSeries(t, pool, series) - - pkScript := TstCreatePkScript(t, pool, seriesID, branch, index) var chainHeight int32 = 1000 + _, credits := TstCreateCreditsOnNewSeries(t, pool, []int64{int64(dustThreshold - 1)}) + c := credits[0] + // Make sure credit is old enough to pass the minConf check. + c.BlockMeta.Height = int32(eligibleInputMinConfirmations) // Check that credit below dustThreshold is rejected. - c1 := TstCreateInputs(t, store1, pkScript, []int64{int64(dustThreshold - 1)})[0] - c1.BlockHeight = int32(100) // make sure it has enough confirmations. - if pool.isCreditEligible(c1, eligibleInputMinConfirmations, chainHeight, dustThreshold) { + if pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) { t.Errorf("Input is eligible and it should not be.") } // Check that a credit with not enough confirmations is rejected. - c2 := TstCreateInputs(t, store2, pkScript, []int64{int64(dustThreshold)})[0] - // the calculation of if it has been confirmed does this: - // chainheigt - bh + 1 >= target, which is quite weird, but the - // reason why I need to put 902 as *that* makes 1000 - 902 +1 = 99 >= - // 100 false - c2.BlockHeight = int32(902) - if pool.isCreditEligible(c2, eligibleInputMinConfirmations, chainHeight, dustThreshold) { + _, credits = TstCreateCreditsOnNewSeries(t, pool, []int64{int64(dustThreshold)}) + c = credits[0] + // The calculation of if it has been confirmed does this: chainheigt - bh + + // 1 >= target, which is quite weird, but the reason why I need to put 902 + // is *that* makes 1000 - 902 +1 = 99 >= 100 false + c.BlockMeta.Height = int32(902) + if pool.isCreditEligible(c, eligibleInputMinConfirmations, chainHeight, dustThreshold) { t.Errorf("Input is eligible and it should not be.") } } @@ -284,21 +266,21 @@ func TestCreditSortingByAddress(t *testing.T) { shaHash0 := bytes.Repeat([]byte{0}, 32) shaHash1 := bytes.Repeat([]byte{1}, 32) shaHash2 := bytes.Repeat([]byte{2}, 32) - c0 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash0, 0) - c1 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash0, 1) - c2 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash1, 0) - c3 := TstNewFakeCredit(t, pool, 1, 0, 0, shaHash2, 0) - c4 := TstNewFakeCredit(t, pool, 1, 0, 1, shaHash0, 0) - c5 := TstNewFakeCredit(t, pool, 1, 1, 0, shaHash0, 0) - c6 := TstNewFakeCredit(t, pool, 2, 0, 0, shaHash0, 0) + c0 := newDummyCredit(t, pool, 1, 0, 0, shaHash0, 0) + c1 := newDummyCredit(t, pool, 1, 0, 0, shaHash0, 1) + c2 := newDummyCredit(t, pool, 1, 0, 0, shaHash1, 0) + c3 := newDummyCredit(t, pool, 1, 0, 0, shaHash2, 0) + c4 := newDummyCredit(t, pool, 1, 0, 1, shaHash0, 0) + c5 := newDummyCredit(t, pool, 1, 1, 0, shaHash0, 0) + c6 := newDummyCredit(t, pool, 2, 0, 0, shaHash0, 0) - randomCredits := [][]Credit{ - []Credit{c6, c5, c4, c3, c2, c1, c0}, - []Credit{c2, c1, c0, c6, c5, c4, c3}, - []Credit{c6, c4, c5, c2, c3, c0, c1}, + randomCredits := [][]credit{ + []credit{c6, c5, c4, c3, c2, c1, c0}, + []credit{c2, c1, c0, c6, c5, c4, c3}, + []credit{c6, c4, c5, c2, c3, c0, c1}, } - want := []Credit{c0, c1, c2, c3, c4, c5, c6} + want := []credit{c0, c1, c2, c3, c4, c5, c6} for _, random := range randomCredits { sort.Sort(byAddress(random)) @@ -318,28 +300,11 @@ func TestCreditSortingByAddress(t *testing.T) { } } -// TstFakeCredit is a structure implementing the Credit interface used to test -// the byAddress sorting. It exists because to test the sorting properly we need -// to be able to set the Credit's TxSha and OutputIndex. -type TstFakeCredit struct { - addr WithdrawalAddress - txSha *wire.ShaHash - outputIndex uint32 - amount btcutil.Amount -} - -func (c *TstFakeCredit) String() string { return "" } -func (c *TstFakeCredit) TxSha() *wire.ShaHash { return c.txSha } -func (c *TstFakeCredit) OutputIndex() uint32 { return c.outputIndex } -func (c *TstFakeCredit) Address() WithdrawalAddress { return c.addr } -func (c *TstFakeCredit) Amount() btcutil.Amount { return c.amount } -func (c *TstFakeCredit) TxOut() *wire.TxOut { return nil } -func (c *TstFakeCredit) OutPoint() *wire.OutPoint { - return &wire.OutPoint{Hash: *c.txSha, Index: c.outputIndex} -} - -func TstNewFakeCredit(t *testing.T, pool *Pool, series uint32, index Index, branch Branch, - txSha []byte, outputIdx int) *TstFakeCredit { +// newDummyCredit creates a new credit with the given hash and outpointIdx, +// locked to the votingpool address identified by the given +// series/index/branch. +func newDummyCredit(t *testing.T, pool *Pool, series uint32, index Index, branch Branch, + txSha []byte, outpointIdx uint32) credit { var hash wire.ShaHash if err := hash.SetBytes(txSha); err != nil { t.Fatal(err) @@ -348,17 +313,15 @@ func TstNewFakeCredit(t *testing.T, pool *Pool, series uint32, index Index, bran // the set of used addresses as that's a requirement of WithdrawalAddress. TstEnsureUsedAddr(t, pool, series, branch, index) addr := TstNewWithdrawalAddress(t, pool, series, branch, index) - return &TstFakeCredit{ - addr: *addr, - txSha: &hash, - outputIndex: uint32(outputIdx), + c := wtxmgr.Credit{ + OutPoint: wire.OutPoint{ + Hash: hash, + Index: outpointIdx, + }, } + return newCredit(c, *addr) } -// Compile time check that TstFakeCredit implements the -// Credit interface. -var _ Credit = (*TstFakeCredit)(nil) - func checkUniqueness(t *testing.T, credits byAddress) { type uniq struct { series uint32 @@ -371,11 +334,11 @@ func checkUniqueness(t *testing.T, credits byAddress) { uniqMap := make(map[uniq]bool) for _, c := range credits { u := uniq{ - series: c.Address().SeriesID(), - branch: c.Address().Branch(), - index: c.Address().Index(), - hash: *c.TxSha(), - outputIndex: c.OutputIndex(), + series: c.addr.SeriesID(), + branch: c.addr.Branch(), + index: c.addr.Index(), + hash: c.OutPoint.Hash, + outputIndex: c.OutPoint.Index, } if _, exists := uniqMap[u]; exists { t.Fatalf("Duplicate found: %v", u) diff --git a/votingpool/withdrawal.go b/votingpool/withdrawal.go index eb7759f..4479640 100644 --- a/votingpool/withdrawal.go +++ b/votingpool/withdrawal.go @@ -22,12 +22,13 @@ import ( "math" "sort" "strconv" + "time" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwallet/legacy/txstore" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/btcsuite/btcwallet/wtxmgr" "github.com/btcsuite/fastsha256" ) @@ -235,7 +236,7 @@ type withdrawal struct { status *WithdrawalStatus transactions []*withdrawalTx pendingRequests []OutputRequest - eligibleInputs []Credit + eligibleInputs []credit current *withdrawalTx } @@ -263,7 +264,7 @@ func (o *withdrawalTxOut) pkScript() []byte { // withdrawalTx represents a transaction constructed by the withdrawal process. type withdrawalTx struct { - inputs []Credit + inputs []credit outputs []*withdrawalTxOut fee btcutil.Amount @@ -288,7 +289,7 @@ func (tx *withdrawalTx) ntxid() Ntxid { // inputTotal returns the sum amount of all inputs in this tx. func (tx *withdrawalTx) inputTotal() (total btcutil.Amount) { for _, input := range tx.inputs { - total += input.Amount() + total += input.Amount } return total } @@ -319,7 +320,7 @@ func (tx *withdrawalTx) toMsgTx() *wire.MsgTx { } for _, i := range tx.inputs { - msgtx.AddTxIn(wire.NewTxIn(i.OutPoint(), []byte{})) + msgtx.AddTxIn(wire.NewTxIn(&i.OutPoint, []byte{})) } return msgtx } @@ -339,16 +340,16 @@ func (tx *withdrawalTx) removeOutput() *withdrawalTxOut { } // addInput adds a new input to this transaction. -func (tx *withdrawalTx) addInput(input Credit) { - log.Debugf("Added tx input with amount %v", input.Amount()) +func (tx *withdrawalTx) addInput(input credit) { + log.Debugf("Added tx input with amount %v", input.Amount) tx.inputs = append(tx.inputs, input) } // removeInput removes the last added input and returns it. -func (tx *withdrawalTx) removeInput() Credit { +func (tx *withdrawalTx) removeInput() credit { removed := tx.inputs[len(tx.inputs)-1] tx.inputs = tx.inputs[:len(tx.inputs)-1] - log.Debugf("Removed tx input with amount %v", removed.Amount()) + log.Debugf("Removed tx input with amount %v", removed.Amount) return removed } @@ -378,7 +379,7 @@ func (tx *withdrawalTx) addChange(pkScript []byte) bool { // // The tx needs to have two or more outputs. The case with only one output must // be handled separately (by the split output procedure). -func (tx *withdrawalTx) rollBackLastOutput() ([]Credit, *withdrawalTxOut, error) { +func (tx *withdrawalTx) rollBackLastOutput() ([]credit, *withdrawalTxOut, error) { // Check precondition: At least two outputs are required in the transaction. if len(tx.outputs) < 2 { str := fmt.Sprintf("at least two outputs expected; got %d", len(tx.outputs)) @@ -387,7 +388,7 @@ func (tx *withdrawalTx) rollBackLastOutput() ([]Credit, *withdrawalTxOut, error) removedOutput := tx.removeOutput() - var removedInputs []Credit + var removedInputs []credit // Continue until sum(in) < sum(out) + fee for tx.inputTotal() >= tx.outputTotal()+calculateTxFee(tx) { removedInputs = append(removedInputs, tx.removeInput()) @@ -399,7 +400,7 @@ func (tx *withdrawalTx) rollBackLastOutput() ([]Credit, *withdrawalTxOut, error) return removedInputs, removedOutput, nil } -func newWithdrawal(roundID uint32, requests []OutputRequest, inputs []Credit, +func newWithdrawal(roundID uint32, requests []OutputRequest, inputs []credit, changeStart ChangeAddress) *withdrawal { outputs := make(map[OutBailmentID]*WithdrawalOutput, len(requests)) for _, request := range requests { @@ -427,7 +428,7 @@ func newWithdrawal(roundID uint32, requests []OutputRequest, inputs []Credit, // found at http://opentransactions.org/wiki/index.php/Startwithdrawal func (p *Pool) StartWithdrawal(roundID uint32, requests []OutputRequest, startAddress WithdrawalAddress, lastSeriesID uint32, changeStart ChangeAddress, - txStore *txstore.Store, chainHeight int32, dustThreshold btcutil.Amount) ( + txStore *wtxmgr.Store, chainHeight int32, dustThreshold btcutil.Amount) ( *WithdrawalStatus, error) { eligible, err := p.getEligibleInputs(txStore, startAddress, lastSeriesID, dustThreshold, @@ -463,7 +464,7 @@ func (w *withdrawal) pushRequest(request OutputRequest) { // popInput removes and returns the first input from the stack of eligible // inputs. -func (w *withdrawal) popInput() Credit { +func (w *withdrawal) popInput() credit { input := w.eligibleInputs[0] w.eligibleInputs = w.eligibleInputs[1:] return input @@ -473,8 +474,8 @@ func (w *withdrawal) popInput() Credit { // TODO: Reverse the stack semantics here as the current one generates a lot of // extra garbage since it always creates a new single-element slice and append // the rest of the items to it. -func (w *withdrawal) pushInput(input Credit) { - w.eligibleInputs = append([]Credit{input}, w.eligibleInputs...) +func (w *withdrawal) pushInput(input credit) { + w.eligibleInputs = append([]credit{input}, w.eligibleInputs...) } // If this returns it means we have added an output and the necessary inputs to fulfil that @@ -597,7 +598,7 @@ func (w *withdrawal) finalizeCurrentTx() error { func (w *withdrawal) maybeDropRequests() { inputAmount := btcutil.Amount(0) for _, input := range w.eligibleInputs { - inputAmount += input.Amount() + inputAmount += input.Amount } outputAmount := btcutil.Amount(0) for _, request := range w.pendingRequests { @@ -715,7 +716,7 @@ func getRawSigs(transactions []*withdrawalTx) (map[Ntxid]TxSigs, error) { msgtx := tx.toMsgTx() ntxid := tx.ntxid() for inputIdx, input := range tx.inputs { - creditAddr := input.Address() + creditAddr := input.addr redeemScript := creditAddr.redeemScript() series := creditAddr.series() // The order of the raw signatures in the signature script must match the @@ -767,10 +768,19 @@ func getRawSigs(transactions []*withdrawalTx) (map[Ntxid]TxSigs, error) { // manager) the redeem script for each of them and constructing the signature // script using that and the given raw signatures. // This function must be called with the manager unlocked. -func SignTx(msgtx *wire.MsgTx, sigs TxSigs, mgr *waddrmgr.Manager, store *txstore.Store) error { - credits, err := store.FindPreviousCredits(btcutil.NewTx(msgtx)) - for i, credit := range credits { - if err = signMultiSigUTXO(mgr, msgtx, i, credit.TxOut().PkScript, sigs[i]); err != nil { +func SignTx(msgtx *wire.MsgTx, sigs TxSigs, mgr *waddrmgr.Manager, store *wtxmgr.Store) error { + // We use time.Now() here as we're not going to store the new TxRecord + // anywhere -- we just need it to pass to store.PreviousPkScripts(). + rec, err := wtxmgr.NewTxRecordFromMsgTx(msgtx, time.Now()) + if err != nil { + return newError(ErrTxSigning, "failed to construct TxRecord for signing", err) + } + pkScripts, err := store.PreviousPkScripts(rec, nil) + if err != nil { + return newError(ErrTxSigning, "failed to obtain pkScripts for signing", err) + } + for i, pkScript := range pkScripts { + if err = signMultiSigUTXO(mgr, msgtx, i, pkScript, sigs[i]); err != nil { return err } } @@ -893,7 +903,7 @@ var calculateTxSize = func(tx *withdrawalTx) int { // Notice that we use 73 as the signature length as that's the maximum // length they may have: // https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm - addr := tx.inputs[i].Address() + addr := tx.inputs[i].addr redeemScriptLen := len(addr.redeemScript()) n := wire.VarIntSerializeSize(uint64(redeemScriptLen)) sigScriptLen := 1 + (74 * int(addr.series().reqSigs)) + redeemScriptLen + 1 + n diff --git a/votingpool/withdrawal_test.go b/votingpool/withdrawal_test.go index 7ba6c89..991864d 100644 --- a/votingpool/withdrawal_test.go +++ b/votingpool/withdrawal_test.go @@ -37,7 +37,7 @@ func TestStartWithdrawal(t *testing.T) { def := vp.TstCreateSeriesDef(t, pool, 2, masters) vp.TstCreateSeries(t, pool, []vp.TstSeriesDef{def}) // Create eligible inputs and the list of outputs we need to fulfil. - vp.TstCreateCreditsOnSeries(t, pool, def.SeriesID, []int64{5e6, 4e6}, store) + vp.TstCreateSeriesCreditsOnStore(t, pool, def.SeriesID, []int64{5e6, 4e6}, store) address1 := "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6" address2 := "3PbExiaztsSYgh6zeMswC49hLUwhTQ86XG" requests := []vp.OutputRequest{ diff --git a/votingpool/withdrawal_wb_test.go b/votingpool/withdrawal_wb_test.go index 5ce5831..c0f2007 100644 --- a/votingpool/withdrawal_wb_test.go +++ b/votingpool/withdrawal_wb_test.go @@ -27,14 +27,13 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/hdkeychain" - "github.com/btcsuite/btcwallet/legacy/txstore" "github.com/btcsuite/btcwallet/waddrmgr" ) // TestOutputSplittingNotEnoughInputs checks that an output will get split if we // don't have enough inputs to fulfil it. func TestOutputSplittingNotEnoughInputs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() net := pool.Manager().ChainParams() @@ -47,7 +46,7 @@ func TestOutputSplittingNotEnoughInputs(t *testing.T) { TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", output1Amount, net), TstNewOutputRequest(t, 2, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", output2Amount, net), } - seriesID, eligible := TstCreateCredits(t, pool, []int64{7}, store) + seriesID, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{7}) w := newWithdrawal(0, requests, eligible, *TstNewChangeAddress(t, pool, seriesID, 0)) // Trigger an output split because of lack of inputs by forcing a high fee. @@ -81,7 +80,7 @@ func TestOutputSplittingNotEnoughInputs(t *testing.T) { } func TestOutputSplittingOversizeTx(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() requestAmount := btcutil.Amount(5) @@ -89,7 +88,7 @@ func TestOutputSplittingOversizeTx(t *testing.T) { smallInput := int64(2) request := TstNewOutputRequest( t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", requestAmount, pool.Manager().ChainParams()) - seriesID, eligible := TstCreateCredits(t, pool, []int64{bigInput, smallInput}, store) + seriesID, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{bigInput, smallInput}) changeStart := TstNewChangeAddress(t, pool, seriesID, 0) w := newWithdrawal(0, []OutputRequest{request}, eligible, *changeStart) restoreCalculateTxFee := replaceCalculateTxFee(TstConstantFee(0)) @@ -136,11 +135,11 @@ func TestOutputSplittingOversizeTx(t *testing.T) { } func TestSplitLastOutputNoOutputs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - w := newWithdrawal(0, []OutputRequest{}, []Credit{}, ChangeAddress{}) - w.current = createWithdrawalTx(t, pool, store, []int64{}, []int64{}) + w := newWithdrawal(0, []OutputRequest{}, []credit{}, ChangeAddress{}) + w.current = createWithdrawalTx(t, pool, []int64{}, []int64{}) err := w.splitLastOutput() @@ -150,12 +149,12 @@ func TestSplitLastOutputNoOutputs(t *testing.T) { // Check that all outputs requested in a withdrawal match the outputs of the generated // transaction(s). func TestWithdrawalTxOutputs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() net := pool.Manager().ChainParams() // Create eligible inputs and the list of outputs we need to fulfil. - seriesID, eligible := TstCreateCredits(t, pool, []int64{2e6, 4e6}, store) + seriesID, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{2e6, 4e6}) outputs := []OutputRequest{ TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", 3e6, net), TstNewOutputRequest(t, 2, "3PbExiaztsSYgh6zeMswC49hLUwhTQ86XG", 2e6, net), @@ -174,7 +173,7 @@ func TestWithdrawalTxOutputs(t *testing.T) { tx := w.transactions[0] // The created tx should include both eligible credits, so we expect it to have // an input amount of 2e6+4e6 satoshis. - inputAmount := eligible[0].Amount() + eligible[1].Amount() + inputAmount := eligible[0].Amount + eligible[1].Amount change := inputAmount - (outputs[0].Amount + outputs[1].Amount + calculateTxFee(tx)) expectedOutputs := append( outputs, TstNewOutputRequest(t, 3, changeStart.addr.String(), change, net)) @@ -185,10 +184,10 @@ func TestWithdrawalTxOutputs(t *testing.T) { // Check that withdrawal.status correctly states that no outputs were fulfilled when we // don't have enough eligible credits for any of them. func TestFulfillRequestsNoSatisfiableOutputs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - seriesID, eligible := TstCreateCredits(t, pool, []int64{1e6}, store) + seriesID, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{1e6}) request := TstNewOutputRequest( t, 1, "3Qt1EaKRD9g9FeL2DGkLLswhK1AKmmXFSe", btcutil.Amount(3e6), pool.Manager().ChainParams()) changeStart := TstNewChangeAddress(t, pool, seriesID, 0) @@ -217,12 +216,12 @@ func TestFulfillRequestsNoSatisfiableOutputs(t *testing.T) { // Check that some requested outputs are not fulfilled when we don't have credits for all // of them. func TestFulfillRequestsNotEnoughCreditsForAllRequests(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() net := pool.Manager().ChainParams() // Create eligible inputs and the list of outputs we need to fulfil. - seriesID, eligible := TstCreateCredits(t, pool, []int64{2e6, 4e6}, store) + seriesID, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{2e6, 4e6}) out1 := TstNewOutputRequest( t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", btcutil.Amount(3e6), net) out2 := TstNewOutputRequest( @@ -240,7 +239,7 @@ func TestFulfillRequestsNotEnoughCreditsForAllRequests(t *testing.T) { tx := w.transactions[0] // The created tx should spend both eligible credits, so we expect it to have // an input amount of 2e6+4e6 satoshis. - inputAmount := eligible[0].Amount() + eligible[1].Amount() + inputAmount := eligible[0].Amount + eligible[1].Amount // We expect it to include outputs for requests 1 and 2, plus a change output, but // output request #3 should not be there because we don't have enough credits. change := inputAmount - (out1.Amount + out2.Amount + calculateTxFee(tx)) @@ -268,10 +267,10 @@ func TestFulfillRequestsNotEnoughCreditsForAllRequests(t *testing.T) { // TestRollbackLastOutput tests the case where we rollback one output // and one input, such that sum(in) >= sum(out) + fee. func TestRollbackLastOutput(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{3, 3, 2, 1, 3}, []int64{3, 3, 2, 2}) + tx := createWithdrawalTx(t, pool, []int64{3, 3, 2, 1, 3}, []int64{3, 3, 2, 2}) initialInputs := tx.inputs initialOutputs := tx.outputs @@ -292,7 +291,7 @@ func TestRollbackLastOutput(t *testing.T) { t.Fatalf("Unexpected number of inputs removed; got %d, want 1", len(removedInputs)) } lastInput := initialInputs[len(initialInputs)-1] - if removedInputs[0] != lastInput { + if !reflect.DeepEqual(removedInputs[0], lastInput) { t.Fatalf("Wrong rolled back input; got %s want %s", removedInputs[0], lastInput) } @@ -303,12 +302,12 @@ func TestRollbackLastOutput(t *testing.T) { } func TestRollbackLastOutputMultipleInputsRolledBack(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() // This tx will need the 3 last inputs to fulfill the second output, so they // should all be rolled back and returned in the reverse order they were added. - tx := createWithdrawalTx(t, pool, store, []int64{1, 2, 3, 4}, []int64{1, 8}) + tx := createWithdrawalTx(t, pool, []int64{1, 2, 3, 4}, []int64{1, 8}) initialInputs := tx.inputs initialOutputs := tx.outputs @@ -323,8 +322,8 @@ func TestRollbackLastOutputMultipleInputsRolledBack(t *testing.T) { t.Fatalf("Unexpected number of inputs removed; got %d, want 3", len(removedInputs)) } for i, amount := range []btcutil.Amount{4, 3, 2} { - if removedInputs[i].Amount() != amount { - t.Fatalf("Unexpected input amount; got %v, want %v", removedInputs[i].Amount(), amount) + if removedInputs[i].Amount != amount { + t.Fatalf("Unexpected input amount; got %v, want %v", removedInputs[i].Amount, amount) } } @@ -337,10 +336,10 @@ func TestRollbackLastOutputMultipleInputsRolledBack(t *testing.T) { // TestRollbackLastOutputNoInputsRolledBack tests the case where we roll back // one output but don't need to roll back any inputs. func TestRollbackLastOutputNoInputsRolledBack(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{4}, []int64{2, 3}) + tx := createWithdrawalTx(t, pool, []int64{4}, []int64{2, 3}) initialInputs := tx.inputs initialOutputs := tx.outputs @@ -385,11 +384,11 @@ func TestRollBackLastOutputInsufficientOutputs(t *testing.T) { // TestRollbackLastOutputWhenNewOutputAdded checks that we roll back the last // output if a tx becomes too big right after we add a new output to it. func TestRollbackLastOutputWhenNewOutputAdded(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() net := pool.Manager().ChainParams() - series, eligible := TstCreateCredits(t, pool, []int64{5, 5}, store) + series, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{5, 5}) requests := []OutputRequest{ // This is ordered by bailment ID TstNewOutputRequest(t, 1, "34eVkREKgvvGASZW7hkgE2uNc1yycntMK6", 1, net), @@ -433,11 +432,11 @@ func TestRollbackLastOutputWhenNewOutputAdded(t *testing.T) { // TestRollbackLastOutputWhenNewInputAdded checks that we roll back the last // output if a tx becomes too big right after we add a new input to it. func TestRollbackLastOutputWhenNewInputAdded(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() net := pool.Manager().ChainParams() - series, eligible := TstCreateCredits(t, pool, []int64{1, 2, 3, 4, 5, 6}, store) + series, eligible := TstCreateCreditsOnNewSeries(t, pool, []int64{1, 2, 3, 4, 5, 6}) requests := []OutputRequest{ // This is manually ordered by outBailmentIDHash, which is the order in // which they're going to be fulfilled by w.fulfillRequests(). @@ -488,10 +487,10 @@ func TestRollbackLastOutputWhenNewInputAdded(t *testing.T) { } func TestWithdrawalTxRemoveOutput(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{1, 2}) + tx := createWithdrawalTx(t, pool, []int64{}, []int64{1, 2}) outputs := tx.outputs // Make sure we have created the transaction with the expected // outputs. @@ -516,10 +515,10 @@ func TestWithdrawalTxRemoveOutput(t *testing.T) { } func TestWithdrawalTxRemoveInput(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{1, 2}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{1, 2}, []int64{}) inputs := tx.inputs // Make sure we have created the transaction with the expected inputs checkTxInputs(t, tx, inputs) @@ -530,23 +529,23 @@ func TestWithdrawalTxRemoveInput(t *testing.T) { gotRemovedInput := tx.removeInput() // Check the popped input looks correct. - if gotRemovedInput != wantRemovedInput { + if !reflect.DeepEqual(gotRemovedInput, wantRemovedInput) { t.Fatalf("Popped input wrong; got %v, want %v", gotRemovedInput, wantRemovedInput) } checkTxInputs(t, tx, inputs[0:1]) // Make sure that the remaining input is really the right one. - if tx.inputs[0] != remainingInput { + if !reflect.DeepEqual(tx.inputs[0], remainingInput) { t.Fatalf("Wrong input: got %v, want %v", tx.inputs[0], remainingInput) } } func TestWithdrawalTxAddChange(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() input, output, fee := int64(4e6), int64(3e6), int64(10) - tx := createWithdrawalTx(t, pool, store, []int64{input}, []int64{output}) + tx := createWithdrawalTx(t, pool, []int64{input}, []int64{output}) restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(btcutil.Amount(fee))) defer restoreCalcTxFee() @@ -569,11 +568,11 @@ func TestWithdrawalTxAddChange(t *testing.T) { // add a change output when there's no satoshis left after paying all // outputs+fees. func TestWithdrawalTxAddChangeNoChange(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() input, output, fee := int64(4e6), int64(4e6), int64(0) - tx := createWithdrawalTx(t, pool, store, []int64{input}, []int64{output}) + tx := createWithdrawalTx(t, pool, []int64{input}, []int64{output}) restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(btcutil.Amount(fee))) defer restoreCalcTxFee() @@ -587,20 +586,20 @@ func TestWithdrawalTxAddChangeNoChange(t *testing.T) { } func TestWithdrawalTxToMsgTxNoInputsOrOutputsOrChange(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{}, []int64{}) msgtx := tx.toMsgTx() compareMsgTxAndWithdrawalTxOutputs(t, msgtx, tx) compareMsgTxAndWithdrawalTxInputs(t, msgtx, tx) } func TestWithdrawalTxToMsgTxNoInputsOrOutputsWithChange(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{}, []int64{}) tx.changeOutput = wire.NewTxOut(int64(1), []byte{}) msgtx := tx.toMsgTx() @@ -610,10 +609,10 @@ func TestWithdrawalTxToMsgTxNoInputsOrOutputsWithChange(t *testing.T) { } func TestWithdrawalTxToMsgTxWithInputButNoOutputsWithChange(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{1}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{1}, []int64{}) tx.changeOutput = wire.NewTxOut(int64(1), []byte{}) msgtx := tx.toMsgTx() @@ -623,11 +622,11 @@ func TestWithdrawalTxToMsgTxWithInputButNoOutputsWithChange(t *testing.T) { } func TestWithdrawalTxToMsgTxWithInputOutputsAndChange(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{1, 2, 3}, []int64{4, 5, 6}) + tx := createWithdrawalTx(t, pool, []int64{1, 2, 3}, []int64{4, 5, 6}) tx.changeOutput = wire.NewTxOut(int64(7), []byte{}) msgtx := tx.toMsgTx() @@ -637,10 +636,10 @@ func TestWithdrawalTxToMsgTxWithInputOutputsAndChange(t *testing.T) { } func TestWithdrawalTxInputTotal(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{5}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{5}, []int64{}) if tx.inputTotal() != btcutil.Amount(5) { t.Fatalf("Wrong total output; got %v, want %v", tx.outputTotal(), btcutil.Amount(5)) @@ -648,10 +647,10 @@ func TestWithdrawalTxInputTotal(t *testing.T) { } func TestWithdrawalTxOutputTotal(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{}, []int64{4}) + tx := createWithdrawalTx(t, pool, []int64{}, []int64{4}) tx.changeOutput = wire.NewTxOut(int64(1), []byte{}) if tx.outputTotal() != btcutil.Amount(4) { @@ -660,12 +659,12 @@ func TestWithdrawalTxOutputTotal(t *testing.T) { } func TestSignMultiSigUTXO(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() // Create a new tx with a single input that we're going to sign. mgr := pool.Manager() - tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{4e6}) + tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{4e6}) sigs, err := getRawSigs([]*withdrawalTx{tx}) if err != nil { t.Fatal(err) @@ -675,7 +674,7 @@ func TestSignMultiSigUTXO(t *testing.T) { txSigs := sigs[tx.ntxid()] idx := 0 // The index of the tx input we're going to sign. - pkScript := tx.inputs[idx].TxOut().PkScript + pkScript := tx.inputs[idx].PkScript TstRunWithManagerUnlocked(t, mgr, func() { if err = signMultiSigUTXO(mgr, msgtx, idx, pkScript, txSigs[idx]); err != nil { t.Fatal(err) @@ -684,11 +683,11 @@ func TestSignMultiSigUTXO(t *testing.T) { } func TestSignMultiSigUTXOUnparseablePkScript(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() - tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) msgtx := tx.toMsgTx() unparseablePkScript := []byte{0x01} @@ -698,11 +697,11 @@ func TestSignMultiSigUTXOUnparseablePkScript(t *testing.T) { } func TestSignMultiSigUTXOPkScriptNotP2SH(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() - tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) addr, _ := btcutil.DecodeAddress("1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX", mgr.ChainParams()) pubKeyHashPkScript, _ := txscript.PayToAddrScript(addr.(*btcutil.AddressPubKeyHash)) msgtx := tx.toMsgTx() @@ -713,11 +712,11 @@ func TestSignMultiSigUTXOPkScriptNotP2SH(t *testing.T) { } func TestSignMultiSigUTXORedeemScriptNotFound(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() - tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) // This is a P2SH address for which the addr manager doesn't have the redeem // script. addr, _ := btcutil.DecodeAddress("3Hb4xcebcKg4DiETJfwjh8sF4uDw9rqtVC", mgr.ChainParams()) @@ -733,11 +732,11 @@ func TestSignMultiSigUTXORedeemScriptNotFound(t *testing.T) { } func TestSignMultiSigUTXONotEnoughSigs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() - tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) sigs, err := getRawSigs([]*withdrawalTx{tx}) if err != nil { t.Fatal(err) @@ -747,9 +746,9 @@ func TestSignMultiSigUTXONotEnoughSigs(t *testing.T) { idx := 0 // The index of the tx input we're going to sign. // Here we provide reqSigs-1 signatures to SignMultiSigUTXO() - reqSigs := tx.inputs[idx].Address().series().TstGetReqSigs() + reqSigs := tx.inputs[idx].addr.series().TstGetReqSigs() txInSigs := txSigs[idx][:reqSigs-1] - pkScript := tx.inputs[idx].TxOut().PkScript + pkScript := tx.inputs[idx].PkScript TstRunWithManagerUnlocked(t, mgr, func() { err = signMultiSigUTXO(mgr, msgtx, idx, pkScript, txInSigs) }) @@ -758,15 +757,15 @@ func TestSignMultiSigUTXONotEnoughSigs(t *testing.T) { } func TestSignMultiSigUTXOWrongRawSigs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() mgr := pool.Manager() - tx := createWithdrawalTx(t, pool, store, []int64{4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{4e6}, []int64{}) sigs := []RawSig{RawSig{0x00}, RawSig{0x01}} idx := 0 // The index of the tx input we're going to sign. - pkScript := tx.inputs[idx].TxOut().PkScript + pkScript := tx.inputs[idx].PkScript var err error TstRunWithManagerUnlocked(t, mgr, func() { err = signMultiSigUTXO(mgr, tx.toMsgTx(), idx, pkScript, sigs) @@ -776,10 +775,10 @@ func TestSignMultiSigUTXOWrongRawSigs(t *testing.T) { } func TestGetRawSigs(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{5e6, 4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{5e6, 4e6}, []int64{}) sigs, err := getRawSigs([]*withdrawalTx{tx}) if err != nil { @@ -791,7 +790,7 @@ func TestGetRawSigs(t *testing.T) { t.Fatalf("Unexpected number of sig lists; got %d, want %d", len(txSigs), len(tx.inputs)) } - checkNonEmptySigsForPrivKeys(t, txSigs, tx.inputs[0].Address().series().privateKeys) + checkNonEmptySigsForPrivKeys(t, txSigs, tx.inputs[0].addr.series().privateKeys) // Since we have all the necessary signatures (m-of-n), we construct the // sigsnature scripts and execute them to make sure the raw signatures are @@ -800,12 +799,12 @@ func TestGetRawSigs(t *testing.T) { } func TestGetRawSigsOnlyOnePrivKeyAvailable(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{5e6, 4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{5e6, 4e6}, []int64{}) // Remove all private keys but the first one from the credit's series. - series := tx.inputs[0].Address().series() + series := tx.inputs[0].addr.series() for i := range series.privateKeys[1:] { series.privateKeys[i] = nil } @@ -824,13 +823,13 @@ func TestGetRawSigsOnlyOnePrivKeyAvailable(t *testing.T) { } func TestGetRawSigsUnparseableRedeemScript(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{5e6, 4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{5e6, 4e6}, []int64{}) // Change the redeem script for one of our tx inputs, to force an error in // getRawSigs(). - tx.inputs[0].Address().script = []byte{0x01} + tx.inputs[0].addr.script = []byte{0x01} _, err := getRawSigs([]*withdrawalTx{tx}) @@ -838,13 +837,13 @@ func TestGetRawSigsUnparseableRedeemScript(t *testing.T) { } func TestGetRawSigsInvalidAddrBranch(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{5e6, 4e6}, []int64{}) + tx := createWithdrawalTx(t, pool, []int64{5e6, 4e6}, []int64{}) // Change the branch of our input's address to an invalid value, to force // an error in getRawSigs(). - tx.inputs[0].Address().branch = Branch(999) + tx.inputs[0].addr.branch = Branch(999) _, err := getRawSigs([]*withdrawalTx{tx}) @@ -870,10 +869,10 @@ func TestOutBailmentIDSort(t *testing.T) { } func TestTxTooBig(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{5}, []int64{1}) + tx := createWithdrawalTx(t, pool, []int64{5}, []int64{1}) restoreCalcTxSize := replaceCalculateTxSize(func(tx *withdrawalTx) int { return txMaxSize - 1 }) if isTxTooBig(tx) { @@ -899,10 +898,10 @@ func TestTxTooBig(t *testing.T) { } func TestTxSizeCalculation(t *testing.T) { - tearDown, pool, store := TstCreatePoolAndTxStore(t) + tearDown, pool, _ := TstCreatePoolAndTxStore(t) defer tearDown() - tx := createWithdrawalTx(t, pool, store, []int64{1, 5}, []int64{2}) + tx := createWithdrawalTx(t, pool, []int64{1, 5}, []int64{2}) size := calculateTxSize(tx) @@ -912,7 +911,7 @@ func TestTxSizeCalculation(t *testing.T) { // output. restoreCalcTxFee := replaceCalculateTxFee(TstConstantFee(1)) defer restoreCalcTxFee() - seriesID := tx.inputs[0].Address().SeriesID() + seriesID := tx.inputs[0].addr.SeriesID() tx.addChange(TstNewChangeAddress(t, pool, seriesID, 0).addr.ScriptAddress()) msgtx := tx.toMsgTx() sigs, err := getRawSigs([]*withdrawalTx{tx}) @@ -925,7 +924,7 @@ func TestTxSizeCalculation(t *testing.T) { // calculateTxSize() we use a dummy signature for the worst-case scenario (73 // bytes) so the estimate here can be up to 2 bytes bigger for every // signature in every input's SigScript. - maxDiff := 2 * len(msgtx.TxIn) * int(tx.inputs[0].Address().series().reqSigs) + maxDiff := 2 * len(msgtx.TxIn) * int(tx.inputs[0].addr.series().reqSigs) // To make things worse, there's a possibility that the length of the // actual SignatureScript is at the upper boundary of one of the uint* // types, and when that happens our dummy SignatureScript is likely to have @@ -973,17 +972,6 @@ func TestTxFeeEstimationForLargeTx(t *testing.T) { } } -// lookupStoredTx returns the TxRecord from the given store whose SHA matches the -// given ShaHash. -func lookupStoredTx(store *txstore.Store, sha *wire.ShaHash) *txstore.TxRecord { - for _, r := range store.Records() { - if bytes.Equal(r.Tx().Sha()[:], sha[:]) { - return r - } - } - return nil -} - // checkNonEmptySigsForPrivKeys checks that every signature list in txSigs has // one non-empty signature for every non-nil private key in the given list. This // is to make sure every signature list matches the specification at @@ -1044,12 +1032,12 @@ func checkMsgTxOutputs(t *testing.T, msgtx *wire.MsgTx, requests []OutputRequest } // checkTxInputs ensures that the tx.inputs match the given inputs. -func checkTxInputs(t *testing.T, tx *withdrawalTx, inputs []Credit) { +func checkTxInputs(t *testing.T, tx *withdrawalTx, inputs []credit) { if len(tx.inputs) != len(inputs) { t.Fatalf("Wrong number of inputs in tx; got %d, want %d", len(tx.inputs), len(inputs)) } for i, input := range tx.inputs { - if input != inputs[i] { + if !reflect.DeepEqual(input, inputs[i]) { t.Fatalf("Unexpected input; got %s, want %s", input, inputs[i]) } } @@ -1059,9 +1047,9 @@ func checkTxInputs(t *testing.T, tx *withdrawalTx, inputs []Credit) { // transaction (using the given raw signatures and the pkScripts from credits) and execute // those scripts to validate them. func signTxAndValidate(t *testing.T, mgr *waddrmgr.Manager, tx *wire.MsgTx, txSigs TxSigs, - credits []Credit) { + credits []credit) { for i := range tx.TxIn { - pkScript := credits[i].TxOut().PkScript + pkScript := credits[i].PkScript TstRunWithManagerUnlocked(t, mgr, func() { if err := signMultiSigUTXO(mgr, tx, i, pkScript, txSigs[i]); err != nil { t.Fatal(err) @@ -1076,9 +1064,9 @@ func compareMsgTxAndWithdrawalTxInputs(t *testing.T, msgtx *wire.MsgTx, tx *with } for i, txin := range msgtx.TxIn { - outpoint := tx.inputs[i].OutPoint() - if txin.PreviousOutPoint != *outpoint { - t.Fatalf("Wrong outpoint; got %v expected %v", txin.PreviousOutPoint, *outpoint) + outpoint := tx.inputs[i].OutPoint + if txin.PreviousOutPoint != outpoint { + t.Fatalf("Wrong outpoint; got %v expected %v", txin.PreviousOutPoint, outpoint) } } }